#18 merge latest package from CentOS 9 Stream
Merged a month ago by dcavalca. Opened a month ago by teknoraver.
rpms/ teknoraver/rpm c9s-sig-hyperscale  into  c9s-sig-hyperscale

@@ -0,0 +1,124 @@ 

+ From 1dc9372821487ccace23ff1ae9cba6b30f02c91c Mon Sep 17 00:00:00 2001

+ From: Florian Festi <ffesti@redhat.com>

+ Date: Tue, 5 Jul 2022 16:34:08 +0200

+ Subject: [PATCH] Add SourceLicense tag to spec syntax

+ 

+ to set a separate license to the source RPM. This can be useful if the

+ sources have code under additional licenses that do not end up in the

+ binary packeges.

+ 

+ Resolves: #2079

+ 

+ Note on the backport:  The spec document on this branch is ancient and

+ doesn't even contain the License tag's description so this backport

+ leaves the documentation part out.

+ 

+ (backported from commit 9ed9d3fce34bc3c8121989e0cf263528e7e68756)

+ ---

+  build/parsePreamble.c       |  6 ++++++

+  lib/rpmtag.h                |  3 +++

+  tests/data/SPECS/foo.spec   |  1 +

+  tests/data/SPECS/hello.spec |  1 +

+  tests/rpmbuild.at           | 11 +++++++++++

+  tests/rpmspec.at            |  1 +

+  6 files changed, 23 insertions(+)

+ 

+ diff --git a/build/parsePreamble.c b/build/parsePreamble.c

+ index e7d6d8752..bd07ecdf0 100644

+ --- a/build/parsePreamble.c

+ +++ b/build/parsePreamble.c

+ @@ -831,6 +831,11 @@ static rpmRC handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag,

+  	if (addLangTag(spec, pkg->header, tag, field, lang))

+  	    goto exit;

+  	break;

+ +    case RPMTAG_SOURCELICENSE:

+ +	if (addLangTag(spec, spec->sourcePackage->header,

+ +		       RPMTAG_LICENSE, field, lang))

+ +	    goto exit;

+ +	break;

+      case RPMTAG_BUILDROOT:

+  	/* just silently ignore BuildRoot */

+  	break;

+ @@ -1012,6 +1017,7 @@ static struct PreambleRec_s const preambleList[] = {

+      {RPMTAG_EPOCH,		0, 0, 1, LEN_AND_STR("epoch")},

+      {RPMTAG_SUMMARY,		1, 0, 1, LEN_AND_STR("summary")},

+      {RPMTAG_LICENSE,		0, 0, 1, LEN_AND_STR("license")},

+ +    {RPMTAG_SOURCELICENSE,	0, 0, 1, LEN_AND_STR("sourcelicense")},

+      {RPMTAG_DISTRIBUTION,	0, 0, 1, LEN_AND_STR("distribution")},

+      {RPMTAG_DISTURL,		0, 0, 1, LEN_AND_STR("disturl")},

+      {RPMTAG_VENDOR,		0, 0, 1, LEN_AND_STR("vendor")},

+ diff --git a/lib/rpmtag.h b/lib/rpmtag.h

+ index 7d1943835..1fd829118 100644

+ --- a/lib/rpmtag.h

+ +++ b/lib/rpmtag.h

+ @@ -375,6 +375,9 @@ typedef enum rpmTag_e {

+      RPMTAG_MODULARITYLABEL	= 5096, /* s */

+      RPMTAG_PAYLOADDIGESTALT	= 5097, /* s[] */

+  

+ +    /* Backports */

+ +    RPMTAG_SOURCELICENSE	= 5102, /* internal */

+ +

+      RPMTAG_FIRSTFREE_TAG	/*!< internal */

+  } rpmTag;

+  

+ diff --git a/tests/data/SPECS/foo.spec b/tests/data/SPECS/foo.spec

+ index 859e98142..9b1087094 100644

+ --- a/tests/data/SPECS/foo.spec

+ +++ b/tests/data/SPECS/foo.spec

+ @@ -8,6 +8,7 @@ Source: hello-2.0.tar.gz

+  Patch1: hello-1.0-modernize.patch

+  Group: Testing

+  License: GPLv2+

+ +SourceLicense: GPL, ASL 1.0

+  BuildArch: noarch

+  

+  %description

+ diff --git a/tests/data/SPECS/hello.spec b/tests/data/SPECS/hello.spec

+ index 5bc9cfaf7..4b9053aca 100644

+ --- a/tests/data/SPECS/hello.spec

+ +++ b/tests/data/SPECS/hello.spec

+ @@ -4,6 +4,7 @@ Version: 1.0

+  Release: 1

+  Group: Utilities

+  License: GPL

+ +SourceLicense: GPL, ASL 1.0

+  Distribution: RPM test suite.

+  Vendor: Red Hat Software

+  Packager: Red Hat Software <bugs@redhat.com>

+ diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at

+ index bff65303b..7680f1502 100644

+ --- a/tests/rpmbuild.at

+ +++ b/tests/rpmbuild.at

+ @@ -31,6 +31,17 @@ run rpmbuild \

+  [0],

+  [ignore],

+  [ignore])

+ +

+ +AT_CHECK([

+ +

+ +runroot rpm -qp --qf "%{license}\n" /build/SRPMS/hello-1.0-1.src.rpm

+ +runroot rpm -qp --qf "%{license}\n" /build/RPMS/*/hello-1.0-1.*.rpm

+ +],

+ +[0],

+ +[GPL, ASL 1.0

+ +GPL

+ +],

+ +[])

+  AT_CLEANUP

+  

+  AT_SETUP([rpmbuild -ba autosetup])

+ diff --git a/tests/rpmspec.at b/tests/rpmspec.at

+ index 2b11201db..c898ee654 100644

+ --- a/tests/rpmspec.at

+ +++ b/tests/rpmspec.at

+ @@ -243,6 +243,7 @@ Source: hello-2.0.tar.gz

+  Patch1: hello-1.0-modernize.patch

+  Group: Testing

+  License: GPLv2+

+ +SourceLicense: GPL, ASL 1.0

+  BuildArch: noarch

+  

+  %description

+ -- 

+ 2.45.2

+ 

@@ -0,0 +1,12 @@ 

+ diff -up rpm-4.16.1.3/lib/rpmts.c.orig rpm-4.16.1.3/lib/rpmts.c

+ --- rpm-4.16.1.3/lib/rpmts.c.orig	2024-07-11 13:55:35.430198126 +0200

+ +++ rpm-4.16.1.3/lib/rpmts.c	2024-07-11 13:55:59.243061182 +0200

+ @@ -482,6 +482,8 @@ static int makePubkeyHeader(rpmts ts, rp

+      int rc = -1;

+      int i;

+  

+ +    memset(&kd, 0, sizeof(kd));

+ +

+      if ((enc = rpmPubkeyBase64(key)) == NULL)

+  	goto exit;

+      if ((dig = rpmPubkeyDig(key)) == NULL)

@@ -0,0 +1,32 @@ 

+ From bff65aad8af719542c7b0c6429e09223c014a909 Mon Sep 17 00:00:00 2001

+ From: Michal Domonkos <mdomonko@redhat.com>

+ Date: Thu, 6 Jun 2024 09:15:02 +0200

+ Subject: [PATCH] Fix potential use of uninitialized pipe array

+ 

+ We only call pipe(2) after the script is written to disk so if the

+ latter fails, the array will be left uninitialized and subsequently read

+ after skipping to the exit label.  Fix by initializing it.

+ 

+ Found by Coverity.

+ 

+ Fixes: RHEL-22604

+ ---

+  lib/rpmscript.c | 2 +-

+  1 file changed, 1 insertion(+), 1 deletion(-)

+ 

+ diff --git a/lib/rpmscript.c b/lib/rpmscript.c

+ index 281c55c53..1de4acf8e 100644

+ --- a/lib/rpmscript.c

+ +++ b/lib/rpmscript.c

+ @@ -316,7 +316,7 @@ static rpmRC runExtScript(rpmPlugins plugins, ARGV_const_t prefixes,

+      char * fn = NULL;

+      pid_t pid, reaped;

+      int status;

+ -    int inpipe[2];

+ +    int inpipe[2] = { -1, -1 };

+      FILE *in = NULL;

+      const char *line;

+      char *mline = NULL;

+ -- 

+ 2.45.2

+ 

@@ -0,0 +1,91 @@ 

+ From bce17e42f2301a88574d757740627480a38d86aa Mon Sep 17 00:00:00 2001

+ From: Michal Domonkos <mdomonko@redhat.com>

+ Date: Fri, 26 Jul 2024 10:44:04 +0200

+ Subject: [PATCH] Fix root relocation regression

+ 

+ When relocating the root directory, make sure we insert the new path's

+ dirname to dirNames[] even if the root itself is owned by the package.

+ 

+ This appears to have been the intention from the first version (largely

+ untouched since) of this code as we allow the root to pass through the

+ first checks (by setting len to 0 in that case) as well as the second

+ for loop where we do the relocations.

+ 

+ This allows fsm to properly create and remove the relocated directory

+ since we're now using fd-based calls (#1919) and the parent directory

+ needs to be opened first.

+ 

+ No need to do string comparison here, the empty basename signals that

+ we're processing the root directory, so just use that.

+ 

+ Building a relocatable package that owns the root directory seems to be

+ a handy way to create user-installable packages (see RHEL-28967) and it

+ happened to work before with the path-based calls so this technically

+ was a regression.  Add a test that emulates this use case.

+ 

+ Backported from commits:

+ 31c14ba6610568c2d634647fed1fb57221178da9

+ 308ac60677732e9979b9ce11e5a3085906da1901

+ 

+ Fixes: RHEL-28967

+ ---

+  lib/relocation.c | 14 ++++++++------

+  1 file changed, 8 insertions(+), 6 deletions(-)

+ 

+ diff --git a/lib/relocation.c b/lib/relocation.c

+ index 3ba4cfeab..8c35bc1a7 100644

+ --- a/lib/relocation.c

+ +++ b/lib/relocation.c

+ @@ -123,7 +123,7 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,

+      char ** baseNames;

+      char ** dirNames;

+      uint32_t * dirIndexes;

+ -    rpm_count_t fileCount, dirCount;

+ +    rpm_count_t fileCount, dirCount, dirCountOrig;

+      int nrelocated = 0;

+      int fileAlloced = 0;

+      char * fn = NULL;

+ @@ -162,7 +162,7 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,

+      baseNames = bnames.data;

+      dirIndexes = dindexes.data;

+      fileCount = rpmtdCount(&bnames);

+ -    dirCount = rpmtdCount(&dnames);

+ +    dirCount = dirCountOrig = rpmtdCount(&dnames);

+      /* XXX TODO: use rpmtdDup() instead */

+      dirNames = dnames.data = duparray(dnames.data, dirCount);

+      dnames.flags |= RPMTD_PTR_ALLOCED;

+ @@ -179,8 +179,9 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,

+  	rpmFileTypes ft;

+  	int fnlen;

+  

+ +	size_t baselen = strlen(baseNames[i]);

+  	size_t len = maxlen +

+ -		strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;

+ +		strlen(dirNames[dirIndexes[i]]) + baselen + 1;

+  	if (len >= fileAlloced) {

+  	    fileAlloced = len * 2;

+  	    fn = xrealloc(fn, fileAlloced);

+ @@ -242,8 +243,9 @@ assert(fn != NULL);		/* XXX can't happen */

+  	    continue;

+  	}

+  

+ -	/* Relocation on full paths only, please. */

+ -	if (fnlen != len) continue;

+ +	/* Relocation on '/' and full paths only, please. */

+ +	if (baselen && fnlen != len)

+ +	    continue;

+  

+  	rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n",

+  	       fn, relocations[j].newPath);

+ @@ -294,7 +296,7 @@ assert(fn != NULL);		/* XXX can't happen */

+      }

+  

+      /* Finish off by relocating directories. */

+ -    for (i = dirCount - 1; i >= 0; i--) {

+ +    for (i = dirCountOrig - 1; i >= 0; i--) {

+  	for (j = numRelocations - 1; j >= 0; j--) {

+  

+  	    if (relocations[j].oldPath == NULL) /* XXX can't happen */

+ -- 

+ 2.45.2

+ 

The added file is too large to be shown here, see it at: 0001-RPM-with-Copy-on-Write.patch
@@ -0,0 +1,51 @@ 

+ From f6ffaacbbf7f5aca45139a612c5dd8db3f2094dc Mon Sep 17 00:00:00 2001

+ From: Michal Domonkos <mdomonko@redhat.com>

+ Date: Mon, 5 Aug 2024 14:40:57 +0200

+ Subject: [PATCH] Skip to hashed subpacket data directly

+ 

+ Let OpenScanHub grok the bigger picture instead of producing a spurious

+ overrun warning for v->hashlen when we're dereferencing p later.

+ 

+ Casting the v pointer back to uint8_t is unnecessary when we could just

+ use h directly but it's done this way in the if branch for pgp version 3

+ in this function as well as in pgpPrtKey() so copy that, just for the

+ sake of consistency.

+ 

+ Along the same lines (consistency), change the p pointer to a const

+ pointer.

+ 

+ No functional change.

+ 

+ Resolves: RHEL-22607

+ ---

+  rpmio/rpmpgp.c | 5 ++---

+  1 file changed, 2 insertions(+), 3 deletions(-)

+ 

+ diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c

+ index d0688ebe9..6f044eb1b 100644

+ --- a/rpmio/rpmpgp.c

+ +++ b/rpmio/rpmpgp.c

+ @@ -565,7 +565,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,

+  		     pgpDigParams _digp)

+  {

+      uint8_t version = 0;

+ -    uint8_t * p;

+ +    const uint8_t * p;

+      unsigned int plen;

+      int rc = 1;

+  

+ @@ -618,10 +618,9 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,

+  	pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype);

+  	pgpPrtNL();

+  

+ -	p = &v->hashlen[0];

+  	if (pgpGet(v->hashlen, sizeof(v->hashlen), h + hlen, &plen))

+  	    return 1;

+ -	p += sizeof(v->hashlen);

+ +	p = ((uint8_t *)v) + sizeof(*v);

+  

+  	if ((p + plen) > (h + hlen))

+  	    return 1;

+ -- 

+ 2.46.0

+ 

@@ -0,0 +1,57 @@ 

+ From d29651be364ef72c7c0f468157602e4ed5cab4ff Mon Sep 17 00:00:00 2001

+ From: Florian Festi <ffesti@redhat.com>

+ Date: Wed, 12 Jun 2024 15:46:12 +0200

+ Subject: [PATCH] Talk about rpmsign in the rpmsign man page

+ 

+ In the past handling signatures was done by the rpm / rpmbuild binaries

+ directly. When this functionality was split into rpmsign the man page

+ was not adjusted accoringly. This is the long overdue update.

+ 

+ Resolves: # 3125

+ 

+ (backported from commit 8e1f55c7004e8c1a7d9140ab2dd9456a7ace3e77)

+ ---

+  doc/rpmsign.8 | 10 +++++-----

+  1 file changed, 5 insertions(+), 5 deletions(-)

+ 

+ diff --git a/doc/rpmsign.8 b/doc/rpmsign.8

+ index f7ceae89b..6c5bcc8ef 100644

+ --- a/doc/rpmsign.8

+ +++ b/doc/rpmsign.8

+ @@ -5,9 +5,9 @@ rpmsign \- RPM Package Signing

+  .SS "SIGNING PACKAGES:"

+  .PP

+  

+ -\fBrpm\fR \fB--addsign|--resign\fR [\fBrpmsign-options\fR] \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+ +\fBrpmsign\fR \fB--addsign|--resign\fR [\fBrpmsign-options\fR] \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+  

+ -\fBrpm\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+ +\fBrpmsign\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+  

+  .SS "rpmsign-options"

+  .PP

+ @@ -22,10 +22,10 @@ options generate and insert new signatures for each package

+  existing signatures. There are two options for historical reasons,

+  there is no difference in behavior currently.

+  

+ -To create a signature rpm needs to verify the package's checksum. As a result

+ +To create a signature rpmsign needs to verify the package's checksum. As a result

+  packages with a MD5/SHA1 checksums cannot be signed in FIPS mode.

+  

+ -\fBrpm\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+ +\fBrpmsign\fR \fB--delsign\fR \fB\fIPACKAGE_FILE\fB\fR\fI ...\fR

+  

+  .PP

+  Delete all signatures from each package \fIPACKAGE_FILE\fR given.

+ @@ -36,7 +36,7 @@ Delete all signatures from each package \fIPACKAGE_FILE\fR given.

+  \fB--rpmv3\fR

+  Force RPM V3 header+payload signature addition.

+  These are expensive and redundant baggage on packages where a separate

+ -payload digest exists (packages built with rpm >= 4.14).  Rpm will

+ +payload digest exists (packages built with rpm >= 4.14).  Rpmsign will

+  automatically detect the need for V3 signatures, but this option can be

+  used to force their creation if the packages must be fully 

+  signature verifiable with rpm < 4.14 or other interoperability reasons.

+ -- 

+ 2.45.2

+ 

@@ -0,0 +1,64 @@ 

+ 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 | 11 +++++------

+  1 file changed, 5 insertions(+), 6 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 <inttypes.h>

+  #include <utime.h>

+  #include <errno.h>

+ -#include <stdbool.h>

+  #include <fcntl.h>

+  #ifdef WITH_CAP

+  #include <sys/capability.h>

+ @@ -56,7 +55,7 @@ struct filedata_s {

+      int stage;

+      int setmeta;

+      int skip;

+ -    bool plugin_contents;

+ +    int plugin_contents;

+      rpmFileAction action;

+      const char *suffix;

+      char *fpath;

+ @@ -890,10 +889,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+      struct diriter_s di = { -1, -1 };

+      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 */

+ @@ -924,7 +923,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	    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;

+ @@ -986,7 +985,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  		rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,

+  					      fp->sb.st_mode, fp->action);

+  		if (rc == RPMRC_PLUGIN_CONTENTS) {

+ -		    fp->plugin_contents = true;

+ +		    fp->plugin_contents = 1;

+  		    rc = RPMRC_OK;

+  		}

+  	    }

+ -- 

+ 2.35.1

+ 

The added file is too large to be shown here, see it at: 0003-Match-formatting-style-of-existing-code.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

+ 

@@ -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

+ @@ -22,3 +22,4 @@ m4_include([rpmreplace.at])

+  m4_include([rpmconfig.at])

+  m4_include([rpmconfig2.at])

+  m4_include([rpmconfig3.at])

+ +m4_include([rpm2extents.at])

+ -- 

+ 2.35.1

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ @@ -203,7 +203,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

+ 

@@ -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

+ +

+  static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)

+  {

+      char const * const pgpmark = "-----BEGIN PGP ";

+ @@ -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

+ 

@@ -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

+ 

@@ -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

+ @@ -41,7 +41,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

+ -

+  static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)

+  {

+      char const * const pgpmark = "-----BEGIN PGP ";

+ @@ -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

+ 

@@ -0,0 +1,214 @@ 

+ 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/Makefile.in  | 51 ++++++++++++++++++++++++++----------------------

+  lib/rpmplugin.h  |  5 +++++

+  lib/rpmplugins.c | 37 +++++++++++++++++++++++++++++++++++

+  lib/rpmplugins.h | 15 ++++++++++++++

+  4 files changed, 85 insertions(+), 23 deletions(-)

+ 

+ diff --git a/lib/Makefile.in b/lib/Makefile.in

+ index e1965d8..c4bba25 100644

+ --- a/lib/Makefile.in

+ +++ b/lib/Makefile.in

+ @@ -173,10 +173,11 @@ am__librpm_la_SOURCES_DIST = backend/dbi.c backend/dbi.h \

+  	rpmlock.c rpmlock.h misc.h relocation.c 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 backend/db3.c backend/bdb_ro.c \

+ -	backend/ndb/glue.c backend/ndb/rpmpkg.c backend/ndb/rpmpkg.h \

+ -	backend/ndb/rpmidx.c backend/ndb/rpmidx.h backend/ndb/rpmxdb.c \

+ -	backend/ndb/rpmxdb.h backend/sqlite.c

+ +	rpmvs.c rpmvs.h rpmextents.c rpmextents_internal.h \

+ +	backend/db3.c backend/bdb_ro.c backend/ndb/glue.c \

+ +	backend/ndb/rpmpkg.c backend/ndb/rpmpkg.h backend/ndb/rpmidx.c \

+ +	backend/ndb/rpmidx.h backend/ndb/rpmxdb.c backend/ndb/rpmxdb.h \

+ +	backend/sqlite.c

+  am__dirstamp = $(am__leading_dot)dirstamp

+  @BDB_TRUE@am__objects_1 = backend/db3.lo

+  @BDB_RO_TRUE@am__objects_2 = backend/bdb_ro.lo

+ @@ -192,8 +193,8 @@ am_librpm_la_OBJECTS = backend/dbi.lo backend/dummydb.lo \

+  	rpmlead.lo rpmps.lo rpmprob.lo rpmrc.lo rpmte.lo rpmts.lo \

+  	rpmfs.lo signature.lo transaction.lo verify.lo rpmlock.lo \

+  	relocation.lo rpmscript.lo rpmchroot.lo rpmplugins.lo rpmug.lo \

+ -	rpmtriggers.lo rpmvs.lo $(am__objects_1) $(am__objects_2) \

+ -	$(am__objects_3) $(am__objects_4)

+ +	rpmtriggers.lo rpmvs.lo rpmextents.lo $(am__objects_1) \

+ +	$(am__objects_2) $(am__objects_3) $(am__objects_4)

+  librpm_la_OBJECTS = $(am_librpm_la_OBJECTS)

+  AM_V_lt = $(am__v_lt_@AM_V@)

+  am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)

+ @@ -228,21 +229,21 @@ am__depfiles_remade = ./$(DEPDIR)/cpio.Plo ./$(DEPDIR)/depends.Plo \

+  	./$(DEPDIR)/relocation.Plo ./$(DEPDIR)/rpmal.Plo \

+  	./$(DEPDIR)/rpmchecksig.Plo ./$(DEPDIR)/rpmchroot.Plo \

+  	./$(DEPDIR)/rpmdb.Plo ./$(DEPDIR)/rpmds.Plo \

+ -	./$(DEPDIR)/rpmfi.Plo ./$(DEPDIR)/rpmfs.Plo \

+ -	./$(DEPDIR)/rpmgi.Plo ./$(DEPDIR)/rpminstall.Plo \

+ -	./$(DEPDIR)/rpmlead.Plo ./$(DEPDIR)/rpmlock.Plo \

+ -	./$(DEPDIR)/rpmplugins.Plo ./$(DEPDIR)/rpmprob.Plo \

+ -	./$(DEPDIR)/rpmps.Plo ./$(DEPDIR)/rpmrc.Plo \

+ -	./$(DEPDIR)/rpmscript.Plo ./$(DEPDIR)/rpmtd.Plo \

+ -	./$(DEPDIR)/rpmte.Plo ./$(DEPDIR)/rpmtriggers.Plo \

+ -	./$(DEPDIR)/rpmts.Plo ./$(DEPDIR)/rpmug.Plo \

+ -	./$(DEPDIR)/rpmvs.Plo ./$(DEPDIR)/signature.Plo \

+ -	./$(DEPDIR)/tagexts.Plo ./$(DEPDIR)/tagname.Plo \

+ -	./$(DEPDIR)/transaction.Plo ./$(DEPDIR)/verify.Plo \

+ -	backend/$(DEPDIR)/bdb_ro.Plo backend/$(DEPDIR)/db3.Plo \

+ -	backend/$(DEPDIR)/dbi.Plo backend/$(DEPDIR)/dbiset.Plo \

+ -	backend/$(DEPDIR)/dummydb.Plo backend/$(DEPDIR)/sqlite.Plo \

+ -	backend/ndb/$(DEPDIR)/glue.Plo \

+ +	./$(DEPDIR)/rpmextents.Plo ./$(DEPDIR)/rpmfi.Plo \

+ +	./$(DEPDIR)/rpmfs.Plo ./$(DEPDIR)/rpmgi.Plo \

+ +	./$(DEPDIR)/rpminstall.Plo ./$(DEPDIR)/rpmlead.Plo \

+ +	./$(DEPDIR)/rpmlock.Plo ./$(DEPDIR)/rpmplugins.Plo \

+ +	./$(DEPDIR)/rpmprob.Plo ./$(DEPDIR)/rpmps.Plo \

+ +	./$(DEPDIR)/rpmrc.Plo ./$(DEPDIR)/rpmscript.Plo \

+ +	./$(DEPDIR)/rpmtd.Plo ./$(DEPDIR)/rpmte.Plo \

+ +	./$(DEPDIR)/rpmtriggers.Plo ./$(DEPDIR)/rpmts.Plo \

+ +	./$(DEPDIR)/rpmug.Plo ./$(DEPDIR)/rpmvs.Plo \

+ +	./$(DEPDIR)/signature.Plo ./$(DEPDIR)/tagexts.Plo \

+ +	./$(DEPDIR)/tagname.Plo ./$(DEPDIR)/transaction.Plo \

+ +	./$(DEPDIR)/verify.Plo backend/$(DEPDIR)/bdb_ro.Plo \

+ +	backend/$(DEPDIR)/db3.Plo backend/$(DEPDIR)/dbi.Plo \

+ +	backend/$(DEPDIR)/dbiset.Plo backend/$(DEPDIR)/dummydb.Plo \

+ +	backend/$(DEPDIR)/sqlite.Plo backend/ndb/$(DEPDIR)/glue.Plo \

+  	backend/ndb/$(DEPDIR)/rpmidx.Plo \

+  	backend/ndb/$(DEPDIR)/rpmpkg.Plo \

+  	backend/ndb/$(DEPDIR)/rpmxdb.Plo

+ @@ -589,8 +590,9 @@ librpm_la_SOURCES = backend/dbi.c backend/dbi.h backend/dummydb.c \

+  	signature.h transaction.c verify.c rpmlock.c rpmlock.h misc.h \

+  	relocation.c 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 $(am__append_1) \

+ -	$(am__append_4) $(am__append_5) $(am__append_8)

+ +	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h rpmextents.c \

+ +	rpmextents_internal.h $(am__append_1) $(am__append_4) \

+ +	$(am__append_5) $(am__append_8)

+  librpm_la_LDFLAGS = -version-info $(rpm_version_info)

+  librpm_la_LIBADD = $(top_builddir)/rpmio/librpmio.la @WITH_POPT_LIB@ \

+  	@WITH_CAP_LIB@ @WITH_ACL_LIB@ @LIBINTL@ $(am__append_2) \

+ @@ -751,6 +753,7 @@ distclean-compile:

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmchroot.Plo@am__quote@ # am--include-marker

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmdb.Plo@am__quote@ # am--include-marker

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmds.Plo@am__quote@ # am--include-marker

+ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmextents.Plo@am__quote@ # am--include-marker

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmfi.Plo@am__quote@ # am--include-marker

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmfs.Plo@am__quote@ # am--include-marker

+  @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmgi.Plo@am__quote@ # am--include-marker

+ @@ -981,6 +984,7 @@ distclean: distclean-am

+  	-rm -f ./$(DEPDIR)/rpmchroot.Plo

+  	-rm -f ./$(DEPDIR)/rpmdb.Plo

+  	-rm -f ./$(DEPDIR)/rpmds.Plo

+ +	-rm -f ./$(DEPDIR)/rpmextents.Plo

+  	-rm -f ./$(DEPDIR)/rpmfi.Plo

+  	-rm -f ./$(DEPDIR)/rpmfs.Plo

+  	-rm -f ./$(DEPDIR)/rpmgi.Plo

+ @@ -1080,6 +1084,7 @@ maintainer-clean: maintainer-clean-am

+  	-rm -f ./$(DEPDIR)/rpmchroot.Plo

+  	-rm -f ./$(DEPDIR)/rpmdb.Plo

+  	-rm -f ./$(DEPDIR)/rpmds.Plo

+ +	-rm -f ./$(DEPDIR)/rpmextents.Plo

+  	-rm -f ./$(DEPDIR)/rpmfi.Plo

+  	-rm -f ./$(DEPDIR)/rpmfs.Plo

+  	-rm -f ./$(DEPDIR)/rpmgi.Plo

+ 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,

+  					      int fd, 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

+ @@ -435,3 +435,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

+ @@ -168,6 +168,21 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,

+                                     int fd, 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

+ 

@@ -0,0 +1,89 @@ 

+ 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 | 37 ++++++++++---------------------------

+  1 file changed, 10 insertions(+), 27 deletions(-)

+ 

+ diff --git a/lib/fsm.c b/lib/fsm.c

+ index feda3750c..711f553d3 100644

+ --- a/lib/fsm.c

+ +++ b/lib/fsm.c

+ @@ -55,7 +55,6 @@ struct filedata_s {

+      int stage;

+      int setmeta;

+      int skip;

+ -    int plugin_contents;

+      rpmFileAction action;

+      const char *suffix;

+      char *fpath;

+ @@ -918,20 +917,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	fp->setmeta = (fp->skip == 0) &&

+  		      (fp->sb.st_nlink == 1 || fp->action == FA_TOUCH);

+  

+ -	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;

+ -	default:

+ -	    fp->action = FA_SKIP;

+ -	    fp->skip = XFA_SKIPPING(fp->action);

+ -	}

+ +	setFileState(fs, fx);

+  	fsmDebug(rpmfiDN(fi), fp->fpath, fp->action, &fp->sb);

+  

+  	fp->stage = FILE_PRE;

+ @@ -984,10 +970,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	    if (!rc) {

+  		rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,

+  					      fp->sb.st_mode, fp->action);

+ -		if (rc == RPMRC_PLUGIN_CONTENTS) {

+ -		    fp->plugin_contents = 1;

+ -		    rc = RPMRC_OK;

+ -		}

+  	    }

+  	    if (rc)

+  		goto setmeta; /* for error notification */

+ @@ -1016,15 +998,16 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	    if (fp->action == FA_TOUCH)

+  		goto setmeta;

+  

+ -            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(di.dirfd, fi, fp, files, psm, nodigest,

+ -				       &firstlink, &firstlinkfile,

+ -				       &di.firstdir, &fd);

+ -		    }

+ +		    rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest,

+ +				   &firstlink, &firstlinkfile,

+ +				   &di.firstdir, &fd);

+  		}

+              } else if (S_ISDIR(fp->sb.st_mode)) {

+                  if (rc == RPMERR_ENOENT) {

+ -- 

+ 2.35.1

+ 

@@ -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

+ 

@@ -0,0 +1,33 @@ 

+ 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,15 @@ function runroot()

+      )

+  }

+  

+ +function runroot_plugins()

+ +{

+ +    setup_env

+ +    (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()

+  {

+      setup_env

+ -- 

+ 2.35.1

+ 

@@ -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

+      CAP_DISABLED=true;

+  fi

+  

+ +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 setup_env()

+  {

+      if [ -d testing ]; then

+ 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

+ 

@@ -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

+ @@ -471,4 +471,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

+ @@ -182,7 +182,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

+ 

@@ -0,0 +1,175 @@ 

+ 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         | 48 ++++++++++++++++++++++++++---------------------

+  plugins/reflink.c | 18 +++++++++++++-----

+  3 files changed, 40 insertions(+), 28 deletions(-)

+ 

+ diff --git a/lib/depends.c b/lib/depends.c

+ index 8998afcd3..30234df3d 100644

+ --- a/lib/depends.c

+ +++ b/lib/depends.c

+ @@ -80,8 +80,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

+ @@ -17,7 +17,6 @@

+  #include <rpm/rpmts.h>

+  #include <rpm/rpmlog.h>

+  #include <rpm/rpmmacro.h>

+ -#include <rpm/rpmlib.h>

+  

+  #include "rpmio/rpmio_internal.h"	/* fdInit/FiniDigest */

+  #include "fsm.h"

+ @@ -869,6 +868,24 @@ static rpmfi fsmIterFini(rpmfi fi, struct diriter_s *di)

+      return rpmfiFree(fi);

+  }

+  

+ +static int fiIterator(rpmPlugins plugins, FD_t payload, rpmfiles files, rpmfi *fi)

+ +{

+ +    rpmRC plugin_rc = rpmpluginsCallFsmFileArchiveReader(plugins, payload, files, fi);

+ +    switch (plugin_rc) {

+ +	case RPMRC_PLUGIN_CONTENTS:

+ +	    if (*fi == NULL)

+ +                return RPMERR_BAD_MAGIC;

+ +            return RPMRC_OK;

+ +	case RPMRC_OK:

+ +	    *fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);

+ +	    if (*fi == NULL)

+ +                return RPMERR_BAD_MAGIC;

+ +            return RPMRC_OK;

+ +	default:

+ +            return RPMRC_FAIL;

+ +    }

+ +}

+ +

+  int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+                rpmpsm psm, char ** failedFile)

+  {

+ @@ -886,13 +903,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+      struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));

+      struct filedata_s *firstlink = NULL;

+      struct diriter_s di = { -1, -1 };

+ -    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));

+ @@ -927,12 +937,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+      if (rc)

+  	goto exit;

+  

+ -    if (cpio) {

+ -	fi = fsmIter(payload, files,

+ -		     payload ? RPMFI_ITER_READ_ARCHIVE : RPMFI_ITER_FWD, &di);

+ -    } else {

+ -	fi = rpmfilesIter(files, RPMFI_ITER_FWD);

+ -    }

+ +    rc = fiIterator(plugins, payload, files, &fi);

+ +    if (rc)

+ +        goto exit;

+  

+      if (fi == NULL) {

+          rc = RPMERR_BAD_MAGIC;

+ @@ -956,7 +963,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	    int mayopen = 0;

+  	    int fd = -1;

+  

+ -	    if (!cpio && di.dirfd >= 0)

+ +	    if (di.dirfd >= 0)

+  		fsmClose(&di.dirfd);

+  	    rc = ensureDir(plugins, rpmfiDN(fi), 0,

+  			    (fp->action == FA_CREATE), 0, &di.dirfd);

+ @@ -1075,16 +1082,13 @@ setmeta:

+  	rc = fx;

+  

+      /* If all went well, commit files to final destination */

+ -    if (cpio) {

+ -	fi = fsmIter(NULL, files, RPMFI_ITER_FWD, &di);

+ -    } else {

+ -	fi = rpmfilesIter(files, RPMFI_ITER_FWD);

+ -    }

+ +    fi = fsmIter(NULL, files, RPMFI_ITER_FWD, &di);

+ +

+      while (!rc && (fx = rpmfiNext(fi)) >= 0) {

+  	struct filedata_s *fp = &fdata[fx];

+  

+  	if (!fp->skip) {

+ -	    if (!cpio && di.dirfd >= 0)

+ +	    if (di.dirfd >= 0)

+  		fsmClose(&di.dirfd);

+  	    if (!rc)

+  		rc = ensureDir(NULL, rpmfiDN(fi), 0, 0, 0, &di.dirfd);

+ 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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -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

+ 

@@ -0,0 +1,98 @@ 

+ 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"

+ @@ -1286,19 +1287,25 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)

+  

+  	rpmtsNotify(ts, p, RPMCALLBACK_VERIFY_PROGRESS, oc++, total);

+  	FD_t fd = rpmtsNotify(ts, p, RPMCALLBACK_INST_OPEN_FILE, 0, 0);

+ -	if (fd != NULL) {

+ -	    prc = rpmpkgRead(vs, fd, NULL, NULL, &vd.msg);

+ -	    rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_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);

+ +	    }

+ +	    if (prc == RPMRC_OK)

+ +		prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);

+ +

+ +	    /* Record verify result */

+ +	    if (vd.type[RPMSIG_SIGNATURE_TYPE] == RPMRC_OK)

+ +		verified |= RPMSIG_SIGNATURE_TYPE;

+ +	    if (vd.type[RPMSIG_DIGEST_TYPE] == RPMRC_OK)

+ +		verified |= RPMSIG_DIGEST_TYPE;

+  	}

+  

+ -	if (prc == RPMRC_OK)

+ -	    prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);

+ -

+ -	/* Record verify result */

+ -	if (vd.type[RPMSIG_SIGNATURE_TYPE] == RPMRC_OK)

+ -	    verified |= RPMSIG_SIGNATURE_TYPE;

+ -	if (vd.type[RPMSIG_DIGEST_TYPE] == RPMRC_OK)

+ -	    verified |= RPMSIG_DIGEST_TYPE;

+  	rpmteSetVerified(p, verified);

+  

+  	if (prc)

+ 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

+ 

@@ -0,0 +1,395 @@ 

+ From: Richard Phibel <richardphibel@meta.com>

+ 

+ Subject: RPM with Copy on Write: add deny list mechanism

+ 

+ commit 3431550e6c92ba4bc6d091cb244f70c158dfbbaa

+ Author: Richard Phibel <richardphibel@meta.com>

+ Date:   Wed Oct 19 23:05:17 2022 +0200

+ 

+     RPM with Copy on Write: add deny list mechanism

+     

+     A couple of issues were encountered when using RPM CoW for some

+     packages. This change adds a deny list mechanism to disable RPM CoW for

+     specific packages.

+ 

+ Signed-off-by: Richard Phibel <richardphibel@meta.com>

+ ---

+  build/pack.c         |   2 +-

+  lib/package.c        |   6 +--

+  lib/rpmlead.c        |  43 +++++++++---------

+  lib/rpmlead.h        |  37 +++++++++++++++-

+  rpm2extents.c        | 102 ++++++++++++++++++++++++++++++++-----------

+  sign/rpmgensig.c     |   2 +-

+  tests/rpm2extents.at |  11 +++++

+  7 files changed, 148 insertions(+), 55 deletions(-)

+ 

+ diff --git a/build/pack.c b/build/pack.c

+ index 8d6f74935ebb8a4d65aa2bcb666825a99162be9c..e2f05b6412d3e0d814a1160a0dcdfad5c323a8f8 100644

+ --- a/build/pack.c

+ +++ b/build/pack.c

+ @@ -493,7 +493,7 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp,

+      }

+  

+      /* Write the lead section into the package. */

+ -    if (rpmLeadWrite(fd, pkg->header)) {

+ +    if (rpmLeadWriteFromHeader(fd, pkg->header)) {

+  	rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd));

+  	goto exit;

+      }

+ diff --git a/lib/package.c b/lib/package.c

+ index 90bd0d8a719353e8965c140b40e2dceef80a2ed1..fd41abbf6b4df07afa2caf1c47d9724765ee84e8 100644

+ --- a/lib/package.c

+ +++ b/lib/package.c

+ @@ -410,11 +410,7 @@ rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp)

+      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);

+ +    rpmRC rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg);

+      if (rc != RPMRC_OK)

+  	goto exit;

+  

+ diff --git a/lib/rpmlead.c b/lib/rpmlead.c

+ index 45b1c6f8edacb65549ba8d321de8f6ce8cef4503..82105122724a8efb071aecefdb278ef96ea5e70d 100644

+ --- a/lib/rpmlead.c

+ +++ b/lib/rpmlead.c

+ @@ -24,24 +24,6 @@ static unsigned char const lead_magic[] = {

+      RPMLEAD_MAGIC0, RPMLEAD_MAGIC1, RPMLEAD_MAGIC2, RPMLEAD_MAGIC3

+  };

+  

+ -/** \ingroup lead

+ - * The lead data structure.

+ - * The lead needs to be 8 byte aligned.

+ - * @deprecated The lead (except for signature_type) is legacy.

+ - * @todo Don't use any information from lead.

+ - */

+ -struct rpmlead_s {

+ -    unsigned char magic[4];

+ -    unsigned char major;

+ -    unsigned char minor;

+ -    short type;

+ -    short archnum;

+ -    char name[66];

+ -    short osnum;

+ -    short signature_type;       /*!< Signature header type (RPMSIG_HEADERSIG) */

+ -    char reserved[16];      /*!< Pad to 96 bytes -- 8 byte aligned! */

+ -};

+ -

+  static int rpmLeadFromHeader(Header h, struct rpmlead_s *l)

+  {

+      if (h != NULL) {

+ @@ -70,13 +52,23 @@ static int rpmLeadFromHeader(Header h, struct rpmlead_s *l)

+  }

+  

+  /* The lead needs to be 8 byte aligned */

+ -rpmRC rpmLeadWrite(FD_t fd, Header h)

+ +rpmRC rpmLeadWriteFromHeader(FD_t fd, Header h)

+  {

+      rpmRC rc = RPMRC_FAIL;

+      struct rpmlead_s l;

+  

+ -    if (rpmLeadFromHeader(h, &l)) {

+ -	

+ +    if (rpmLeadFromHeader(h, &l)) {	

+ +	rc = rpmLeadWrite(fd, l);

+ +    }

+ +

+ +    return rc;

+ +}

+ +

+ +/* The lead needs to be 8 byte aligned */

+ +rpmRC rpmLeadWrite(FD_t fd, struct rpmlead_s l)

+ +{

+ +    rpmRC rc = RPMRC_FAIL;

+ +

+  	l.type = htons(l.type);

+  	l.archnum = htons(l.archnum);

+  	l.osnum = htons(l.osnum);

+ @@ -84,7 +76,6 @@ rpmRC rpmLeadWrite(FD_t fd, Header h)

+  	    

+  	if (Fwrite(&l, 1, sizeof(l), fd) == sizeof(l))

+  	    rc = RPMRC_OK;

+ -    }

+  

+      return rc;

+  }

+ @@ -107,6 +98,11 @@ static rpmRC rpmLeadCheck(struct rpmlead_s *lead, char **msg)

+  }

+  

+  rpmRC rpmLeadRead(FD_t fd, char **emsg)

+ +{

+ +    return rpmLeadReadAndReturn(fd, emsg, NULL);

+ +}

+ +

+ +rpmRC rpmLeadReadAndReturn(FD_t fd, char **emsg, struct rpmlead_s * ret)

+  {

+      rpmRC rc = RPMRC_OK;

+      struct rpmlead_s l;

+ @@ -136,5 +132,8 @@ rpmRC rpmLeadRead(FD_t fd, char **emsg)

+  	    free(err);

+      }

+  

+ +	if (ret)

+ +		*ret = l;

+ +

+      return rc;

+  }

+ diff --git a/lib/rpmlead.h b/lib/rpmlead.h

+ index 9d86a8d73b3250d3c306b3a3ac4a33486e6920ec..8a592abc1d0e69822f438c4c7b248cce1cb5ee72 100644

+ --- a/lib/rpmlead.h

+ +++ b/lib/rpmlead.h

+ @@ -19,13 +19,39 @@ extern "C" {

+  

+  #define RPMLEAD_SIZE 96         /*!< Don't rely on sizeof(struct) */

+  

+ +/** \ingroup lead

+ + * The lead data structure.

+ + * The lead needs to be 8 byte aligned.

+ + * @deprecated The lead (except for signature_type) is legacy.

+ + * @todo Don't use any information from lead.

+ + */

+ +struct rpmlead_s {

+ +    unsigned char magic[4];

+ +    unsigned char major;

+ +    unsigned char minor;

+ +    short type;

+ +    short archnum;

+ +    char name[66];

+ +    short osnum;

+ +    short signature_type;       /*!< Signature header type (RPMSIG_HEADERSIG) */

+ +    char reserved[16];      /*!< Pad to 96 bytes -- 8 byte aligned! */

+ +};

+ +

+  /** \ingroup lead

+   * Write lead to file handle.

+   * @param fd		file handle

+   * @param h		package header

+   * @return		RPMRC_OK on success, RPMRC_FAIL on error

+   */

+ -rpmRC rpmLeadWrite(FD_t fd, Header h);

+ +rpmRC rpmLeadWriteFromHeader(FD_t fd, Header h);

+ +

+ +/** \ingroup lead

+ + * Write lead to file handle.

+ + * @param fd		file handle

+ + * @param l		lead

+ + * @return		RPMRC_OK on success, RPMRC_FAIL on error

+ + */

+ +rpmRC rpmLeadWrite(FD_t fd, struct rpmlead_s l);

+  

+  /** \ingroup lead

+   * Read lead from file handle.

+ @@ -35,6 +61,15 @@ rpmRC rpmLeadWrite(FD_t fd, Header h);

+   */

+  rpmRC rpmLeadRead(FD_t fd, char **emsg);

+  

+ +/** \ingroup lead

+ + * Read lead from file handle and return it.

+ + * @param fd		file handle

+ + * @param[out] emsg		failure message on error (malloced)

+ + * @param[out] ret		address of lead

+ + * @return		RPMRC_OK on success, RPMRC_FAIL/RPMRC_NOTFOUND on error

+ + */

+ +rpmRC rpmLeadReadAndReturn(FD_t fd, char **emsg, struct rpmlead_s * ret);

+ +

+  #ifdef __cplusplus

+  }

+  #endif

+ diff --git a/rpm2extents.c b/rpm2extents.c

+ index 7dd5128decb03781411bd714339b3b6e9b805842..702d3765d76faf618992ca112011d2312d7fcdcc 100644

+ --- a/rpm2extents.c

+ +++ b/rpm2extents.c

+ @@ -134,6 +134,28 @@ exit:

+      return rc;

+  }

+  

+ +/**

+ + * Check if package is in deny list.

+ + * @param package_name	package name

+ + * @return 		true if package is in deny list

+ + */

+ +static inline int isInDenyList(char * package_name)

+ +{

+ +    int is_in_deny_list = 0;

+ +    if (package_name) {

+ +	char *e_denylist = getenv("LIBREPO_TRANSCODE_RPMS_DENYLIST");

+ +	char *denytlist_item = strtok(e_denylist, ",");

+ +	while (denytlist_item) {

+ +	    if (strstr(package_name, denytlist_item)) {

+ +		is_in_deny_list = 1;

+ +		break;

+ +	    }

+ +	    denytlist_item = strtok(NULL, ",");

+ +	}

+ +    }

+ +	return is_in_deny_list;

+ +}

+ +

+  static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) {

+      size_t len;

+      rpmRC rc = RPMRC_FAIL;

+ @@ -239,15 +261,41 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+      uint32_t offset_ix = 0;

+      size_t len;

+      int next = 0;

+ +	struct rpmlead_s l;

+ +    rpmfiles files = NULL;

+ +    rpmfi fi = NULL;

+ +    char *msg = NULL;

+  

+      fdo = fdDup(STDOUT_FILENO);

+  

+ +    rc = rpmLeadReadAndReturn(fdi, &msg, &l);

+ +    if (rc != RPMRC_OK)

+ +	goto exit;

+ +

+ +    /* Skip conversion if package is in deny list */

+ +    if (isInDenyList(l.name)) {

+ +	if (rpmLeadWrite(fdo, l)) {

+ +		fprintf(stderr, _("Unable to write package lead: %s\n"),

+ +		Fstrerror(fdo));

+ +	    rc = RPMRC_FAIL;

+ +	    goto exit;

+ +	}

+ +

+ +	ssize_t fdilength = ufdCopy(fdi, fdo);

+ +	if (fdilength == -1) {

+ +	    fprintf(stderr, _("process_package cat failed\n"));

+ +	    rc = RPMRC_FAIL;

+ +	    goto exit;

+ +	}

+ +

+ +	goto exit;

+ +    } else {

+      if (rpmReadPackageRaw(fdi, &sigh, &h)) {

+  	rpmlog(RPMLOG_ERR, _("Error reading package\n"));

+  	exit(EXIT_FAILURE);

+      }

+  

+ -    if (rpmLeadWrite(fdo, h))

+ +    if (rpmLeadWriteFromHeader(fdo, h))

+      {

+  	rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"),

+  		Fstrerror(fdo));

+ @@ -264,38 +312,41 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  	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);

+ -    }

+ +	/* 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);

+ +	gzdi = Fdopen(fdi, rpmio_flags);	/* XXX gzdi == fdi */

+ +	free(rpmio_flags);

+  

+      if (gzdi == NULL) {

+  	rpmlog(RPMLOG_ERR, _("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);

+ +	files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);

+ +	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);

+ +	/* 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);

+ +	/* 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);

+ +	zeros = xcalloc(fundamental_block_size, 1);

+  

+      while (next >= 0) {

+  	next = rpmfiNext(fi);

+ @@ -342,8 +393,8 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+      }

+      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);

+  

+      validation_pos = pos;

+      ssize_t validation_len = ufdCopy(validationi, fdo);

+ @@ -412,6 +463,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  	rc = RPMRC_FAIL;

+  	goto exit;

+      }

+ +	}

+  

+  exit:

+      rpmfilesFree(files);

+ diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c

+ index b4897f1de444080c8dc6c4913b5f33de20796822..d54e254ff41a0441d04fbcf27f26e4809f563b79 100644

+ --- a/sign/rpmgensig.c

+ +++ b/sign/rpmgensig.c

+ @@ -610,7 +610,7 @@ static int rpmSign(const char *rpm, int deleting, int flags)

+  	}

+  

+  	/* Write the lead/signature of the output rpm */

+ -	rc = rpmLeadWrite(ofd, h);

+ +	rc = rpmLeadWriteFromHeader(ofd, h);

+  	if (rc != RPMRC_OK) {

+  	    rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,

+  		Fstrerror(ofd));

+ diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at

+ index 5135c9cf83d9e75e9d9bc0b84186ab10cc0cbcac..c9c79c5acd22b86704460f295712ce7ab5ee3259 100644

+ --- a/tests/rpm2extents.at

+ +++ b/tests/rpm2extents.at

+ @@ -95,6 +95,17 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

+  [])

+  AT_CLEANUP

+  

+ +# check that package in denylist is not transcoded

+ +AT_SETUP([rpm2extents denylist])

+ +AT_KEYWORDS([rpm2extents])

+ +AT_CHECK([

+ +export LIBREPO_TRANSCODE_RPMS_DENYLIST="vim,hello,cowsay"

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | runroot_other cmp /data/RPMS/hello-2.0-1.x86_64.rpm -],

+ +[0],

+ +[],

+ +[ignore])

+ +AT_CLEANUP

+ +

+  AT_SETUP([rpm2extents install package])

+  AT_KEYWORDS([rpm2extents reflink])

+  AT_SKIP_IF([$REFLINK_DISABLED])

@@ -0,0 +1,386 @@ 

+ From: Richard Phibel <richardphibel@meta.com>

+ 

+ commit 7976c921f60ec5d20c50c4c702ec5636b39210ba

+ Author: Richard Phibel <richardphibel@meta.com>

+ Date:   Wed Oct 26 18:57:19 2022 +0200

+ 

+     RPM with Copy on Write: workaround for corrupt signature header

+     

+     Some packages have errors in the signature header. These errors do not

+     prevent installation of the package but cause issues in rpm2extents

+     conversion. This commit implements the same fix as in

+     unloadImmutableRegion.

+ 

+ Signed-off-by: Richard Phibel <richardphibel@meta.com>

+ ---

+  rpm2extents.c | 281 +++++++++++++++++++++++++++-----------------------

+  1 file changed, 153 insertions(+), 128 deletions(-)

+ 

+ diff --git a/rpm2extents.c b/rpm2extents.c

+ index 702d3765d76faf618992ca112011d2312d7fcdcc..b641511fff884e3c8fb396f28acbd8e2c736e28a 100644

+ --- a/rpm2extents.c

+ +++ b/rpm2extents.c

+ @@ -139,7 +139,7 @@ exit:

+   * @param package_name	package name

+   * @return 		true if package is in deny list

+   */

+ -static inline int isInDenyList(char * package_name)

+ +static inline int isInDenyList(char *package_name)

+  {

+      int is_in_deny_list = 0;

+      if (package_name) {

+ @@ -153,7 +153,7 @@ static inline int isInDenyList(char * package_name)

+  	    denytlist_item = strtok(NULL, ",");

+  	}

+      }

+ -	return is_in_deny_list;

+ +    return is_in_deny_list;

+  }

+  

+  static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) {

+ @@ -229,6 +229,22 @@ exit:

+      return rc;

+  }

+  

+ +static void sanitizeSignatureHeader(Header * sigh)

+ +{

+ +    struct rpmtd_s td;

+ +

+ +    /* This is inspired by the code in unloadImmutableRegion. See https://github.com/rpm-software-management/rpm/pull/1330 */

+ +    if (!headerGet(*sigh, RPMTAG_HEADERSIGNATURES, &td, HEADERGET_DEFAULT)) {

+ +	/* Signature header corrupt/missing */

+ +	rpmlog(RPMLOG_WARNING, _("Error verifying signature header\n"));

+ +	rpmtdFreeData(&td);

+ +	Header nh = headerCopy(*sigh);

+ +	headerFree(*sigh);

+ +	*sigh = headerLink(nh);

+ +	headerFree(nh);

+ +    }

+ +}

+ +

+  static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  {

+      uint32_t diglen;

+ @@ -261,7 +277,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+      uint32_t offset_ix = 0;

+      size_t len;

+      int next = 0;

+ -	struct rpmlead_s l;

+ +    struct rpmlead_s l;

+      rpmfiles files = NULL;

+      rpmfi fi = NULL;

+      char *msg = NULL;

+ @@ -274,43 +290,47 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  

+      /* Skip conversion if package is in deny list */

+      if (isInDenyList(l.name)) {

+ +	rpmlog(RPMLOG_WARNING, _("package %s is in deny list: conversion skipped\n"), l.name);

+  	if (rpmLeadWrite(fdo, l)) {

+ -		fprintf(stderr, _("Unable to write package lead: %s\n"),

+ -		Fstrerror(fdo));

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"),

+ +		    Fstrerror(fdo));

+  	    rc = RPMRC_FAIL;

+  	    goto exit;

+  	}

+  

+  	ssize_t fdilength = ufdCopy(fdi, fdo);

+  	if (fdilength == -1) {

+ -	    fprintf(stderr, _("process_package cat failed\n"));

+ +	    rpmlog(RPMLOG_ERR, _("process_package cat failed\n"));

+  	    rc = RPMRC_FAIL;

+  	    goto exit;

+  	}

+  

+  	goto exit;

+      } else {

+ -    if (rpmReadPackageRaw(fdi, &sigh, &h)) {

+ -	rpmlog(RPMLOG_ERR, _("Error reading package\n"));

+ -	exit(EXIT_FAILURE);

+ -    }

+ +	if (rpmReadPackageRaw(fdi, &sigh, &h)) {

+ +	    rpmlog(RPMLOG_ERR, _("Error reading package\n"));

+ +	    exit(EXIT_FAILURE);

+ +	}

+  

+ -    if (rpmLeadWriteFromHeader(fdo, h))

+ -    {

+ -	rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"),

+ -		Fstrerror(fdo));

+ -	exit(EXIT_FAILURE);

+ -    }

+ +	sanitizeSignatureHeader(&sigh);

+  

+ -    if (rpmWriteSignature(fdo, sigh)) {

+ -	rpmlog(RPMLOG_ERR, _("Unable to write signature: %s\n"), Fstrerror(fdo));

+ -	exit(EXIT_FAILURE);

+ -    }

+ +	if (rpmLeadWriteFromHeader(fdo, h)) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"),

+ +		   Fstrerror(fdo));

+ +	    exit(EXIT_FAILURE);

+ +	}

+  

+ -    if (headerWrite(fdo, h, HEADER_MAGIC_YES)) {

+ -	rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo));

+ -	exit(EXIT_FAILURE);

+ -    }

+ +	if (rpmWriteSignature(fdo, sigh)) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write signature: %s\n"),

+ +		   Fstrerror(fdo));

+ +	    exit(EXIT_FAILURE);

+ +	}

+ +

+ +	if (headerWrite(fdo, h, HEADER_MAGIC_YES)) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"),

+ +		   Fstrerror(fdo));

+ +	    exit(EXIT_FAILURE);

+ +	}

+  

+  	/* Retrieve payload size and compression type. */

+  	{

+ @@ -323,10 +343,11 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  	gzdi = Fdopen(fdi, rpmio_flags);	/* XXX gzdi == fdi */

+  	free(rpmio_flags);

+  

+ -    if (gzdi == NULL) {

+ -	rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));

+ -	exit(EXIT_FAILURE);

+ -    }

+ +	if (gzdi == NULL) {

+ +	    rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"),

+ +		   Fstrerror(gzdi));

+ +	    exit(EXIT_FAILURE);

+ +	}

+  

+  	files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);

+  	fi = rpmfiNewArchiveReader(gzdi, files,

+ @@ -348,124 +369,128 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  

+  	zeros = xcalloc(fundamental_block_size, 1);

+  

+ -    while (next >= 0) {

+ -	next = rpmfiNext(fi);

+ -	if (next == RPMERR_ITER_END) {

+ -	    rc = RPMRC_OK;

+ -	    break;

+ +	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) {

+ +		rpmlog(RPMLOG_ERR, _("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) {

+ +		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;

+  	}

+ -	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;

+ +	Fclose(gzdi);		/* XXX gzdi == fdi */

+ +

+ +	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 = rpmfiFDigest(fi, NULL, NULL);

+ -	if (digestSetGetEntry(ds, digest, NULL)) {

+ -	    /* This specific digest has already been included, so skip it. */

+ -	    continue;

+ +

+ +	digest_table_pos = validation_pos + validation_len;

+ +

+ +	len = sizeof(offset_ix);

+ +	if (Fwrite(&offset_ix, len, 1, fdo) != len) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write length of table\n"));

+ +	    rc = RPMRC_FAIL;

+ +	    goto exit;

+  	}

+ -	pad = pad_to(pos, fundamental_block_size);

+ -	if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {

+ -	    rpmlog(RPMLOG_ERR, _("Unable to write padding\n"));

+ +	len = sizeof(diglen);

+ +	if (Fwrite(&diglen, len, 1, fdo) != len) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write length of digest\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) {

+ -	    char *errstr = rpmfileStrerror(rc);

+ -	    rpmlog(RPMLOG_ERR,

+ -		   _("rpmfiArchiveReadToFile failed while extracting "\

+ -		   "\"%s\" with RC %d: %s\n"),

+ -		   rpmfiFN(fi), rc, errstr);

+ -	    free(errstr);

+ +	len = sizeof(rpm_loff_t);

+ +	for (int x = 0; x < offset_ix; x++) {

+ +	    if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) {

+ +		rpmlog(RPMLOG_ERR, _("Unable to write digest\n"));

+ +		rc = RPMRC_FAIL;

+ +		goto exit;

+ +	    }

+ +	    if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) {

+ +		rpmlog(RPMLOG_ERR, _("Unable to write offset\n"));

+ +		rc = RPMRC_FAIL;

+ +		goto exit;

+ +	    }

+ +	}

+ +	digest_pos =

+ +	    (digest_table_pos + sizeof(offset_ix) + sizeof(diglen) +

+ +	     offset_ix * (diglen + sizeof(rpm_loff_t))

+ +	    );

+ +

+ +	ssize_t digest_len = ufdCopy(digestori, fdo);

+ +	if (digest_len == -1) {

+ +	    rpmlog(RPMLOG_ERR, _("digest table ufdCopy failed\n"));

+ +	    rc = RPMRC_FAIL;

+  	    goto exit;

+  	}

+ -	pos += size;

+ -    }

+ -    Fclose(gzdi);	/* XXX gzdi == fdi */

+ -

+ -	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;

+ +	/* 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.

+ +	 */

+  

+ -    len = sizeof(offset_ix);

+ -    if (Fwrite(&offset_ix, len, 1, fdo) != len) {

+ -	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) {

+ -	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) {

+ -	    rpmlog(RPMLOG_ERR, _("Unable to write digest\n"));

+ +	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) {

+ +	    rpmlog(RPMLOG_ERR, _("Unable to write final padding\n"));

+  	    rc = RPMRC_FAIL;

+  	    goto exit;

+  	}

+ -	if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) {

+ -	    rpmlog(RPMLOG_ERR, _("Unable to write offset\n"));

+ +	zeros = _free(zeros);

+ +	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;

+  	}

+      }

+ -    digest_pos = (

+ -	digest_table_pos + sizeof(offset_ix) + sizeof(diglen) +

+ -	offset_ix * (diglen + sizeof(rpm_loff_t))

+ -    );

+ -

+ -    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.

+ -    */

+  

+ -    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) {

+ -	rpmlog(RPMLOG_ERR, _("Unable to write final padding\n"));

+ -	rc = RPMRC_FAIL;

+ -	goto exit;

+ -    }

+ -    zeros = _free(zeros);

+ -    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;

+ -    }

+ -	}

+ -

+ -exit:

+ +  exit:

+      rpmfilesFree(files);

+      rpmfiFree(fi);

+      headerFree(h);

@@ -0,0 +1,80 @@ 

+ From 937f9bc67b905851c78719d8397926eaa97b174a Mon Sep 17 00:00:00 2001

+ From: Richard Phibel <richardphibel@meta.com>

+ Date: Mon, 22 May 2023 05:16:51 +0200

+ Subject: [PATCH] Fix stack overflow

+ 

+ Creation of array struct digestoffset offsets[rpmfiFC(fi)] caused a

+ stack overflow because the total size is greater than 8M which is the

+ stack size limit on Linux. To fix the issue, the array is allocated on

+ the heap.

+ 

+ I used AddressSanitizer to find the root cause of the issue. It found a

+ number of memory leaks so I fixed them as well.

+ ---

+  rpm2extents.c | 15 +++++++++++----

+  1 file changed, 11 insertions(+), 4 deletions(-)

+ 

+ diff --git a/rpm2extents.c b/rpm2extents.c

+ index c2a373914..0ee8666fa 100644

+ --- a/rpm2extents.c

+ +++ b/rpm2extents.c

+ @@ -226,6 +226,7 @@ exit:

+      if(msg) {

+  	free(msg);

+      }

+ +    rpmtsFree(ts);

+      return rc;

+  }

+  

+ @@ -243,6 +244,7 @@ static void sanitizeSignatureHeader(Header * sigh)

+  	*sigh = headerLink(nh);

+  	headerFree(nh);

+      }

+ +    rpmtdFreeData(&td);

+  }

+  

+  static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+ @@ -281,6 +283,8 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+      rpmfiles files = NULL;

+      rpmfi fi = NULL;

+      char *msg = NULL;

+ +    struct digestoffset *offsets = NULL;

+ +    digestSet ds = NULL;

+  

+      fdo = fdDup(STDOUT_FILENO);

+  

+ @@ -357,10 +361,8 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  	 * now?)

+  	 */

+  	diglen = (uint32_t) rpmDigestLength(rpmfiDigestAlgo(fi));

+ -	digestSet ds =

+ -	    digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp,

+ -			    NULL);

+ -	struct digestoffset offsets[rpmfiFC(fi)];

+ +	ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp, NULL);

+ +	offsets = xcalloc(rpmfiFC(fi), sizeof(*offsets));

+  	pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES);

+  

+  	/* main headers are aligned to 8 byte boundry */

+ @@ -494,6 +496,10 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+      rpmfilesFree(files);

+      rpmfiFree(fi);

+      headerFree(h);

+ +    headerFree(sigh);

+ +    free(offsets);

+ +    Fclose(fdo);

+ +    digestSetFree(ds);

+      return rc;

+  }

+  

+ @@ -693,6 +699,7 @@ int main(int argc, char *argv[]) {

+  

+      FD_t fdi = fdDup(STDIN_FILENO);

+      rc = teeRpm(fdi, algos, nb_algos);

+ +    Fclose(fdi);

+      if (rc != RPMRC_OK) {

+  	/* translate rpmRC into generic failure return code. */

+  	return EXIT_FAILURE;

+ -- 

+ 2.40.1

+ 

@@ -0,0 +1,28 @@ 

+ From a3b6102b4d2e79a8b74b036c6a29272a7f6e5c6a Mon Sep 17 00:00:00 2001

+ From: Richard Phibel <richardphibel@meta.com>

+ Date: Fri, 11 Aug 2023 00:43:21 +0200

+ Subject: [PATCH] Fix issue for transaction with transcoded and non-transcoded

+  packages

+ 

+ The flag saying whether a package is transcoded is not clean-up between

+ each packages. Because of that if a non-transcoded package is treated

+ after a transcoded one, the package is treated as transcoded

+ ---

+  plugins/reflink.c | 1 +

+  1 file changed, 1 insertion(+)

+ 

+ diff --git a/plugins/reflink.c b/plugins/reflink.c

+ index 986cbd172..20d35eefd 100644

+ --- a/plugins/reflink.c

+ +++ b/plugins/reflink.c

+ @@ -234,6 +234,7 @@ static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)

+  	inodeIndexHashFree(state->inodeIndexes);

+  	state->inodeIndexes = NULL;

+      }

+ +    state->transcoded = 0;

+      return RPMRC_OK;

+  }

+  

+ -- 

+ 2.40.1

+ 

@@ -0,0 +1,11 @@ 

+ --- a/rpm2extents.c      2024-07-03 07:13:36.195332381 -0700

+ +++ b/rpm2extents.c      2024-07-03 07:13:43.606553540 -0700

+ @@ -269,7 +269,7 @@

+ 

+      FD_t fdo;

+      FD_t gzdi;

+ -    Header h, sigh;

+ +    Header h=NULL, sigh=NULL;

+      long fundamental_block_size = sysconf(_SC_PAGESIZE);

+      rpmRC rc = RPMRC_OK;

+      rpm_mode_t mode;

@@ -0,0 +1,11 @@ 

+ --- a/plugins/reflink.c

+ +++ b/plugins/reflink.c

+ @@ -63,7 +63,7 @@ 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 *

+ +static void *

+  bsearch_r (const void *__key, const void *__base, size_t __nmemb, size_t __size,

+  	 __compar_d_fn_t __compar, void *__arg)

+  {

@@ -0,0 +1,35 @@ 

+ --- a/plugins/reflink.c

+ +++ b/plugins/reflink.c

+ @@ -277,13 +277,17 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa

+      fcr.dest_offset = 0;

+      if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) {

+  	rpm_ino_t inode = rpmfiFInode(fi);

+ +        char fullpath[PATH_MAX];

+ +

+ +        snprintf(fullpath, sizeof(fullpath), "%s/%s", rpmfiDN(fi), path);

+ +

+  	/* check for hard link entry in table. GetEntry overwrites hlix with

+  	 * the address of the first match.

+  	 */

+  	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hl_target,

+  				   NULL, NULL)) {

+  	    /* entry is in table, use hard link */

+ -	    if (link(hl_target[0], path) != 0) {

+ +	    if (link(hl_target[0], fullpath) != 0) {

+  		rpmlog(RPMLOG_ERR,

+  		       _("reflink: Unable to hard link %s -> %s due to %s\n"),

+  		       hl_target[0], path, strerror(errno));

+ @@ -296,11 +300,11 @@ 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, rstrdup(path));

+ +	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rstrdup(fullpath));

+  	}

+  	/* derived from wfd_open in fsm.c */

+  	mode_t old_umask = umask(0577);

+ -	dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);

+ +	dst = open(fullpath, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);

+  	umask(old_umask);

+  	if (dst == -1) {

+  	    rpmlog(RPMLOG_ERR,

file added
+279
@@ -0,0 +1,279 @@ 

+ From 517a37ae7beeb77e2b4870be11611f82c1200b3c Mon Sep 17 00:00:00 2001

+ From: Matthew Almond <malmond@fb.com>

+ Date: Thu, 11 Jun 2020 13:01:04 -0700

+ Subject: [PATCH 2/2] Measure plugin

+ 

+ ---

+  macros.in           |   1 +

+  plugins/Makefile.am |   4 +

+  plugins/measure.c   | 231 ++++++++++++++++++++++++++++++++++++++++++++

+  3 files changed, 236 insertions(+)

+  create mode 100644 plugins/measure.c

+ 

+ diff --git a/macros.in b/macros.in

+ index 3cc8a3555..c8a087959 100644

+ --- a/macros.in

+ +++ b/macros.in

+ @@ -1173,6 +1173,7 @@ package or when debugging this package.\

+  # Transaction plugin macros

+  %__plugindir		%{_libdir}/rpm-plugins

+  %__transaction_reflink		%{__plugindir}/reflink.so

+ +%__transaction_measure		%{__plugindir}/measure.so

+  %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so

+  %__transaction_selinux		%{__plugindir}/selinux.so

+  %__transaction_syslog		%{__plugindir}/syslog.so

+ diff --git a/plugins/Makefile.am b/plugins/Makefile.am

+ index 06393ce8d..daed6423c 100644

+ --- a/plugins/Makefile.am

+ +++ b/plugins/Makefile.am

+ @@ -29,6 +29,10 @@ systemd_inhibit_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/

+  plugins_LTLIBRARIES += systemd_inhibit.la

+  endif

+  

+ +measure_la_SOURCES = measure.c

+ +measure_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la

+ +plugins_LTLIBRARIES += measure.la

+ +

+  prioreset_la_SOURCES = prioreset.c

+  prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la

+  plugins_LTLIBRARIES += prioreset.la

+ diff --git a/plugins/measure.c b/plugins/measure.c

+ new file mode 100644

+ index 000000000..2cdfc885e

+ --- /dev/null

+ +++ b/plugins/measure.c

+ @@ -0,0 +1,231 @@

+ +#include "system.h"

+ +#include "time.h"

+ +

+ +#include <rpm/rpmlog.h>

+ +#include <rpm/rpmmacro.h>

+ +#include <rpm/rpmts.h>

+ +#include "lib/rpmlib.h"

+ +

+ +#include "lib/rpmplugin.h"

+ +

+ +struct measurestat {

+ +    /* We're counting psm not packages because packages often run psm_pre/post

+ +       more than once and we want to accumulate the time

+ +    */

+ +    unsigned int psm_count;

+ +    unsigned int scriptlet_count;

+ +    struct timespec plugin_start;

+ +    struct timespec psm_start;

+ +    struct timespec scriptlet_start;

+ +};

+ +

+ +static rpmRC push(const char *format, const char *value, const char *formatted)

+ +{

+ +    char *key = NULL;

+ +    rpmRC rc = RPMRC_FAIL;

+ +    if (formatted == NULL) {

+ +        /* yes we're okay with discarding const here */

+ +        key = (char *)format;

+ +    } else {

+ +        if (rasprintf(&key, format, formatted) == -1) {

+ +            rpmlog(

+ +                RPMLOG_ERR,

+ +                _("measure: Failed to allocate formatted key %s, %s\n"),

+ +                format,

+ +                formatted

+ +            );

+ +            goto exit;

+ +        }

+ +    }

+ +    if (rpmPushMacro(NULL, key, NULL, value, RMIL_GLOBAL)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to set %s\n"), key);

+ +        goto exit;

+ +    }

+ +    rc = RPMRC_OK;

+ +exit:

+ +    if (formatted != NULL) {

+ +        free(key);

+ +    }

+ +    return rc;

+ +}

+ +

+ +static rpmRC diff_ms(char **ms, struct timespec *start, struct timespec *end)

+ +{

+ +    if (rasprintf(ms, "%ld", (

+ +        (end->tv_sec - start->tv_sec) * 1000 +

+ +        (end->tv_nsec - start->tv_nsec) / 1000000

+ +    )) == -1) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted ms\n"));

+ +        return RPMRC_FAIL;

+ +    }

+ +    return RPMRC_OK;

+ +}

+ +

+ +static rpmRC measure_init(rpmPlugin plugin, rpmts ts)

+ +{

+ +    rpmRC rc = RPMRC_FAIL;

+ +    struct measurestat *state = rcalloc(1, sizeof(*state));

+ +    if (clock_gettime(CLOCK_MONOTONIC, &state->plugin_start)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to get plugin_start time\n"));

+ +        goto exit;

+ +    }

+ +    state->psm_count = 0;

+ +    state->scriptlet_count = 0;

+ +    rpmPluginSetData(plugin, state);

+ +    rc = RPMRC_OK;

+ +exit:

+ +    return rc;

+ +}

+ +

+ +static void measure_cleanup(rpmPlugin plugin)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    free(state);

+ +}

+ +

+ +static rpmRC measure_tsm_post(rpmPlugin plugin, rpmts ts, int res)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    char *psm_count = NULL, *scriptlet_count = NULL;

+ +    rpmRC rc = RPMRC_FAIL;

+ +

+ +    if (rasprintf(&psm_count, "%d", state->psm_count) == -1) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted psm_count\n"));

+ +        goto exit;

+ +    }

+ +    if (rasprintf(&scriptlet_count, "%d", state->scriptlet_count) == -1) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted scriptlet_count\n"));

+ +        goto exit;

+ +    }

+ +    if (push("_measure_plugin_psm_count", psm_count, NULL) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("_measure_plugin_scriptlet_count", scriptlet_count, NULL) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    rc = RPMRC_OK;

+ +exit:

+ +    free(psm_count);

+ +    free(scriptlet_count);

+ +    return rc;

+ +}

+ +

+ +static rpmRC measure_psm_pre(rpmPlugin plugin, rpmte te)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    rpmRC rc = RPMRC_FAIL;

+ +

+ +    if (clock_gettime(CLOCK_MONOTONIC, &state->psm_start)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to get psm_start time\n"));

+ +        goto exit;

+ +    }

+ +    rc = RPMRC_OK;

+ +exit:

+ +    return rc;

+ +}

+ +

+ +static rpmRC measure_psm_post(rpmPlugin plugin, rpmte te, int res)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    struct timespec end;

+ +    char *offset = NULL, *duration = NULL, *prefix = NULL;

+ +    Header h = rpmteHeader(te);

+ +    rpmRC rc = RPMRC_FAIL;

+ +

+ +    if (clock_gettime(CLOCK_MONOTONIC, &end)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to get psm end time\n"));

+ +        goto exit;

+ +    }

+ +    if (rasprintf(&prefix, "_measure_plugin_package_%u", state->psm_count) == -1) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate prefix\n"));

+ +        goto exit;

+ +    }

+ +    if (diff_ms(&offset, &state->plugin_start, &state->psm_start) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (diff_ms(&duration, &state->psm_start, &end) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_nevra", rpmteNEVRA(te), prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_compressor", headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR), prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_offset", offset, prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_ms", duration, prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    state->psm_count += 1;

+ +    rc = RPMRC_OK;

+ +exit:

+ +    headerFree(h);

+ +    free(prefix);

+ +    free(duration);

+ +    free(offset);

+ +    return rc;

+ +}

+ +

+ +static rpmRC measure_scriptlet_pre(rpmPlugin plugin,

+ +					const char *s_name, int type)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    if (clock_gettime(CLOCK_MONOTONIC, &state->scriptlet_start)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to get scriptlet_start time\n"));

+ +        return RPMRC_FAIL;

+ +    }

+ +    return RPMRC_OK;

+ +}

+ +

+ +static rpmRC measure_scriptlet_post(rpmPlugin plugin,

+ +					const char *s_name, int type, int res)

+ +{

+ +    struct measurestat *state = rpmPluginGetData(plugin);

+ +    struct timespec end;

+ +    char *offset = NULL, *duration = NULL, *prefix = NULL;

+ +    rpmRC rc = RPMRC_FAIL;

+ +

+ +    if (clock_gettime(CLOCK_MONOTONIC, &end)) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to get end time\n"));

+ +        goto exit;

+ +    }

+ +

+ +    if (rasprintf(&prefix, "_measure_plugin_scriptlet_%d", state->scriptlet_count) == -1) {

+ +        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted prefix\n"));

+ +        goto exit;

+ +    }

+ +    if (diff_ms(&offset, &state->plugin_start, &state->scriptlet_start) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (diff_ms(&duration, &state->scriptlet_start, &end) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_name", s_name, prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_offset", offset, prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    if (push("%s_ms", duration, prefix) != RPMRC_OK) {

+ +        goto exit;

+ +    }

+ +    state->scriptlet_count += 1;

+ +    rc = RPMRC_OK;

+ +exit:

+ +    free(prefix);

+ +    free(duration);

+ +    free(offset);

+ +    return rc;

+ +}

+ +

+ +struct rpmPluginHooks_s measure_hooks = {

+ +    .init = measure_init,

+ +    .cleanup = measure_cleanup,

+ +    .tsm_post = measure_tsm_post,

+ +    .psm_pre = measure_psm_pre,

+ +    .psm_post = measure_psm_post,

+ +    .scriptlet_pre = measure_scriptlet_pre,

+ +    .scriptlet_post = measure_scriptlet_post,

+ +};

+ -- 

+ 2.24.1

+ 

@@ -0,0 +1,193 @@ 

+ From 8c0d382a8c62ec6f4f5d69920b76cd4f6bb24e9b Mon Sep 17 00:00:00 2001

+ From: Aleksandr Kazakov <alexkazakov@meta.com>

+ Date: Mon, 6 Feb 2023 19:15:45 +0000

+ Subject: [PATCH] Backport multi-threaded zstd to 4.16.x

+ 

+ Signed-off-by: Aleksandr Kazakov <alexkazakov@meta.com>

+ ---

+  configure.ac  |  2 +-

+  macros.in     | 15 ++++-----

+  rpmio/rpmio.c | 84 ++++++++++++++++++++++++++++++++++-----------------

+  3 files changed, 66 insertions(+), 35 deletions(-)

+ 

+ diff --git a/configure.ac b/configure.ac

+ index 71e9009..28979af 100644

+ --- a/configure.ac

+ +++ b/configure.ac

+ @@ -215,7 +215,7 @@ AC_ARG_ENABLE([zstd],

+                [enable_zstd=auto])

+  

+  AS_IF([test "x$enable_zstd" != "xno"], [

+ -  PKG_CHECK_MODULES([ZSTD], [libzstd], [have_zstd=yes], [have_zstd=no])

+ +  PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.3.8], [have_zstd=yes], [have_zstd=no])

+    AS_IF([test "$enable_zstd" = "yes"], [

+      if test "$have_zstd" = "no"; then

+        AC_MSG_ERROR([--enable-zstd specified, but not available])

+ diff --git a/macros.in b/macros.in

+ index 99eb034..fa85fdb 100644

+ --- a/macros.in

+ +++ b/macros.in

+ @@ -365,13 +365,14 @@ package or when debugging this package.\

+  #%packager

+  

+  #	Compression type and level for source/binary package payloads.

+ -#		"w9.gzdio"	gzip level 9 (default).

+ -#		"w9.bzdio"	bzip2 level 9.

+ -#		"w6.xzdio"	xz level 6, xz's default.

+ -#		"w7T16.xzdio"	xz level 7 using 16 thread (xz only)

+ -#		"w6.lzdio"	lzma-alone level 6, lzma's default

+ -#		"w3.zstdio"	zstd level 3, zstd's default

+ -#		"w.ufdio"	uncompressed

+ +#		"w9.gzdio"		gzip level 9 (default).

+ +#		"w9.bzdio"		bzip2 level 9.

+ +#		"w6.xzdio"		xz level 6, xz's default.

+ +#		"w7T16.xzdio"		xz level 7 using 16 threads

+ +#		"w19T8.zstdio"		zstd level 19 using 8 threads

+ +#		"w6.lzdio"		lzma-alone level 6, lzma's default

+ +#		"w3.zstdio"		zstd level 3, zstd's default

+ +#		"w.ufdio"		uncompressed

+  #

+  #%_source_payload	w9.gzdio

+  #%_binary_payload	w9.gzdio

+ diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c

+ index 52be6a3..b5c76a8 100644

+ --- a/rpmio/rpmio.c

+ +++ b/rpmio/rpmio.c

+ @@ -1073,6 +1073,7 @@ static rpmzstd rpmzstdNew(int fdno, const char *fmode)

+      char *t = stdio;

+      char *te = t + sizeof(stdio) - 2;

+      int c;

+ +    int threads = 0;

+  

+      switch ((c = *s++)) {

+      case 'a':

+ @@ -1101,7 +1102,14 @@ static rpmzstd rpmzstdNew(int fdno, const char *fmode)

+  	    flags &= ~O_ACCMODE;

+  	    flags |= O_RDWR;

+  	    continue;

+ -	    break;

+ +	case 'T':

+ +	    if (*s >= '0' && *s <= '9') {

+ +		threads = strtol(s, (char **)&s, 10);

+ +		/* T0 means automatic detection */

+ +		if (threads == 0)

+ +		    threads = sysconf(_SC_NPROCESSORS_ONLN);

+ +	    }

+ +	    continue;

+  	default:

+  	    if (c >= (int)'0' && c <= (int)'9') {

+  		level = strtol(s-1, (char **)&s, 10);

+ @@ -1135,11 +1143,17 @@ static rpmzstd rpmzstdNew(int fdno, const char *fmode)

+  	}

+  	nb = ZSTD_DStreamInSize();

+      } else {					/* compressing */

+ -	if ((_stream = (void *) ZSTD_createCStream()) == NULL

+ -	 || ZSTD_isError(ZSTD_initCStream(_stream, level))) {

+ +	if ((_stream = (void *) ZSTD_createCCtx()) == NULL

+ +	 || ZSTD_isError(ZSTD_CCtx_setParameter(_stream, ZSTD_c_compressionLevel, level))) {

+  	    goto err;

+  	}

+ -	nb = ZSTD_CStreamOutSize();

+ +

+ +        rpmlog(RPMLOG_DEBUG, "using %i threads in zstd compression\n", threads);

+ +        if (threads > 0) {

+ +            if (ZSTD_isError (ZSTD_CCtx_setParameter(_stream, ZSTD_c_nbWorkers, threads)))

+ +            rpmlog(RPMLOG_WARNING, "zstd library does not support multi-threading\n");

+ +	}

+ + 	nb = ZSTD_CStreamOutSize();

+      }

+  

+      rpmzstd zstd = (rpmzstd) xcalloc(1, sizeof(*zstd));

+ @@ -1158,7 +1172,7 @@ err:

+      if ((flags & O_ACCMODE) == O_RDONLY)

+  	ZSTD_freeDStream(_stream);

+      else

+ -	ZSTD_freeCStream(_stream);

+ +	ZSTD_freeCCtx(_stream);

+      return NULL;

+  }

+  

+ @@ -1184,16 +1198,24 @@ assert(zstd);

+  	rc = 0;

+      } else {					/* compressing */

+  	/* close frame */

+ -	zstd->zob.dst  = zstd->b;

+ -	zstd->zob.size = zstd->nb;

+ -	zstd->zob.pos  = 0;

+ -	int xx = ZSTD_flushStream(zstd->_stream, &zstd->zob);

+ -	if (ZSTD_isError(xx))

+ -	    fps->errcookie = ZSTD_getErrorName(xx);

+ -	else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp))

+ -	    fps->errcookie = "zstdFlush fwrite failed.";

+ -	else

+ -	    rc = 0;

+ +	int xx;

+ +	do {

+ +	  ZSTD_inBuffer zib = { NULL, 0, 0 };

+ +	  zstd->zob.dst  = zstd->b;

+ +	  zstd->zob.size = zstd->nb;

+ +	  zstd->zob.pos  = 0;

+ +	  xx = ZSTD_compressStream2(zstd->_stream, &zstd->zob, &zib, ZSTD_e_flush);

+ +	  if (ZSTD_isError(xx)) {

+ +	      fps->errcookie = ZSTD_getErrorName(xx);

+ +	      break;

+ +	  }

+ +	  else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) {

+ +	      fps->errcookie = "zstdClose fwrite failed.";

+ +	      break;

+ +	  }

+ +	  else

+ +	      rc = 0;

+ +	} while (xx != 0);

+      }

+      return rc;

+  }

+ @@ -1238,7 +1260,7 @@ assert(zstd);

+  	zstd->zob.pos  = 0;

+  

+  	/* Compress next chunk. */

+ -        int xx = ZSTD_compressStream(zstd->_stream, &zstd->zob, &zib);

+ +        int xx = ZSTD_compressStream2(zstd->_stream, &zstd->zob, &zib, ZSTD_e_continue);

+          if (ZSTD_isError(xx)) {

+  	    fps->errcookie = ZSTD_getErrorName(xx);

+  	    return -1;

+ @@ -1267,17 +1289,25 @@ assert(zstd);

+  	ZSTD_freeDStream(zstd->_stream);

+      } else {					/* compressing */

+  	/* close frame */

+ -	zstd->zob.dst  = zstd->b;

+ -	zstd->zob.size = zstd->nb;

+ -	zstd->zob.pos  = 0;

+ -	int xx = ZSTD_endStream(zstd->_stream, &zstd->zob);

+ -	if (ZSTD_isError(xx))

+ -	    fps->errcookie = ZSTD_getErrorName(xx);

+ -	else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp))

+ -	    fps->errcookie = "zstdClose fwrite failed.";

+ -	else

+ -	    rc = 0;

+ -	ZSTD_freeCStream(zstd->_stream);

+ +	int xx;

+ +	do {

+ +	  ZSTD_inBuffer zib = { NULL, 0, 0 };

+ +	  zstd->zob.dst  = zstd->b;

+ +	  zstd->zob.size = zstd->nb;

+ +	  zstd->zob.pos  = 0;

+ +	  xx = ZSTD_compressStream2(zstd->_stream, &zstd->zob, &zib, ZSTD_e_end);

+ +	  if (ZSTD_isError(xx)) {

+ +	      fps->errcookie = ZSTD_getErrorName(xx);

+ +	      break;

+ +	  }

+ +	  else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) {

+ +	      fps->errcookie = "zstdClose fwrite failed.";

+ +	      break;

+ +	  }

+ +	  else

+ +	      rc = 0;

+ +	} while (xx != 0);

+ +	ZSTD_freeCCtx(zstd->_stream);

+      }

+  

+      if (zstd->fp && fileno(zstd->fp) > 2)

+ -- 

+ 2.38.1

+ 

@@ -0,0 +1,12 @@ 

+ diff '--color=auto' -rup rpm-4.16.1.3-orig/macros.in rpm-4.16.1.3/macros.in

+ --- rpm-4.16.1.3-orig/macros.in	2020-09-30 03:48:01.216567738 -0400

+ +++ rpm-4.16.1.3/macros.in	2022-03-03 00:20:16.830231123 -0500

+ @@ -153,7 +153,7 @@

+  %_bzip2bin		%{__bzip2}

+  

+  #	The location of the rpm database file(s).

+ -%_dbpath		%{_var}/lib/rpm

+ +%_dbpath		%{_usr}/lib/sysimage/rpm

+  

+  #	The location of the rpm database file(s) after "rpm --rebuilddb".

+  %_dbpath_rebuild	%{_dbpath}

file modified
+192 -14
@@ -32,7 +32,7 @@ 

  

  %global rpmver 4.16.1.3

  #global snapver rc1

- %global rel 30

+ %global rel 34.1

  %global sover 9

  

  %global srcver %{rpmver}%{?snapver:-%{snapver}}
@@ -57,6 +57,11 @@ 

  

  Source10: rpmdb-rebuild.service

  

+ Source20: rpmdb-migrate.service

+ Source21: rpmdb_migrate

+ 

+ # Set rpmdb path to /usr/lib/sysimage/rpm

+ Patch0: rpm-4.16.x-rpm_dbpath.patch

  # Disable autoconf config.site processing (#962837)

  Patch1: rpm-4.15.x-siteconfig.patch

  # In current Fedora, man-pages pkg owns all the localized man directories
@@ -110,6 +115,10 @@ 

  Patch142: 0001-Expose-and-document-rpmdb-verifydb-operation.patch

  Patch143: 0001-Don-t-segfault-on-missing-priority-tag.patch

  Patch144: 0001-Use-unsigned-integers-for-buildtime-too-for-Y2K38-sa.patch

+ Patch145: 0001-Fix-potential-use-of-uninitialized-pipe-array.patch

+ Patch146: 0001-Fix-potential-use-of-uninitialized-pgp-struct.patch

+ Patch147: 0001-Add-SourceLicense-tag-to-spec-syntax.patch

+ Patch148: 0001-Talk-about-rpmsign-in-the-rpmsign-man-page.patch

  

  # These are not yet upstream

  Patch906: rpm-4.7.1-geode-i686.patch
@@ -131,6 +140,55 @@ 

  Patch1000: rpm-4.16.1.3-hashtab-use-after-free-fix.patch

  Patch1001: rpm-4.16.1.3-find_debuginfo_vendor_opts.patch

  Patch1002: 0001-Macroize-find-debuginfo-script-location.patch

+ Patch1003: 0001-Fix-root-relocation-regression.patch

+ Patch1004: 0001-Skip-to-hashed-subpacket-data-directly.patch

+ 

+ %if %{with zstd}

+ Patch2000: rpm-4.16.1.3-backport-multithreaded-zstd.patch

+ %endif

+ 

+ # 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

+ Patch9931: 0031-rpmcow-denylist.patch

+ Patch9932: 0032-rpmcow-workaround.patch

+ Patch9933: 0033-rpmcow-fix-stack-overflow-in-rpm2extents.patch

+ Patch9934: 0034-rpmcow-fix-issue-for-transaction-with-transcoded-and-untranscoded-packages.patch

+ Patch9935: 0035-rpmcow-fix-segfault-in-rpm2extents.patch

+ Patch9936: 0036-bsearchr_static.patch

+ Patch9937: 0037-plugin_path.patch

+ Provides: rpm(pr1470)

+ Provides: rpm(pr1470_1)

+ 

+ Patch9999: measure.patch

  

  # Partially GPL/LGPL dual-licensed and some bits with BSD

  # SourceLicense: (GPLv2+ and LGPLv2+ with exceptions) and BSD
@@ -193,6 +251,15 @@ 

  BuildRequires: ima-evm-utils-devel >= 1.0

  %endif

  

+ # For the rpmdb migration scriptlet (#2055033)

+ Requires(pre): coreutils

+ Requires(pre): findutils

+ Requires(pre): sed

+ 

+ # We don't need a custom policy anymore

+ Provides:      %{name}-selinux = %{name}-%{version}

+ Obsoletes:     %{name}-selinux < 4.16.1.3-22.3

+ 

  %description

  The RPM Package Manager (RPM) is a powerful command line driven

  package management system capable of installing, uninstalling,
@@ -252,7 +319,7 @@ 

  Requires: findutils sed grep gawk diffutils file patch >= 2.5

  Requires: tar unzip gzip bzip2 cpio xz

  %if %{with zstd}

- Requires: zstd

+ Requires: zstd >= 1.3.8

  %endif

  Requires: pkgconfig >= 1:0.24

  Requires: /usr/bin/gdb-add-index
@@ -350,6 +417,13 @@ 

  %description plugin-fapolicyd

  %{summary}.

  

+ %package plugin-reflink

+ Summary: Rpm plugin for reflink functionality

+ Requires: rpm-libs%{_isa} = %{version}-%{release}

+ 

+ %description plugin-reflink

+ %{summary}.

+ 

  %package plugin-prioreset

  Summary: Rpm plugin for resetting scriptlet priorities for SysV init

  Requires: rpm-libs%{_isa} = %{version}-%{release}
@@ -367,6 +441,14 @@ 

  %description plugin-audit

  %{summary}.

  

+ %package plugin-measure

+ Summary: Rpm plugin for measure

+ Group: System Environment/Base

+ Requires: rpm-libs%{_isa} = %{version}-%{release}

+ 

+ %description plugin-measure

+ Adds measure support

+ 

  # with plugins

  %endif

  
@@ -450,6 +532,10 @@ 

  

  mkdir -p $RPM_BUILD_ROOT%{_unitdir}

  install -m 644 %{SOURCE10} $RPM_BUILD_ROOT/%{_unitdir}

+ install -m 644 %{SOURCE20} $RPM_BUILD_ROOT/%{_unitdir}

+ 

+ mkdir -p $RPM_BUILD_ROOT%{rpmhome}

+ install -m 755 %{SOURCE21} $RPM_BUILD_ROOT/%{rpmhome}

  

  # Save list of packages through cron

  mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/cron.daily
@@ -460,17 +546,17 @@ 

  

  %if %{with bdb}

  mkdir -p ${RPM_BUILD_ROOT}%{_tmpfilesdir}

- echo "r /var/lib/rpm/__db.*" > ${RPM_BUILD_ROOT}%{_tmpfilesdir}/rpm.conf

+ echo "r /usr/lib/sysimage/rpm/__db.*" > ${RPM_BUILD_ROOT}%{_tmpfilesdir}/rpm.conf

  %endif

  

  mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/rpm

  mkdir -p $RPM_BUILD_ROOT%{rpmhome}/macros.d

- mkdir -p $RPM_BUILD_ROOT/var/lib/rpm

+ mkdir -p $RPM_BUILD_ROOT/usr/lib/sysimage/rpm

  

  # init an empty database for %ghost'ing for all supported backends

  for be in %{?with_ndb:ndb} %{?with_sqlite:sqlite} %{?with_bdb:bdb}; do

      ./rpmdb --define "_db_backend ${be}" --dbpath=${PWD}/${be} --initdb

-     cp -va ${be}/. $RPM_BUILD_ROOT/var/lib/rpm/

+     cp -va ${be}/. $RPM_BUILD_ROOT/usr/lib/sysimage/rpm/

  done

  

  %find_lang rpm
@@ -491,16 +577,25 @@ 

  make clean

  %endif

  

- # Handle rpmdb rebuild service on erasure of old to avoid ordering issues

- # https://pagure.io/fesco/issue/2382

- %triggerun -- rpm < 4.15.90-0.git14971.10

+ %pre

+ # Symlink all rpmdb files to the new location if we're still using /var/lib/rpm

+ if [ -d /var/lib/rpm ]; then

+     mkdir -p /usr/lib/sysimage/rpm

+     rpmdb_files=$(find /var/lib/rpm -maxdepth 1 -type f | sed 's|^/var/lib/rpm/||g' | sort)

+     for rpmdb_file in ${rpmdb_files[@]}; do

+         ln -sfr /var/lib/rpm/${rpmdb_file} /usr/lib/sysimage/rpm/${rpmdb_file}

+     done

+ fi

+ 

+ %triggerun -- rpm < 4.16.1.3-11.3

+ # Handle rpmdb migrate service on erasure of old to avoid ordering issues

  if [ -x /usr/bin/systemctl ]; then

-     systemctl --no-reload preset rpmdb-rebuild ||:

+     systemctl --no-reload preset rpmdb-migrate ||:

  fi

  

  %posttrans

- if [ -f /var/lib/rpm/Packages ]; then

-     touch /var/lib/rpm/.rebuilddb

+ if [ -d /var/lib/rpm ]; then

+     touch /var/lib/rpm/.migratedb

  fi

  

  %files -f rpm.lang
@@ -512,12 +607,13 @@ 

  %endif

  

  %{_unitdir}/rpmdb-rebuild.service

+ %{_unitdir}/rpmdb-migrate.service

  

  %dir %{_sysconfdir}/rpm

  

- %attr(0755, root, root) %dir /var/lib/rpm

- %attr(0644, root, root) %ghost %config(missingok,noreplace) /var/lib/rpm/*

- %attr(0644, root, root) %ghost /var/lib/rpm/.*.lock

+ %attr(0755, root, root) %dir /usr/lib/sysimage/rpm

+ %attr(0644, root, root) %ghost %config(missingok,noreplace) /usr/lib/sysimage/rpm/*

+ %attr(0644, root, root) %ghost /usr/lib/sysimage/rpm/.*.lock

  

  %{_bindir}/rpm

  %if %{with libarchive}
@@ -593,6 +689,11 @@ 

  %{_libdir}/rpm-plugins/fapolicyd.so

  %{_mandir}/man8/rpm-plugin-fapolicyd.8*

  

+ %files plugin-reflink

+ %{_bindir}/rpm2extents

+ %{_bindir}/rpm2extents_dump

+ %{_libdir}/rpm-plugins/reflink.so

+ 

  %files plugin-prioreset

  %{_libdir}/rpm-plugins/prioreset.so

  %{_mandir}/man8/rpm-plugin-prioreset.8*
@@ -600,6 +701,9 @@ 

  %files plugin-audit

  %{_libdir}/rpm-plugins/audit.so

  %{_mandir}/man8/rpm-plugin-audit.8*

+ 

+ %files plugin-measure

+ %{_libdir}/rpm-plugins/measure.so

  # with plugins

  %endif

  
@@ -659,6 +763,33 @@ 

  %doc doc/librpm/html/*

  

  %changelog

+ * Tue Aug 20 2024 Matteo Croce <teknoraver@meta.com> - 4.16.1.3-34.1

+ - Backport fix from upstream

+ 

+ * Tue Aug 13 2024 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-34

+ - Fix discarded const qualifier in previous patch (RHEL-22607)

+ 

+ * Wed Aug 07 2024 Matteo Croce <teknoraver@meta.com> - 4.16.1.3-33.1

+ - Port COW over latest Red Hat package

+ - Drop the fs-verity patches

+ - Patch 1534.patch contained a backport of upstream PR #1534:

+   https://github.com/rpm-software-management/rpm/pull/1534

+   Since the new file state machine was backported in 4.16.1.3-26, drop it

+ 

+ * Mon Aug 05 2024 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-33

+ - Fix root relocation regression (RHEL-28967)

+ - Don't confuse OpenScanHub with false array overrun (RHEL-22607)

+ 

+ * Fri Jul 12 2024 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-32

+ - Revert incorrect fix for false array overrun (RHEL-22607)

+ 

+ * Fri Jul 12 2024 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-31

+ - Fix potential use of uninitialized pipe array (RHEL-22604)

+ - Fix potential use of uninitialized pgp struct (RHEL-22605)

+ - Don't confuse OpenScanHub with false array overrun (RHEL-22607)

+ - Add SourceLicense tag to spec syntax (RHEL-28798)

+ - Talk about rpmsign in the rpmsign(8) man page (RHEL-40895)

+ 

  * Mon Jun 03 2024 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-30

  - Don't segfault on missing priority tag (RHEL-35249)

  - Use unsigned integers for buildtime too for Y2K38 safety (RHEL-22602)
@@ -681,11 +812,23 @@ 

  - Backport file handling code from rpm-4.19 to fix CVE-2021-35937,

    CVE-2021-35938 and CVE-2021-35939

  

+ * Wed Jul 3 2024 Michal Grzedzicki <mge@meta.com> - 4.16.1.3-25.2

+ - Fix segfault in rpm2extents

+ 

+ * Thu Aug 17 2023 Richard Phibel <richardphibel@meta.com> - 4.16.1.3-25.1

+ - Merge upstream changes for Hyperscale

+ 

+ * Fri Aug 11 2023 Richard Phibel <richardphibel@meta.com> - 4.16.1.3-22.5

+ - Fix issue for transaction with transcoded and non-transcoded packages

+ 

  * Fri Jun 30 2023 Florian Festi <ffesti@redhat.com> - 4.16.1.3-25

  - Followup on #2166383

  - Add compat scripts calling external find-debug, sepdebugcrcfix and debugedit

  - Add %%__find_debuginfo macro

  

+ * Mon May 22 2023 Richard Phibel <richardphibel@meta.com> - 4.16.1.3-22.4

+ - Fix stack overflow in rpm2extents and various memory leaks

+ 

  * Thu May 04 2023 Florian Festi <ffesti@redhat.com> - 4.16.1.3-24

  - Use external find-debug and debugedit (#2166383)

  
@@ -693,9 +836,24 @@ 

  - Don't error out on IMA signatures on files not supporting them

    (#2157835, #2157836)

  

+ * Sat Feb 11 2023 Davide Cavalca <dcavalca@centosproject.org> - 4.16.1.3-22.3

+ - Drop our selinux policy as it's been subsumed by the main one

+ 

+ * Mon Feb 06 2023 Aleksandr Kazakov <alexkazakov@meta.com> - 4.16.1.3-22.2

+ - Backport multi-threaded zstd for Hyperscale

+ 

+ * Tue Dec 20 2022 Davide Cavalca <dcavalca@centosproject.org> - 4.16.1.3-22.1

+ - Merge upstream changes for Hyperscale

+ 

  * Mon Dec 19 2022 Florian Festi <ffesti@redhat.com> - 4.16.1.3-22

  - Fix option handling in rpm2archive for #2150804

  

+ * Wed Dec 07 2022 Davide Cavalca <dcavalca@centosproject.org> - 4.16.1.3-21.1

+ - Merge upstream changes for Hyperscale

+ 

+ * Wed Nov 30 2022 Richard Phibel <richardphibel@meta.com> - 4.16.1.3-19.2

+ - Add deny list support and workaround for RPM CoW

+ 

  * Fri Nov 18 2022 Yaakov Selkowitz <yselkowi@redhat.com> - 4.16.1.3-21

  - Support long language names for QT (#2144005)

  
@@ -706,12 +864,18 @@ 

  - Handle SELinux log messages (#2123719)

  - Add --nocompression to rpm2archive (#2150804)

  

+ * Sat Oct 29 2022 Richard Phibel <richardphibel@fb.com> - 4.16.1.3-19.1

+ - Merge upstream changes for Hyperscale

+ 

  * Fri Oct 21 2022 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-19

  - Bump release for rebuild

  

  * Fri Sep 23 2022 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-18

  - Make write() nonblocking in fapolicyd plugin (#2111251)

  

+ * Thu Aug 18 2022 Manu Bretelle <chantra@fb.com> - 4.16.1.3-17.1

+ - Rebuild for hyperscale

+ 

  * Wed Aug 03 2022 Florian Festi <ffesti@redhat.com> - 4.16.1.3-17

  - Make rpm2cpio.sh more robust (#1983015)

  
@@ -724,6 +888,20 @@ 

  * Tue Apr 05 2022 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-12

  - Fix minor ABI regression in rpmcli.h (#2037352)

  

+ * Sun Apr 03 2022 Neal Gompa <ngompa@centosproject.org> - 4.16.1.3-11.3

+ - Migrate rpmdb to /usr/lib/sysimage/rpm

+   https://fedoraproject.org/wiki/Changes/RelocateRPMToUsr

+ 

+ * Tue Mar 29 2022 Manu Bretelle <chantra@fb.com> - 4.16.1.3-11.2

+ - Make `rpm -i` work without `--nodigest`

+ - Remove need of executable stack for reflink plugin

+ - Split RPM CoW diffs in individual patches

+ 

+ * Fri Feb 25 2022 Manu Bretelle <chantra@fb.com> - 4.16.1.3-11.1

+ - Added fsverity backport

+ - Added measure plugin support

+ - Add support for RPM CoW

+ 

  * Mon Feb 14 2022 Michal Domonkos <mdomonko@redhat.com> - 4.16.1.3-11

  - Fix IMA signature lengths assumed constant, take III (#2018937)

  - Fix regression reading rpm v3 and other rare packages (#2037186)

@@ -0,0 +1,18 @@ 

+ [Unit]

+ Description=RPM database migration to /usr

+ ConditionPathExists=/var/lib/rpm/.migratedb

+ 

+ # This should run before any daemons that may open the rpmdb

+ DefaultDependencies=no

+ After=sysinit.target

+ Before=basic.target shutdown.target

+ Conflicts=shutdown.target

+ # In case /var is remote-mounted

+ RequiresMountsFor=/var

+ 

+ [Service]

+ Type=oneshot

+ ExecStart=/usr/lib/rpm/rpmdb_migrate

+ 

+ [Install]

+ WantedBy=basic.target

file modified
+4 -4
@@ -1,19 +1,19 @@ 

  [Unit]

  Description=RPM database rebuild

- ConditionPathExists=/var/lib/rpm/.rebuilddb

+ ConditionPathExists=/usr/lib/sysimage/rpm/.rebuilddb

  

  # This should run before any daemons that may open the rpmdb

  DefaultDependencies=no

  After=sysinit.target

  Before=basic.target shutdown.target

  Conflicts=shutdown.target

- # In case /var is remote-mounted

- RequiresMountsFor=/var

+ # In case /usr is remote-mounted

+ RequiresMountsFor=/usr

  

  [Service]

  Type=oneshot

  ExecStart=/usr/bin/rpmdb --rebuilddb

- ExecStartPost=rm -f /var/lib/rpm/.rebuilddb

+ ExecStartPost=rm -f /usr/lib/sysimage/rpm/.rebuilddb

  

  [Install]

  WantedBy=basic.target

file added
+40
@@ -0,0 +1,40 @@ 

+ #!/bin/bash

+ # Script to migrate rpmdb from /var/lib/rpm to new rpmdb path in /usr

+ 

+ # Copyright (C) 2022 Neal Gompa <ngompa@fedoraproject.org>.

+ #

+ # Fedora-License-Identifier: GPLv2+

+ # SPDX-2.0-License-Identifier: GPL-2.0+

+ # SPDX-3.0-License-Identifier: GPL-2.0-or-later

+ #

+ # This program is free software.

+ # For more information on the license, see COPYING or

+ # <https://www.gnu.org/licenses/gpl-2.0.en.html>.

+ # For more information on free software, see

+ # <https://www.gnu.org/philosophy/free-sw.en.html>.

+ 

+ 

+ set -euo pipefail

+ 

+ # Script to migrate the rpmdb to /usr

+ rpmdb_path="$(rpm --eval '%_dbpath')"

+ rpmdb_path_old="/var/lib/rpm"

+ rpmdb_path_new="${rpmdb_path}"

+ 

+ 

+ if [ "${rpmdb_path}" = "${rpmdb_path_old}" ]; then

+ 	echo "The rpmdb path is still in /var, exiting!"

+ 	exit 0

+ fi

+ 

+ if [ -L "${rpmdb_path_old}" ]; then

+ 	echo "The rpmdb has already been migrated, exiting!"

+ 	rm -v "${rpmdb_path_old}/.migratedb"

+ 	exit 0

+ fi

+ 

+ rpm --verbose --rebuilddb

+ 

+ rm -rfv ${rpmdb_path_old}

+ 

+ ln -srv ${rpmdb_path_new} ${rpmdb_path_old}

Rebase CoW over latest CentOS package.
Drop fs-verity code.
Drop 1534.patch since contains a now unneeded backport of upstream PR #1534

This lgtm but I'd like to get more eyes on it. Note that it's a merge commit, you'll want to look at https://git.centos.org/fork/teknoraver/rpms/rpm/commits/c9s-sig-hyperscale for the full history as it doesn't show up properly in Pagure.

8 new commits added

  • Merge remote-tracking branch 'rh/c9s' into c9s-sig-hyperscale
  • Don't confuse OpenScanHub with false array overrun, take II
  • Fix root relocation regression
  • Revert "Don't confuse OpenScanHub with false array overrun"
  • Talk about rpmsign in the rpmsign(8) man page
  • Add SourceLicense tag to spec syntax
  • Don't confuse OpenScanHub with false array overrun
  • Fix OpenScanHub findings
a month ago

8 new commits added

  • Merge remote-tracking branch 'rh/c9s' into c9s-sig-hyperscale
  • Don't confuse OpenScanHub with false array overrun, take II
  • Fix root relocation regression
  • Revert "Don't confuse OpenScanHub with false array overrun"
  • Talk about rpmsign in the rpmsign(8) man page
  • Add SourceLicense tag to spec syntax
  • Don't confuse OpenScanHub with false array overrun
  • Fix OpenScanHub findings
a month ago

8 new commits added

  • Merge remote-tracking branch 'rh/c9s' into c9s-sig-hyperscale
  • Don't confuse OpenScanHub with false array overrun, take II
  • Fix root relocation regression
  • Revert "Don't confuse OpenScanHub with false array overrun"
  • Talk about rpmsign in the rpmsign(8) man page
  • Add SourceLicense tag to spec syntax
  • Don't confuse OpenScanHub with false array overrun
  • Fix OpenScanHub findings
a month ago

I've gone through all of it, I think? At this point, I see two issues:

  • All the section tags in the patch messages somehow got dropped. In my experience, this is caused by the usage of git am without --keep-non-patch.
  • rpmdb-rebuild.service has been reverted to look in /var/lib/rpm instead of /usr/lib/sysimage/rpm somehow.

The first may or may not be important, but the second one is important.

FYI, the easiest way to see the real diff is to look at the merge commit diff, since that seems to generate a reliable diff across the tree.

8 new commits added

  • Merge remote-tracking branch 'rh/c9s' into c9s-sig-hyperscale
  • Don't confuse OpenScanHub with false array overrun, take II
  • Fix root relocation regression
  • Revert "Don't confuse OpenScanHub with false array overrun"
  • Talk about rpmsign in the rpmsign(8) man page
  • Add SourceLicense tag to spec syntax
  • Don't confuse OpenScanHub with false array overrun
  • Fix OpenScanHub findings
a month ago

I've restored the rpmdb-rebuild.service and refreshed the patches to keep the merged commit smaller.
If you're referring to patches 36 and 37, that are just fixups to adapt the cow code to the 4.19 one.

1 new commit added

  • Fix discarded const qualifier in previous patch
a month ago

Pull-Request has been merged by dcavalca

a month ago
Metadata
Changes Summary 50
+124
file added
0001-Add-SourceLicense-tag-to-spec-syntax.patch
+12
file added
0001-Fix-potential-use-of-uninitialized-pgp-struct.patch
+32
file added
0001-Fix-potential-use-of-uninitialized-pipe-array.patch
+91
file added
0001-Fix-root-relocation-regression.patch
+1377
file added
0001-RPM-with-Copy-on-Write.patch
+51
file added
0001-Skip-to-hashed-subpacket-data-directly.patch
+57
file added
0001-Talk-about-rpmsign-in-the-rpmsign-man-page.patch
+64
file added
0002-Remove-use-of-bool-type-for-consistency.patch
+1231
file added
0003-Match-formatting-style-of-existing-code.patch
+27
file added
0004-Fix-printf-formatting-in-reflink.c.patch
+73
file added
0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch
+431
file added
0006-rpm2extents-verify-package-signature-during-transcod.patch
+282
file added
0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch
+116
file added
0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch
+70
file added
0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch
+204
file added
0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch
+389
file added
0011-rpm2extents-Perform-digest-computation-within-the-va.patch
+299
file added
0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch
+214
file added
0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch
+89
file added
0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch
+33
file added
0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch
+33
file added
0016-test-new-runroot_plugins-function-to-run-command-in-.patch
+63
file added
0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch
+97
file added
0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch
+175
file added
0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch
+35
file added
0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch
+43
file added
0021-tests-Fix-tests-AT_KEYWORDS-usage.patch
+132
file added
0022-reflink-fix-support-for-hardlinks.patch
+393
file added
0023-rpm2extents-Improve-logging.patch
+200
file added
0024-rpm2extents-create-footer-struct-and-helpers.patch
+176
file added
0025-extents-move-more-functions-helpers-behind-rpmextent.patch
+25
file added
0026-fix-integer-underflow-in-vfyFDCb.patch
+169
file added
0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch
+117
file added
0028-reflink-remove-requirement-for-executable-stack-flag.patch
+109
file added
0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch
+98
file added
0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch
+395
file added
0031-rpmcow-denylist.patch
+386
file added
0032-rpmcow-workaround.patch
+80
file added
0033-rpmcow-fix-stack-overflow-in-rpm2extents.patch
+28
file added
0034-rpmcow-fix-issue-for-transaction-with-transcoded-and-untranscoded-packages.patch
+11
file added
0035-rpmcow-fix-segfault-in-rpm2extents.patch
+11
file added
0036-bsearchr_static.patch
+35
file added
0037-plugin_path.patch
+279
file added
measure.patch
+193
file added
rpm-4.16.1.3-backport-multithreaded-zstd.patch
+12
file added
rpm-4.16.x-rpm_dbpath.patch
+192 -14
file changed
rpm.spec
+18
file added
rpmdb-migrate.service
+4 -4
file changed
rpmdb-rebuild.service
+40
file added
rpmdb_migrate