#2 merge c8s changes into c8s-sig-hyperscale-experimental
Closed 2 years ago by dcavalca. Opened 2 years ago by chantra.

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

+ From 845b5c3882b1eecb31d712b61a4e91fe0eb70712 Mon Sep 17 00:00:00 2001

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

+ Date: Sun, 31 Jan 2021 12:30:33 -0800

+ Subject: [PATCH 02/30] Remove use of bool type for consistency

+ 

+ ---

+  lib/fsm.c | 9 ++++-----

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

+ 

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

+ index 90193c749..feda3750c 100644

+ --- a/lib/fsm.c

+ +++ b/lib/fsm.c

+ @@ -8,7 +8,6 @@

+ 

+  #include <utime.h>

+  #include <errno.h>

+ -#include <stdbool.h>

+  #if WITH_CAP

+  #include <sys/capability.h>

+  #endif

+ @@ -896,10 +895,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  

+      Header h = rpmteHeader(te);

+      const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);

+ -    bool cpio = true;

+ +    int cpio = 1;

+  

+      if (payloadfmt && rstreq(payloadfmt, "clon")) {

+ -	cpio = false;

+ +	cpio = 0;

+      }

+  

+      /* transaction id used for temporary path suffix while installing */

+ @@ -927,13 +926,13 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	/* Run fsm file pre hook for all plugins */

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

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

+ -	fp->plugin_contents = false;

+ +	fp->plugin_contents = 0;

+  	switch (rc) {

+  	case RPMRC_OK:

+  	    setFileState(fs, fx);

+  	    break;

+  	case RPMRC_PLUGIN_CONTENTS:

+ -	    fp->plugin_contents = true;

+ +	    fp->plugin_contents = 1;

+  	    // reduce reads on cpio to this value. Could be zero if

+  	    // this is from a hard link.

+  	    rc = RPMRC_OK;

+ -- 

+ 2.35.1

+ 

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

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

+  m4_include([rpmmacro.at])

+  m4_include([rpmpython.at])

+  m4_include([rpmdepmatch.at])

+ +m4_include([rpm2extents.at])

+ -- 

+ 2.35.1

+ 

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

+ @@ -185,7 +185,7 @@ bin_PROGRAMS +=		rpmgraph

+  rpmgraph_SOURCES =	tools/rpmgraph.c

+  rpmgraph_LDADD =	lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@

+  

+ -dist_bin_SCRIPTS =	scripts/gendiff

+ +dist_bin_SCRIPTS =	scripts/gendiff scripts/rpm2extents_dump

+  

+  rpmconfig_DATA = rpmrc

+  rpmrc: $(top_srcdir)/rpmrc.in

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

+ index f943b9af4..baea987e4 100644

+ --- a/tests/rpm2extents.at

+ +++ b/tests/rpm2extents.at

+ @@ -29,3 +29,36 @@ AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rp

+  [ignore])

+  AT_CLEANUP

+  

+ +# Check that tailer writes checksig return code and content.

+ +#

+ +AT_SETUP([rpm2extents signature])

+ +AT_KEYWORDS([rpm2extents digest signature])

+ +AT_CHECK([

+ +RPMDB_INIT

+ +

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null

+ +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm

+ +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm

+ +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm

+ +],

+ +[0],

+ +[RPMSignOutput RC 2

+ +RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

+ +    Header SHA256 digest: OK

+ +    Header SHA1 digest: OK

+ +    Payload SHA256 digest: OK

+ +    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

+ +    MD5 digest: OK

+ +

+ +RPMSignOutput RC 0

+ +RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

+ +    Header SHA256 digest: OK

+ +    Header SHA1 digest: OK

+ +    Payload SHA256 digest: OK

+ +    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

+ +    MD5 digest: OK

+ +

+ +],

+ +[])

+ +AT_CLEANUP

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,204 @@ 

+ From 7da1e826ccb08fdd404524146736b3f12a473e31 Mon Sep 17 00:00:00 2001

+ From: chantra <chantr4@gmail.com>

+ Date: Mon, 31 Jan 2022 14:42:25 -0800

+ Subject: [PATCH 10/30] [rpm2extents] Make rpmkeys support reading metadata

+  from transcoded footer

+ 

+ If the file is seekable and is a transcoded file, read signature validation from transcoded file tail metadata

+ ---

+  lib/rpmchecksig.c    | 112 ++++++++++++++++++++++++++++++++++++++++++-

+  tests/rpm2extents.at |  34 ++++++++++++-

+  2 files changed, 144 insertions(+), 2 deletions(-)

+ 

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

+ index fcdbb424f..6164d012c 100644

+ --- a/lib/rpmchecksig.c

+ +++ b/lib/rpmchecksig.c

+ @@ -24,6 +24,11 @@

+  

+  #include "debug.h"

+  

+ +/* magic value at end of file (64 bits) that indicates this is a transcoded

+ + * rpm.

+ + */

+ +#define MAGIC 3472329499408095051

+ +

+  int _print_pkts = 0;

+  

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

+ @@ -220,6 +225,107 @@ exit:

+      return rc;

+  }

+  

+ +static rpmRC isTranscodedRpm(FD_t fd) {

+ +    rpmRC rc = RPMRC_NOTFOUND;

+ +    rpm_loff_t current;

+ +    uint64_t magic;

+ +    size_t len;

+ +

+ +    // If the file is not seekable, we cannot detect whether or not it is transcoded.

+ +    if(Fseek(fd, 0, SEEK_CUR) < 0) {

+ +        return RPMRC_FAIL;

+ +    }

+ +    current = Ftell(fd);

+ +

+ +    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));

+ +	rc = RPMRC_FAIL;

+ +	goto exit;

+ +    }

+ +    len = sizeof(magic);

+ +    if (Fread(&magic, len, 1, fd) != len) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));

+ +	rc = RPMRC_FAIL;

+ +	goto exit;

+ +    }

+ +    if (magic != MAGIC) {

+ +	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));

+ +	rc = RPMRC_NOTFOUND;

+ +	goto exit;

+ +    }

+ +    rc = RPMRC_OK;

+ +exit:

+ +    if (Fseek(fd, current, SEEK_SET) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));

+ +    }

+ +    return rc;

+ +}

+ +

+ +static int rpmpkgVerifySigsTranscoded(FD_t fd){

+ +    rpm_loff_t current;

+ +    uint64_t magic;

+ +    rpm_loff_t offset;

+ +    int32_t rc;

+ +    size_t len;

+ +    uint64_t content_len;

+ +    char *content = NULL;

+ +

+ +    current = Ftell(fd);

+ +

+ +    if(Fseek(fd, -(sizeof(magic) + 3 * sizeof(offset) ), SEEK_END) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: failed to seek for offset\n"));

+ +	rc = -1;

+ +	goto exit;

+ +    }

+ +

+ +    len = sizeof(offset);

+ +    if (Fread(&offset, len, 1, fd) != len) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification offset\n"));

+ +	rc = -1;

+ +	goto exit;

+ +    }

+ +

+ +    if(Fseek(fd,  offset, SEEK_SET) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to seek signature verification offset\n"));

+ +	rc = -1;

+ +	goto exit;

+ +    }

+ +    len = sizeof(rc);

+ +    if (Fread(&rc, len, 1, fd) != len) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification RC\n"));

+ +	rc = -1;

+ +	goto exit;

+ +    }

+ +

+ +    len = sizeof(content_len);

+ +    if (Fread(&content_len, len, 1, fd) != len) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content length\n"));

+ +	goto exit;

+ +    }

+ +

+ +    content = malloc(content_len + 1);

+ +    if(content == NULL) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to allocate memory to read signature content\n"));

+ +	goto exit;

+ +    }

+ +    content[content_len] = 0;

+ +    if (Fread(content, content_len, 1, fd) != content_len) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content\n"));

+ +	goto exit;

+ +    }

+ +

+ +    rpmlog(RPMLOG_NOTICE, "%s", content);

+ +exit:

+ +    if(content){

+ +	free(content);

+ +    }

+ +    if (Fseek(fd, current, SEEK_SET) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: unable to seek back to original location\n"));

+ +    }

+ +    return rc;

+ +

+ +}

+ +

+  static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,

+  			   FD_t fd, const char *fn)

+  {

+ @@ -229,10 +335,14 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,

+  			    .verbose = rpmIsVerbose(),

+      };

+      int rc;

+ -    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);

+  

+      rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : "");

+  

+ +    if(isTranscodedRpm(fd) == RPMRC_OK){

+ +	return rpmpkgVerifySigsTranscoded(fd);

+ +    }

+ +    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);

+ +

+      rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);

+  

+      if (rc)

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

+ index baea987e4..18accfc75 100644

+ --- a/tests/rpm2extents.at

+ +++ b/tests/rpm2extents.at

+ @@ -29,7 +29,7 @@ AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rp

+  [ignore])

+  AT_CLEANUP

+  

+ -# Check that tailer writes checksig return code and content.

+ +# Check that transcoder writes checksig return code and content.

+  #

+  AT_SETUP([rpm2extents signature])

+  AT_KEYWORDS([rpm2extents digest signature])

+ @@ -62,3 +62,35 @@ RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

+  ],

+  [])

+  AT_CLEANUP

+ +

+ +AT_SETUP([rpm2extents signature verification])

+ +AT_KEYWORDS([rpm2extents digest signature])

+ +AT_CHECK([

+ +RPMDB_INIT

+ +

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null

+ +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

+ +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm

+ +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

+ +],

+ +[0],

+ +[/tmp/hello-2.0-1.x86_64-signed.rpm:

+ +    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

+ +    Header SHA256 digest: OK

+ +    Header SHA1 digest: OK

+ +    Payload SHA256 digest: OK

+ +    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

+ +    MD5 digest: OK

+ +1

+ +/tmp/hello-2.0-1.x86_64-signed.rpm:

+ +    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

+ +    Header SHA256 digest: OK

+ +    Header SHA1 digest: OK

+ +    Payload SHA256 digest: OK

+ +    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

+ +    MD5 digest: OK

+ +0

+ +],

+ +[])

+ +AT_CLEANUP

+ -- 

+ 2.35.1

+ 

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

+ @@ -40,7 +40,8 @@ librpm_la_SOURCES = \

+  	rpmscript.h rpmscript.c \

+  	rpmchroot.c rpmchroot.h \

+  	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \

+ -	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h

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

+ +	rpmextents.c rpmextents_internal.h

+  

+  librpm_la_LDFLAGS = -version-info $(rpm_version_info)

+  

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

+ index 6164d012c..dc1726a18 100644

+ --- a/lib/rpmchecksig.c

+ +++ b/lib/rpmchecksig.c

+ @@ -20,15 +20,11 @@

+  #include "rpmio/rpmio_internal.h" 	/* fdSetBundle() */

+  #include "lib/rpmlead.h"

+  #include "lib/header_internal.h"

+ +#include "lib/rpmextents_internal.h"

+  #include "lib/rpmvs.h"

+  

+  #include "debug.h"

+  

+ -/* magic value at end of file (64 bits) that indicates this is a transcoded

+ - * rpm.

+ - */

+ -#define MAGIC 3472329499408095051

+ -

+  int _print_pkts = 0;

+  

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

+ @@ -225,42 +221,6 @@ exit:

+      return rc;

+  }

+  

+ -static rpmRC isTranscodedRpm(FD_t fd) {

+ -    rpmRC rc = RPMRC_NOTFOUND;

+ -    rpm_loff_t current;

+ -    uint64_t magic;

+ -    size_t len;

+ -

+ -    // If the file is not seekable, we cannot detect whether or not it is transcoded.

+ -    if(Fseek(fd, 0, SEEK_CUR) < 0) {

+ -        return RPMRC_FAIL;

+ -    }

+ -    current = Ftell(fd);

+ -

+ -    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {

+ -	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));

+ -	rc = RPMRC_FAIL;

+ -	goto exit;

+ -    }

+ -    len = sizeof(magic);

+ -    if (Fread(&magic, len, 1, fd) != len) {

+ -	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));

+ -	rc = RPMRC_FAIL;

+ -	goto exit;

+ -    }

+ -    if (magic != MAGIC) {

+ -	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));

+ -	rc = RPMRC_NOTFOUND;

+ -	goto exit;

+ -    }

+ -    rc = RPMRC_OK;

+ -exit:

+ -    if (Fseek(fd, current, SEEK_SET) < 0) {

+ -	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));

+ -    }

+ -    return rc;

+ -}

+ -

+  static int rpmpkgVerifySigsTranscoded(FD_t fd){

+      rpm_loff_t current;

+      uint64_t magic;

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

+ new file mode 100644

+ index 000000000..015277751

+ --- /dev/null

+ +++ b/lib/rpmextents.c

+ @@ -0,0 +1,46 @@

+ +

+ +#include "system.h"

+ +

+ +#include <rpm/rpmlog.h>

+ +#include <rpm/rpmio.h>

+ +

+ +#include "lib/rpmextents_internal.h"

+ +

+ +rpmRC isTranscodedRpm(FD_t fd) {

+ +    rpmRC rc = RPMRC_NOTFOUND;

+ +    rpm_loff_t current;

+ +    extents_magic_t magic;

+ +    size_t len;

+ +

+ +    // If the file is not seekable, we cannot detect whether or not it is transcoded.

+ +    if(Fseek(fd, 0, SEEK_CUR) < 0) {

+ +        return RPMRC_FAIL;

+ +    }

+ +    current = Ftell(fd);

+ +

+ +    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));

+ +	rc = RPMRC_FAIL;

+ +	goto exit;

+ +    }

+ +    len = sizeof(magic);

+ +    if (Fread(&magic, len, 1, fd) != len) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));

+ +	rc = RPMRC_FAIL;

+ +	goto exit;

+ +    }

+ +    if (magic != EXTENTS_MAGIC) {

+ +	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));

+ +	rc = RPMRC_NOTFOUND;

+ +	goto exit;

+ +    }

+ +    rc = RPMRC_OK;

+ +exit:

+ +    if (Fseek(fd, current, SEEK_SET) < 0) {

+ +	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));

+ +	rc = RPMRC_FAIL;

+ +    }

+ +    return rc;

+ +}

+ +

+ +

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

+ new file mode 100644

+ index 000000000..57cecfc31

+ --- /dev/null

+ +++ b/lib/rpmextents_internal.h

+ @@ -0,0 +1,22 @@

+ +#ifndef _RPMEXTENTS_INTERNAL_H

+ +#define _RPMEXTENTS_INTERNAL_H

+ +

+ +#ifdef __cplusplus

+ +extern "C" {

+ +#endif

+ +

+ +#include <stdint.h>

+ +

+ +/* magic value at end of file (64 bits) that indicates this is a transcoded

+ + * rpm.

+ + */

+ +#define EXTENTS_MAGIC 3472329499408095051

+ +

+ +typedef uint64_t extents_magic_t;

+ +

+ +rpmRC isTranscodedRpm(FD_t fd);

+ +

+ +#ifdef __cplusplus

+ +}

+ +#endif

+ +#endif

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

+ index 513887604..ec575f55e 100644

+ --- a/plugins/reflink.c

+ +++ b/plugins/reflink.c

+ @@ -13,6 +13,7 @@

+  #include <rpm/rpmlog.h>

+  #include "lib/rpmlib.h"

+  #include "lib/rpmplugin.h"

+ +#include "lib/rpmextents_internal.h"

+  #include "lib/rpmte_internal.h"

+  #include <rpm/rpmfileutil.h>

+  #include "rpmio/rpmio_internal.h"

+ @@ -40,11 +41,6 @@

+  

+  #define BUFFER_SIZE (1024 * 128)

+  

+ -/* magic value at end of file (64 bits) that indicates this is a transcoded

+ - * rpm.

+ - */

+ -#define MAGIC 3472329499408095051

+ -

+  struct reflink_state_s {

+      /* Stuff that's used across rpms */

+      long fundamental_block_size;

+ @@ -96,40 +92,28 @@ static void reflink_cleanup(rpmPlugin plugin) {

+  }

+  

+  static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {

+ +    rpmRC rc;

+ +    size_t len;

+ +

+      reflink_state state = rpmPluginGetData(plugin);

+      state->fd = rpmteFd(te);

+      if (state->fd == 0) {

+  	rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));

+  	return RPMRC_OK;

+      }

+ +

+      rpm_loff_t current = Ftell(state->fd);

+ -    uint64_t magic;

+ -    if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {

+ -	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));

+ -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

+ -	    /* yes this gets a bit repetitive */

+ -	    rpmlog(RPMLOG_ERR,

+ -		 _("reflink: unable to seek back to original location\n"));

+ -	}

+ -	return RPMRC_FAIL;

+ -    }

+ -    size_t len = sizeof(magic);

+ -    if (Fread(&magic, len, 1, state->fd) != len) {

+ -	rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));

+ -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

+ -	    rpmlog(RPMLOG_ERR,

+ -		   _("reflink: unable to seek back to original location\n"));

+ -	}

+ -	return RPMRC_FAIL;

+ -    }

+ -    if (magic != MAGIC) {

+ -	rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));

+ -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

+ -	    rpmlog(RPMLOG_ERR,

+ -		   _("reflink: unable to seek back to original location\n"));

+ +    rc = isTranscodedRpm(state->fd);

+ +

+ +    switch(rc){

+ +	// Fail to parse the file, fail the plugin.

+ +	case RPMRC_FAIL:

+  	    return RPMRC_FAIL;

+ -	}

+ -	return RPMRC_OK;

+ +	// This is not a transcoded file, do nothing.

+ +	case RPMRC_NOTFOUND:

+ +	    return RPMRC_OK;

+ +	default:

+ +	    break;

+      }

+      rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));

+      Header h = rpmteHeader(te);

+ @@ -140,7 +124,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {

+      headerFree(h);

+      state->files = rpmteFiles(te);

+      /* tail of file contains offset_table, offset_checksums then magic */

+ -    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {

+ +    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) {

+  	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),

+  	       state->fd);

+  	return RPMRC_FAIL;

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

+ index e316a2834..a326e3857 100644

+ --- a/rpm2extents.c

+ +++ b/rpm2extents.c

+ @@ -15,6 +15,7 @@

+  #include "lib/rpmts.h"

+  #include "lib/signature.h"

+  #include "lib/header_internal.h"

+ +#include "lib/rpmextents_internal.h"

+  #include "rpmio/rpmio_internal.h"

+  

+  #include <unistd.h>

+ @@ -37,11 +38,6 @@

+  #include "lib/rpmhash.H"

+  #include "lib/rpmhash.C"

+  

+ -/* magic value at end of file (64 bits) that indicates this is a transcoded

+ - * rpm.

+ - */

+ -#define MAGIC 3472329499408095051

+ -

+  struct digestoffset {

+      const unsigned char * digest;

+      rpm_loff_t pos;

+ @@ -402,7 +398,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)

+  	rc = RPMRC_FAIL;

+  	goto exit;

+      }

+ -    uint64_t magic = MAGIC;

+ +    extents_magic_t magic = EXTENTS_MAGIC;

+      len = sizeof(magic);

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

+  	fprintf(stderr, _("Unable to write magic\n"));

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,109 @@ 

+ From 5c97d7f83f56015d6a37934cee4e55ed8d747890 Mon Sep 17 00:00:00 2001

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 16:57:25 -0800

+ Subject: [PATCH 13/30] [plugin] add `plugin_fsm_file_install_func` plugin hook

+ 

+ This hook is to be called when installing individual files from the RPM.

+ ---

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

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

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

+  3 files changed, 57 insertions(+)

+ 

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

+ index fd81aec8d..877db81f3 100644

+ --- a/lib/rpmplugin.h

+ +++ b/lib/rpmplugin.h

+ @@ -60,6 +60,10 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,

+  					      const char* path,

+  					      const char *dest,

+  					      mode_t file_mode, rpmFsmOp op);

+ +typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,

+ +					      const char* path,

+ +					      mode_t file_mode, rpmFsmOp op);

+ +

+  

+  typedef struct rpmPluginHooks_s * rpmPluginHooks;

+  struct rpmPluginHooks_s {

+ @@ -80,6 +84,7 @@ struct rpmPluginHooks_s {

+      plugin_fsm_file_pre_func		fsm_file_pre;

+      plugin_fsm_file_post_func		fsm_file_post;

+      plugin_fsm_file_prepare_func	fsm_file_prepare;

+ +    plugin_fsm_file_install_func	fsm_file_install;

+  };

+  

+  #ifdef __cplusplus

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

+ index 3da3097af..850a025a0 100644

+ --- a/lib/rpmplugins.c

+ +++ b/lib/rpmplugins.c

+ @@ -421,3 +421,40 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,

+  

+      return rc;

+  }

+ +

+ +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

+ +				   const char *path, mode_t file_mode,

+ +				   rpmFsmOp op)

+ +{

+ +    plugin_fsm_file_install_func hookFunc;

+ +    int i;

+ +    rpmRC rc = RPMRC_OK;

+ +    rpmRC hook_rc;

+ +

+ +    for (i = 0; i < plugins->count; i++) {

+ +	rpmPlugin plugin = plugins->plugins[i];

+ +	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_install);

+ +	if (hookFunc) {

+ +	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);

+ +	    if (hook_rc == RPMRC_FAIL) {

+ +		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install failed\n", plugin->name);

+ +		rc = RPMRC_FAIL;

+ +	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {

+ +		if (rc == RPMRC_PLUGIN_CONTENTS) {

+ +		    /* Another plugin already said it'd handle contents. It's

+ +		     * undefined how these would combine, so treat this as a

+ +		     * failure condition.

+ +		    */

+ +		    rc = RPMRC_FAIL;

+ +		} else {

+ +		    /* Plugin will handle content */

+ +		    rc = RPMRC_PLUGIN_CONTENTS;

+ +		}

+ +	    }

+ +	}

+ +    }

+ +

+ +    return rc;

+ +}

+ +

+ +

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

+ index 39762c376..5365cf698 100644

+ --- a/lib/rpmplugins.h

+ +++ b/lib/rpmplugins.h

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

+                                     const char *path, const char *dest,

+                                     mode_t mode, rpmFsmOp op);

+  

+ +/** \ingroup rpmplugins

+ + * Call the fsm file install plugin hook

+ + * @param plugins	plugins structure

+ + * @param fi		file info iterator (or NULL)

+ + * @param path		file object path

+ + * @param file_mode	file object mode

+ + * @param op		file operation + associated flags

+ + * @return		RPMRC_OK on success, RPMRC_FAIL otherwise

+ + */

+ +RPM_GNUC_INTERNAL

+ +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

+ +				   const char* path, mode_t file_mode,

+ +				   rpmFsmOp op);

+ +

+ +

+  #ifdef __cplusplus

+  }

+  #endif

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,79 @@ 

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

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 17:06:55 -0800

+ Subject: [PATCH 14/30] [fsm] Call new `rpmpluginsCallFsmFileInstall` in

+  `rpmPackageFilesInstall`

+ 

+ Call `rpmpluginsCallFsmFileInstall` for every files to be installed by

+ `rpmPackageFilesInstall`, this allows for plugin such as reflink to

+ write (reflink) a file and make sure the parent directories have already

+ been created.

+ If this was done in `rpmpluginsCallFsmFilePre`, the directories may be

+ missing, which makes file installation fail.

+ ---

+  lib/fsm.c | 29 +++++++++--------------------

+  1 file changed, 9 insertions(+), 20 deletions(-)

+ 

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

+ index feda3750c..711f553d3 100644

+ --- a/lib/fsm.c

+ +++ b/lib/fsm.c

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

+      int stage;

+      int setmeta;

+      int skip;

+ -    int plugin_contents;

+      rpmFileAction action;

+      const char *suffix;

+      char *fpath;

+ @@ -921,23 +920,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	/* Remap file perms, owner, and group. */

+  	rc = rpmfiStat(fi, 1, &fp->sb);

+  

+ +	setFileState(fs, fx);

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

+  

+  	/* Run fsm file pre hook for all plugins */

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

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

+ -	fp->plugin_contents = 0;

+ -	switch (rc) {

+ -	case RPMRC_OK:

+ -	    setFileState(fs, fx);

+ -	    break;

+ -	case RPMRC_PLUGIN_CONTENTS:

+ -	    fp->plugin_contents = 1;

+ -	    // reduce reads on cpio to this value. Could be zero if

+ -	    // this is from a hard link.

+ -	    rc = RPMRC_OK;

+ -	    break;

+ -	}

+  	fp->stage = FILE_PRE;

+      }

+      fi = rpmfiFree(fi);

+ @@ -992,14 +980,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+  	    if (fp->action == FA_TOUCH)

+  		continue;

+  

+ -            if (S_ISREG(fp->sb.st_mode)) {

+ +	    rpmRC plugin_rc = rpmpluginsCallFsmFileInstall(plugins, fi, fp->fpath, fp->sb.st_mode, fp->action);

+ +	    if(!(plugin_rc == RPMRC_PLUGIN_CONTENTS || plugin_rc == RPMRC_OK)){

+ +		rc = plugin_rc;

+ +	    } else if(plugin_rc == RPMRC_PLUGIN_CONTENTS){

+ +		rc = RPMRC_OK;

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

+  		if (rc == RPMERR_ENOENT) {

+ -		    if(fp->plugin_contents) {

+ -			rc = RPMRC_OK;

+ -		    }else {

+ -			rc = fsmMkfile(fi, fp, files, psm, nodigest,

+ -			    &firstlink, &firstlinkfile);

+ -		    }

+ +		    rc = fsmMkfile(fi, fp, files, psm, nodigest,

+ +			&firstlink, &firstlinkfile);

+  		}

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

+                  if (rc == RPMERR_ENOENT) {

+ -- 

+ 2.35.1

+ 

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

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

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 17:12:09 -0800

+ Subject: [PATCH 16/30] [test] new runroot_plugins function to run command in

+  fakeroot without having the plugins disabled

+ 

+ ---

+  tests/atlocal.in | 9 +++++++++

+  1 file changed, 9 insertions(+)

+ 

+ diff --git a/tests/atlocal.in b/tests/atlocal.in

+ index c3189d327..c18637362 100644

+ --- a/tests/atlocal.in

+ +++ b/tests/atlocal.in

+ @@ -82,6 +82,14 @@ function runroot()

+      )

+  }

+  

+ +function runroot_plugins()

+ +{

+ +    (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \

+ +     MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns

+ +    )

+ +}

+ +

+ +

+  function runroot_other()

+  {

+      (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,63 @@ 

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

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 17:12:55 -0800

+ Subject: [PATCH 17/30] [test] Add test installing an RPM with reflink plugin

+ 

+ The test only runs for reflinkable FS, like XFS and BTRFS.

+ dbus_announce is being disabled as it generates warning to stderr when

+ running within the fakeroot.

+ ---

+  tests/atlocal.in     | 13 +++++++++++++

+  tests/rpm2extents.at | 15 +++++++++++++++

+  2 files changed, 28 insertions(+)

+ 

+ diff --git a/tests/atlocal.in b/tests/atlocal.in

+ index c18637362..a110564e2 100644

+ --- a/tests/atlocal.in

+ +++ b/tests/atlocal.in

+ @@ -50,6 +50,19 @@ else

+  

+  RPM_XFAIL=${RPM_XFAIL-1}

+  

+ +FSTYPE=$(stat -f -c %T /)

+ +REFLINKABLE_FS=("xfs" "brtfs")

+ +

+ +REFLINK_DISABLED=true;

+ +for item in "${REFLINKABLE_FS[@]}"

+ +do

+ +    if test "${FSTYPE}" = "${item}"

+ +    then

+ +	REFLINK_DISABLED=false;

+ +	break

+ +    fi

+ +done

+ +

+  function run()

+  {

+      "$@" --define "_tmppath ${RPMTEST}/tmp" --define "_topdir ${TOPDIR}" --dbpath="${RPMTEST}/var/lib/rpm/"

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

+ index 18accfc75..44e46a68e 100644

+ --- a/tests/rpm2extents.at

+ +++ b/tests/rpm2extents.at

+ @@ -94,3 +94,18 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

+  ],

+  [])

+  AT_CLEANUP

+ +

+ +AT_SETUP([rpm2extents install package])

+ +AT_KEYWORDS([rpm2extents install package])

+ +AT_SKIP_IF([$REFLINK_DISABLED])

+ +AT_CHECK([

+ +RPMDB_INIT

+ +

+ +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null

+ +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm

+ +test -f ${RPMTEST}/usr/bin/hello

+ +],

+ +[0],

+ +[],

+ +[])

+ +AT_CLEANUP

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,97 @@ 

+ From 048db395b6de8544dc88231f0afebee8570daee6 Mon Sep 17 00:00:00 2001

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 18:21:33 -0800

+ Subject: [PATCH 18/30] [plugin] add `rpmpluginsCallFsmFileArchiveReader`

+ 

+ This allows plugins to provide a custom `rpmfi` to

+ `rpmPackageFilesInstall` function in fsm.c. It enables supporting

+ transcoded files such as with reflink plugin.

+ ---

+  lib/rpmplugin.h  |  4 ++++

+  lib/rpmplugins.c | 34 ++++++++++++++++++++++++++++++++++

+  lib/rpmplugins.h |  4 +++-

+  3 files changed, 41 insertions(+), 1 deletion(-)

+ 

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

+ index 877db81f3..6dbbcff35 100644

+ --- a/lib/rpmplugin.h

+ +++ b/lib/rpmplugin.h

+ @@ -63,6 +63,9 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,

+  typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,

+  					      const char* path,

+  					      mode_t file_mode, rpmFsmOp op);

+ +typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin,

+ +						     FD_t payload,

+ +						     rpmfiles files, rpmfi *fi);

+  

+  

+  typedef struct rpmPluginHooks_s * rpmPluginHooks;

+ @@ -85,6 +88,7 @@ struct rpmPluginHooks_s {

+      plugin_fsm_file_post_func		fsm_file_post;

+      plugin_fsm_file_prepare_func	fsm_file_prepare;

+      plugin_fsm_file_install_func	fsm_file_install;

+ +    plugin_fsm_file_archive_reader_func	fsm_file_archive_reader;

+  };

+  

+  #ifdef __cplusplus

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

+ index 850a025a0..901af1ac5 100644

+ --- a/lib/rpmplugins.c

+ +++ b/lib/rpmplugins.c

+ @@ -457,4 +457,38 @@ rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

+      return rc;

+  }

+  

+ +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,

+ +				   rpmfiles files, rpmfi *fi)

+ +{

+ +    plugin_fsm_file_archive_reader_func hookFunc;

+ +    int i;

+ +    rpmRC rc = RPMRC_OK;

+ +    rpmRC hook_rc;

+ +

+ +    for (i = 0; i < plugins->count; i++) {

+ +	rpmPlugin plugin = plugins->plugins[i];

+ +	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_archive_reader);

+ +	if (hookFunc) {

+ +	    hook_rc = hookFunc(plugin, payload, files, fi);

+ +	    if (hook_rc == RPMRC_FAIL) {

+ +		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader failed\n", plugin->name);

+ +		rc = RPMRC_FAIL;

+ +	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {

+ +		if (rc == RPMRC_PLUGIN_CONTENTS) {

+ +		    /* Another plugin already said it'd handle contents. It's

+ +		     * undefined how these would combine, so treat this as a

+ +		     * failure condition.

+ +		    */

+ +		    rc = RPMRC_FAIL;

+ +		} else {

+ +		    /* Plugin will handle content */

+ +		    rc = RPMRC_PLUGIN_CONTENTS;

+ +		}

+ +	    }

+ +	}

+ +    }

+ +

+ +    return rc;

+ +}

+ +

+  

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

+ index 5365cf698..88807c53c 100644

+ --- a/lib/rpmplugins.h

+ +++ b/lib/rpmplugins.h

+ @@ -181,7 +181,9 @@ rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

+  				   const char* path, mode_t file_mode,

+  				   rpmFsmOp op);

+  

+ -

+ +RPM_GNUC_INTERNAL

+ +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,

+ +					 rpmfiles files, rpmfi *fi);

+  #ifdef __cplusplus

+  }

+  #endif

+ -- 

+ 2.35.1

+ 

@@ -0,0 +1,156 @@ 

+ From 5f762af17c6e72e86e4710975dcdfd71fc5d1b07 Mon Sep 17 00:00:00 2001

+ From: chantra <chantr4@gmail.com>

+ Date: Tue, 8 Feb 2022 18:24:12 -0800

+ Subject: [PATCH 19/30] [reflink] use `rpmpluginsCallFsmFileArchiveReader` to

+  provide custom rpmfi

+ 

+ As the plugin can now be responsible to provide an Archive Reader to

+ `rpmPackageFilesInstall`, we also don't need to mutate the

+ `RPMTAG_PAYLOADFORMAT` header in memory, removing some special-casing.

+ ---

+  lib/depends.c     |  2 --

+  lib/fsm.c         | 38 +++++++++++++++++++-------------------

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

+  3 files changed, 32 insertions(+), 26 deletions(-)

+ 

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

+ index 8998afcd3..30234df3d 100644

+ --- a/lib/depends.c

+ +++ b/lib/depends.c

+ @@ -81,8 +81,6 @@ static rpmRC headerCheckPayloadFormat(Header h) {

+       */

+      if (!payloadfmt) return rc;

+  

+ -    if (rstreq(payloadfmt, "clon")) return rc;

+ -

+      if (!rstreq(payloadfmt, "cpio")) {

+          char *nevra = headerGetAsString(h, RPMTAG_NEVRA);

+          if (payloadfmt && rstreq(payloadfmt, "drpm")) {

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

+ index 711f553d3..6972602e0 100644

+ --- a/lib/fsm.c

+ +++ b/lib/fsm.c

+ @@ -19,7 +19,6 @@

+  

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

+  #include "lib/fsm.h"

+ -#include "lib/rpmlib.h"

+  #include "lib/rpmte_internal.h"	/* XXX rpmfs */

+  #include "lib/rpmplugins.h"	/* rpm plugins hooks */

+  #include "lib/rpmug.h"

+ @@ -892,14 +891,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

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

+      struct filedata_s *firstlink = NULL;

+  

+ -    Header h = rpmteHeader(te);

+ -    const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);

+ -    int cpio = 1;

+ -

+ -    if (payloadfmt && rstreq(payloadfmt, "clon")) {

+ -	cpio = 0;

+ -    }

+ -

+      /* transaction id used for temporary path suffix while installing */

+      rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));

+  

+ @@ -933,14 +924,24 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+      if (rc)

+  	goto exit;

+  

+ -    if (cpio) {

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

+ -	if (fi == NULL) {

+ -	    rc = RPMERR_BAD_MAGIC;

+ -	    goto exit;

+ -	}

+ -    } else {

+ -	fi = rpmfilesIter(files, RPMFI_ITER_FWD);

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

+ +    switch(plugin_rc) {

+ +	case RPMRC_PLUGIN_CONTENTS:

+ +	    if(fi == NULL) {

+ +		rc = RPMERR_BAD_MAGIC;

+ +		goto exit;

+ +	    }

+ +	    rc = RPMRC_OK;

+ +	    break;

+ +	case RPMRC_OK:

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

+ +	    if (fi == NULL) {

+ +		rc = RPMERR_BAD_MAGIC;

+ +		goto exit;

+ +	    }

+ +	    break;

+ +	default:

+ +	    rc = RPMRC_FAIL;

+      }

+  

+      /* Detect and create directories not explicitly in package. */

+ @@ -988,7 +989,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

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

+  		if (rc == RPMERR_ENOENT) {

+  		    rc = fsmMkfile(fi, fp, files, psm, nodigest,

+ -			&firstlink, &firstlinkfile);

+ +				   &firstlink, &firstlinkfile);

+  		}

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

+                  if (rc == RPMERR_ENOENT) {

+ @@ -1096,7 +1097,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

+      rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));

+  

+  exit:

+ -    h = headerFree(h);

+      fi = rpmfiFree(fi);

+      Fclose(payload);

+      free(tid);

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

+ index 7dda06d8e..d5e6db27a 100644

+ --- a/plugins/reflink.c

+ +++ b/plugins/reflink.c

+ @@ -54,6 +54,7 @@ struct reflink_state_s {

+      FD_t fd;

+      rpmfiles files;

+      inodeIndexHash inodeIndexes;

+ +    int transcoded;

+  };

+  

+  typedef struct reflink_state_s * reflink_state;

+ @@ -116,12 +117,8 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {

+  	    break;

+      }

+      rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));

+ -    Header h = rpmteHeader(te);

+ +    state->transcoded = 1;

+  

+ -    /* replace/add header that main fsm.c can read */

+ -    headerDel(h, RPMTAG_PAYLOADFORMAT);

+ -    headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");

+ -    headerFree(h);

+      state->files = rpmteFiles(te);

+      /* tail of file contains offset_table, offset_checksums then magic */

+      if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) {

+ @@ -350,10 +347,21 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa

+      return RPMRC_OK;

+  }

+  

+ +static rpmRC reflink_fsm_file_archive_reader(rpmPlugin plugin, FD_t payload,

+ +					     rpmfiles files, rpmfi *fi) {

+ +    reflink_state state = rpmPluginGetData(plugin);

+ +    if(state->transcoded) {

+ +	*fi = rpmfilesIter(files, RPMFI_ITER_FWD);

+ +	return RPMRC_PLUGIN_CONTENTS;

+ +    }

+ +    return RPMRC_OK;

+ +}

+ +

+  struct rpmPluginHooks_s reflink_hooks = {

+      .init = reflink_init,

+      .cleanup = reflink_cleanup,

+      .psm_pre = reflink_psm_pre,

+      .psm_post = reflink_psm_post,

+      .fsm_file_install = reflink_fsm_file_install,

+ +    .fsm_file_archive_reader = reflink_fsm_file_archive_reader,

+  };

+ -- 

+ 2.35.1

+ 

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

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

+ From: chantra <chantr4@gmail.com>

+ Date: Mon, 28 Mar 2022 19:42:39 -0700

+ Subject: [PATCH 30/30] [rpmcow] Make rpm -i install package without the need

+  of --nodigest

+ 

+ When using transcoded files, the logic to check signature is different

+ and was done while the file was transcoded. This change the code path

+ used by `rpm -{i,U}` to check if the file is transcoded, and in such

+ cases, assume it was already verified.

+ ---

+  lib/transaction.c    | 29 ++++++++++++++++++-----------

+  tests/rpm2extents.at |  6 +++---

+  2 files changed, 21 insertions(+), 14 deletions(-)

+ 

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

+ index 36c2a7a64..703e4140c 100644

+ --- a/lib/transaction.c

+ +++ b/lib/transaction.c

+ @@ -37,6 +37,7 @@

+  #include "lib/rpmfi_internal.h"	/* only internal apis */

+  #include "lib/rpmte_internal.h"	/* only internal apis */

+  #include "lib/rpmts_internal.h"

+ +#include "lib/rpmextents_internal.h"

+  #include "lib/rpmvs.h"

+  #include "rpmio/rpmhook.h"

+  #include "lib/rpmtriggers.h"

+ @@ -1255,10 +1256,16 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)

+  	    .signature = RPMRC_NOTFOUND,

+  	    .vfylevel = vfylevel,

+  	};

+ +	int verified = 0;

+  	rpmRC prc = RPMRC_FAIL;

+  

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

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

+ +	if (fd != NULL && isTranscodedRpm(fd) == RPMRC_OK) {

+ +	    /* Transcoded RPMs are validated at transcoding time */

+ +	    prc = RPMRC_OK;

+ +	    verified = 1;

+ +	} else {

+  	if (fd != NULL) {

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

+  	    rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_FILE, 0, 0);

+ @@ -1267,8 +1274,11 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)

+  	if (prc == RPMRC_OK)

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

+  

+ +	    verified = vd.signature == RPMRC_OK;

+ +	}

+ +

+  	/* Record verify result, signatures only for now */

+ -	rpmteSetVerified(p, vd.signature == RPMRC_OK);

+ +	rpmteSetVerified(p, verified);

+  

+  	if (prc)

+  	    rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0);

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

+ index 5c66de7f6..5135c9cf8 100644

+ --- a/tests/rpm2extents.at

+ +++ b/tests/rpm2extents.at

+ @@ -102,7 +102,7 @@ AT_CHECK([

+  RPMDB_INIT

+  

+  runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null

+ -runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm

+ +runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm

+  test -f ${RPMTEST}/usr/bin/hello

+  ],

+  [0],

+ @@ -115,7 +115,7 @@ AT_KEYWORDS([reflink])

+  AT_CHECK([

+  RPMDB_INIT

+  

+ -runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?

+ +runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?

+  # Check that the file is properly installed in chroot

+  test -f ${RPMTEST}/usr/bin/hello

+  ],

+ @@ -132,7 +132,7 @@ RPMDB_INIT

+  

+  PKG=hlinktest-1.0-1.noarch.rpm

+  runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null

+ -runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}

+ +runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}

+  ],

+  [0],

+  [],

+ -- 

+ 2.35.1

+ 

@@ -1,1640 +0,0 @@ 

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

- index 288668819..96542c8c8 100644

- --- a/Makefile.am

- +++ b/Makefile.am

- @@ -185,7 +185,7 @@ bin_PROGRAMS +=		rpmgraph

-  rpmgraph_SOURCES =	tools/rpmgraph.c

-  rpmgraph_LDADD =	lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@

-  

- -dist_bin_SCRIPTS =	scripts/gendiff

- +dist_bin_SCRIPTS =	scripts/gendiff scripts/rpm2extents_dump

-  

-  rpmconfig_DATA = rpmrc

-  rpmrc: $(top_srcdir)/rpmrc.in

- diff --git a/lib/Makefile.am b/lib/Makefile.am

- index 5a1b6ca9b..2f1b3597f 100644

- --- a/lib/Makefile.am

- +++ b/lib/Makefile.am

- @@ -40,7 +40,8 @@ librpm_la_SOURCES = \

-  	rpmscript.h rpmscript.c \

-  	rpmchroot.c rpmchroot.h \

-  	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \

- -	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h

- +	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h \

- +	rpmextents.c rpmextents_internal.h

-  

-  librpm_la_LDFLAGS = -version-info $(rpm_version_info)

-  

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

- index 8998afcd3..30234df3d 100644

- --- a/lib/depends.c

- +++ b/lib/depends.c

- @@ -81,8 +81,6 @@ static rpmRC headerCheckPayloadFormat(Header h) {

-       */

-      if (!payloadfmt) return rc;

-  

- -    if (rstreq(payloadfmt, "clon")) return rc;

- -

-      if (!rstreq(payloadfmt, "cpio")) {

-          char *nevra = headerGetAsString(h, RPMTAG_NEVRA);

-          if (payloadfmt && rstreq(payloadfmt, "drpm")) {

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

- index feda3750c..6972602e0 100644

- --- a/lib/fsm.c

- +++ b/lib/fsm.c

- @@ -19,7 +19,6 @@

-  

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

-  #include "lib/fsm.h"

- -#include "lib/rpmlib.h"

-  #include "lib/rpmte_internal.h"	/* XXX rpmfs */

-  #include "lib/rpmplugins.h"	/* rpm plugins hooks */

-  #include "lib/rpmug.h"

- @@ -53,7 +52,6 @@ struct filedata_s {

-      int stage;

-      int setmeta;

-      int skip;

- -    int plugin_contents;

-      rpmFileAction action;

-      const char *suffix;

-      char *fpath;

- @@ -893,14 +891,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

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

-      struct filedata_s *firstlink = NULL;

-  

- -    Header h = rpmteHeader(te);

- -    const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);

- -    int cpio = 1;

- -

- -    if (payloadfmt && rstreq(payloadfmt, "clon")) {

- -	cpio = 0;

- -    }

- -

-      /* transaction id used for temporary path suffix while installing */

-      rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));

-  

- @@ -921,23 +911,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-  	/* Remap file perms, owner, and group. */

-  	rc = rpmfiStat(fi, 1, &fp->sb);

-  

- +	setFileState(fs, fx);

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

-  

-  	/* Run fsm file pre hook for all plugins */

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

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

- -	fp->plugin_contents = 0;

- -	switch (rc) {

- -	case RPMRC_OK:

- -	    setFileState(fs, fx);

- -	    break;

- -	case RPMRC_PLUGIN_CONTENTS:

- -	    fp->plugin_contents = 1;

- -	    // reduce reads on cpio to this value. Could be zero if

- -	    // this is from a hard link.

- -	    rc = RPMRC_OK;

- -	    break;

- -	}

-  	fp->stage = FILE_PRE;

-      }

-      fi = rpmfiFree(fi);

- @@ -945,14 +924,24 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-      if (rc)

-  	goto exit;

-  

- -    if (cpio) {

- -	fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);

- -	if (fi == NULL) {

- -	    rc = RPMERR_BAD_MAGIC;

- -	    goto exit;

- -	}

- -    } else {

- -	fi = rpmfilesIter(files, RPMFI_ITER_FWD);

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

- +    switch(plugin_rc) {

- +	case RPMRC_PLUGIN_CONTENTS:

- +	    if(fi == NULL) {

- +		rc = RPMERR_BAD_MAGIC;

- +		goto exit;

- +	    }

- +	    rc = RPMRC_OK;

- +	    break;

- +	case RPMRC_OK:

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

- +	    if (fi == NULL) {

- +		rc = RPMERR_BAD_MAGIC;

- +		goto exit;

- +	    }

- +	    break;

- +	default:

- +	    rc = RPMRC_FAIL;

-      }

-  

-      /* Detect and create directories not explicitly in package. */

- @@ -992,14 +981,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-  	    if (fp->action == FA_TOUCH)

-  		continue;

-  

- -            if (S_ISREG(fp->sb.st_mode)) {

- +	    rpmRC plugin_rc = rpmpluginsCallFsmFileInstall(plugins, fi, fp->fpath, fp->sb.st_mode, fp->action);

- +	    if(!(plugin_rc == RPMRC_PLUGIN_CONTENTS || plugin_rc == RPMRC_OK)){

- +		rc = plugin_rc;

- +	    } else if(plugin_rc == RPMRC_PLUGIN_CONTENTS){

- +		rc = RPMRC_OK;

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

-  		if (rc == RPMERR_ENOENT) {

- -		    if(fp->plugin_contents) {

- -			rc = RPMRC_OK;

- -		    }else {

- -			rc = fsmMkfile(fi, fp, files, psm, nodigest,

- -			    &firstlink, &firstlinkfile);

- -		    }

- +		    rc = fsmMkfile(fi, fp, files, psm, nodigest,

- +				   &firstlink, &firstlinkfile);

-  		}

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

-                  if (rc == RPMERR_ENOENT) {

- @@ -1107,7 +1097,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-      rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));

-  

-  exit:

- -    h = headerFree(h);

-      fi = rpmfiFree(fi);

-      Fclose(payload);

-      free(tid);

- diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c

- index 40a3ab83f..dc1726a18 100644

- --- a/lib/rpmchecksig.c

- +++ b/lib/rpmchecksig.c

- @@ -20,6 +20,7 @@

-  #include "rpmio/rpmio_internal.h" 	/* fdSetBundle() */

-  #include "lib/rpmlead.h"

-  #include "lib/header_internal.h"

- +#include "lib/rpmextents_internal.h"

-  #include "lib/rpmvs.h"

-  

-  #include "debug.h"

- @@ -220,6 +221,71 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd,

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

- @@ -260,6 +330,29 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,

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

-  {

- @@ -304,3 +397,53 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)

-      rpmKeyringFree(keyring);

-      return res;

-  }

- +

- +struct vfydatafd_s {

- +    size_t len;

- +    char msg[BUFSIZ];

- +};

- +

- +

- +static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata)

- +{

- +    struct vfydatafd_s *vd = cbdata;

- +    char *vmsg, *msg;

- +    size_t n;

- +    size_t remainder = BUFSIZ - vd->len;

- +

- +    vmsg = rpmsinfoMsg(sinfo);

- +    rasprintf(&msg, "    %s\n", vmsg);

- +    n = rstrlcpy(vd->msg + vd->len, msg, remainder);

- +    free(vmsg);

- +    free(msg);

- +    if(n <= remainder){

- +	vd->len += n;

- +    }

- +    return 1;

- +}

- +

- +

- +int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg)

- +{

- +    rpmRC rc = RPMRC_FAIL;

- +    rpmKeyring keyring = rpmtsGetKeyring(ts, 1);

- +    rpmVSFlags vsflags = rpmtsVfyFlags(ts);

- +    int vfylevel = rpmtsVfyLevel(ts);

- +    struct vfydatafd_s vd = {.len = 0};

- +

- +    vsflags |= rpmcliVSFlags;

- +    if (rpmcliVfyLevelMask) {

- +	vfylevel &= ~rpmcliVfyLevelMask;

- +	rpmtsSetVfyLevel(ts, vfylevel);

- +    }

- +

- +    if (!rpmpkgVerifySigsFD(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) {

- +	rc = RPMRC_OK;

- +    }

- +    *msg = strdup(vd.msg);

- +    rpmsqPoll();

- +

- +    rpmKeyringFree(keyring);

- +    return rc;

- +}

- +

- diff --git a/lib/rpmcli.h b/lib/rpmcli.h

- index 906fe9951..7ff48b37a 100644

- --- a/lib/rpmcli.h

- +++ b/lib/rpmcli.h

- @@ -411,6 +411,16 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv);

-   */

-  int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv);

-  

- +

- +/** \ingroup rpmcli

- + * Verify package signatures.

- + * @param ts		transaction set

- + * @param fd		a file descriptor to verify

- + * @param msg		a string containing textual information about the verification, similar to rpmcliVerifySignatures output.

- + * @return		0 on success

- + */

- +int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg);

- +

-  #ifdef __cplusplus

-  }

-  #endif

- diff --git a/lib/rpmextents.c b/lib/rpmextents.c

- new file mode 100644

- index 000000000..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/lib/rpmplugin.h b/lib/rpmplugin.h

- index fd81aec8d..6dbbcff35 100644

- --- a/lib/rpmplugin.h

- +++ b/lib/rpmplugin.h

- @@ -60,6 +60,13 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,

-  					      const char* path,

-  					      const char *dest,

-  					      mode_t file_mode, rpmFsmOp op);

- +typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,

- +					      const char* path,

- +					      mode_t file_mode, rpmFsmOp op);

- +typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin,

- +						     FD_t payload,

- +						     rpmfiles files, rpmfi *fi);

- +

-  

-  typedef struct rpmPluginHooks_s * rpmPluginHooks;

-  struct rpmPluginHooks_s {

- @@ -80,6 +87,8 @@ struct rpmPluginHooks_s {

-      plugin_fsm_file_pre_func		fsm_file_pre;

-      plugin_fsm_file_post_func		fsm_file_post;

-      plugin_fsm_file_prepare_func	fsm_file_prepare;

- +    plugin_fsm_file_install_func	fsm_file_install;

- +    plugin_fsm_file_archive_reader_func	fsm_file_archive_reader;

-  };

-  

-  #ifdef __cplusplus

- diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c

- index 3da3097af..901af1ac5 100644

- --- a/lib/rpmplugins.c

- +++ b/lib/rpmplugins.c

- @@ -421,3 +421,74 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,

-  

-      return rc;

-  }

- +

- +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

- +				   const char *path, mode_t file_mode,

- +				   rpmFsmOp op)

- +{

- +    plugin_fsm_file_install_func hookFunc;

- +    int i;

- +    rpmRC rc = RPMRC_OK;

- +    rpmRC hook_rc;

- +

- +    for (i = 0; i < plugins->count; i++) {

- +	rpmPlugin plugin = plugins->plugins[i];

- +	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_install);

- +	if (hookFunc) {

- +	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);

- +	    if (hook_rc == RPMRC_FAIL) {

- +		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install failed\n", plugin->name);

- +		rc = RPMRC_FAIL;

- +	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {

- +		if (rc == RPMRC_PLUGIN_CONTENTS) {

- +		    /* Another plugin already said it'd handle contents. It's

- +		     * undefined how these would combine, so treat this as a

- +		     * failure condition.

- +		    */

- +		    rc = RPMRC_FAIL;

- +		} else {

- +		    /* Plugin will handle content */

- +		    rc = RPMRC_PLUGIN_CONTENTS;

- +		}

- +	    }

- +	}

- +    }

- +

- +    return rc;

- +}

- +

- +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,

- +				   rpmfiles files, rpmfi *fi)

- +{

- +    plugin_fsm_file_archive_reader_func hookFunc;

- +    int i;

- +    rpmRC rc = RPMRC_OK;

- +    rpmRC hook_rc;

- +

- +    for (i = 0; i < plugins->count; i++) {

- +	rpmPlugin plugin = plugins->plugins[i];

- +	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_archive_reader);

- +	if (hookFunc) {

- +	    hook_rc = hookFunc(plugin, payload, files, fi);

- +	    if (hook_rc == RPMRC_FAIL) {

- +		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader failed\n", plugin->name);

- +		rc = RPMRC_FAIL;

- +	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {

- +		if (rc == RPMRC_PLUGIN_CONTENTS) {

- +		    /* Another plugin already said it'd handle contents. It's

- +		     * undefined how these would combine, so treat this as a

- +		     * failure condition.

- +		    */

- +		    rc = RPMRC_FAIL;

- +		} else {

- +		    /* Plugin will handle content */

- +		    rc = RPMRC_PLUGIN_CONTENTS;

- +		}

- +	    }

- +	}

- +    }

- +

- +    return rc;

- +}

- +

- +

- diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h

- index 39762c376..88807c53c 100644

- --- a/lib/rpmplugins.h

- +++ b/lib/rpmplugins.h

- @@ -167,6 +167,23 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,

-                                     const char *path, const char *dest,

-                                     mode_t mode, rpmFsmOp op);

-  

- +/** \ingroup rpmplugins

- + * Call the fsm file install plugin hook

- + * @param plugins	plugins structure

- + * @param fi		file info iterator (or NULL)

- + * @param path		file object path

- + * @param file_mode	file object mode

- + * @param op		file operation + associated flags

- + * @return		RPMRC_OK on success, RPMRC_FAIL otherwise

- + */

- +RPM_GNUC_INTERNAL

- +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,

- +				   const char* path, mode_t file_mode,

- +				   rpmFsmOp op);

- +

- +RPM_GNUC_INTERNAL

- +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,

- +					 rpmfiles files, rpmfi *fi);

-  #ifdef __cplusplus

-  }

-  #endif

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

- index 513887604..4fc1d74d1 100644

- --- a/plugins/reflink.c

- +++ b/plugins/reflink.c

- @@ -13,6 +13,7 @@

-  #include <rpm/rpmlog.h>

-  #include "lib/rpmlib.h"

-  #include "lib/rpmplugin.h"

- +#include "lib/rpmextents_internal.h"

-  #include "lib/rpmte_internal.h"

-  #include <rpm/rpmfileutil.h>

-  #include "rpmio/rpmio_internal.h"

- @@ -28,7 +29,7 @@

-  #undef HTDATATYPE

-  #define HASHTYPE inodeIndexHash

-  #define HTKEYTYPE rpm_ino_t

- -#define HTDATATYPE int

- +#define HTDATATYPE const char *

-  #include "lib/rpmhash.H"

-  #include "lib/rpmhash.C"

-  

- @@ -40,11 +41,6 @@

-  

-  #define BUFFER_SIZE (1024 * 128)

-  

- -/* magic value at end of file (64 bits) that indicates this is a transcoded

- - * rpm.

- - */

- -#define MAGIC 3472329499408095051

- -

-  struct reflink_state_s {

-      /* Stuff that's used across rpms */

-      long fundamental_block_size;

- @@ -58,6 +54,7 @@ struct reflink_state_s {

-      FD_t fd;

-      rpmfiles files;

-      inodeIndexHash inodeIndexes;

- +    int transcoded;

-  };

-  

-  typedef struct reflink_state_s * reflink_state;

- @@ -96,51 +93,35 @@ static void reflink_cleanup(rpmPlugin plugin) {

-  }

-  

-  static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {

- +    rpmRC rc;

- +    size_t len;

- +

-      reflink_state state = rpmPluginGetData(plugin);

-      state->fd = rpmteFd(te);

-      if (state->fd == 0) {

-  	rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));

-  	return RPMRC_OK;

-      }

- +

-      rpm_loff_t current = Ftell(state->fd);

- -    uint64_t magic;

- -    if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {

- -	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));

- -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- -	    /* yes this gets a bit repetitive */

- -	    rpmlog(RPMLOG_ERR,

- -		 _("reflink: unable to seek back to original location\n"));

- -	}

- -	return RPMRC_FAIL;

- -    }

- -    size_t len = sizeof(magic);

- -    if (Fread(&magic, len, 1, state->fd) != len) {

- -	rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));

- -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- -	    rpmlog(RPMLOG_ERR,

- -		   _("reflink: unable to seek back to original location\n"));

- -	}

- -	return RPMRC_FAIL;

- -    }

- -    if (magic != MAGIC) {

- -	rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));

- -	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- -	    rpmlog(RPMLOG_ERR,

- -		   _("reflink: unable to seek back to original location\n"));

- +    rc = isTranscodedRpm(state->fd);

- +

- +    switch(rc){

- +	// Fail to parse the file, fail the plugin.

- +	case RPMRC_FAIL:

-  	    return RPMRC_FAIL;

- -	}

- -	return RPMRC_OK;

- +	// This is not a transcoded file, do nothing.

- +	case RPMRC_NOTFOUND:

- +	    return RPMRC_OK;

- +	default:

- +	    break;

-      }

-      rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));

- -    Header h = rpmteHeader(te);

- +    state->transcoded = 1;

-  

- -    /* replace/add header that main fsm.c can read */

- -    headerDel(h, RPMTAG_PAYLOADFORMAT);

- -    headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");

- -    headerFree(h);

-      state->files = rpmteFiles(te);

-      /* tail of file contains offset_table, offset_checksums then magic */

- -    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {

- +    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) {

-  	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),

-  	       state->fd);

-  	return RPMRC_FAIL;

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

-  	);

-      }

-  

- @@ -239,13 +220,13 @@ rpm_loff_t find(const unsigned char *digest, reflink_state state) {

-      return offset;

-  }

-  

- -static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,

- +static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* path,

-                                    mode_t file_mode, rpmFsmOp op)

-  {

-      struct file_clone_range fcr;

-      rpm_loff_t size;

-      int dst, rc;

- -    int *hlix;

- +    const char **hl_target = NULL;

-  

-      reflink_state state = rpmPluginGetData(plugin);

-      if (state->table == NULL) {

- @@ -262,18 +243,15 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,

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

-  	 * the address of the first match.

-  	 */

- -	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL,

- -	                           NULL)) {

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

- +				   NULL, NULL)) {

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

- -	    char *fn = rpmfilesFN(state->files, hlix[0]);

- -	    if (link(fn, path) != 0) {

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

-  		rpmlog(RPMLOG_ERR,

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

- -		       fn, path, strerror(errno));

- -		free(fn);

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

-  		return RPMRC_FAIL;

-  	    }

- -	    free(fn);

-  	    return RPMRC_PLUGIN_CONTENTS;

-  	}

-  	/* if we didn't hard link, then we'll track this inode as being

- @@ -281,7 +259,7 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,

-  	 */

-  	if (rpmfiFNlink(fi) > 1) {

-  	    /* minor optimization: only store files with more than one link */

- -	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));

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

-  	}

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

-  	mode_t old_umask = umask(0577);

- @@ -366,10 +344,21 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,

-      return RPMRC_OK;

-  }

-  

- +static rpmRC reflink_fsm_file_archive_reader(rpmPlugin plugin, FD_t payload,

- +					     rpmfiles files, rpmfi *fi) {

- +    reflink_state state = rpmPluginGetData(plugin);

- +    if(state->transcoded) {

- +	*fi = rpmfilesIter(files, RPMFI_ITER_FWD);

- +	return RPMRC_PLUGIN_CONTENTS;

- +    }

- +    return RPMRC_OK;

- +}

- +

-  struct rpmPluginHooks_s reflink_hooks = {

-      .init = reflink_init,

-      .cleanup = reflink_cleanup,

-      .psm_pre = reflink_psm_pre,

-      .psm_post = reflink_psm_post,

- -    .fsm_file_pre = reflink_fsm_file_pre,

- +    .fsm_file_install = reflink_fsm_file_install,

- +    .fsm_file_archive_reader = reflink_fsm_file_archive_reader,

-  };

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

- index c111be0a2..a326e3857 100644

- --- a/rpm2extents.c

- +++ b/rpm2extents.c

- @@ -2,7 +2,9 @@

-  

-  #include "system.h"

-  

- +#include <rpm/rpmcli.h>

-  #include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */

- +#include <rpm/rpmlog.h>

-  #include <rpm/rpmfi.h>

-  #include <rpm/rpmtag.h>

-  #include <rpm/rpmio.h>

- @@ -10,8 +12,10 @@

-  

-  #include <rpm/rpmts.h>

-  #include "lib/rpmlead.h"

- +#include "lib/rpmts.h"

-  #include "lib/signature.h"

-  #include "lib/header_internal.h"

- +#include "lib/rpmextents_internal.h"

-  #include "rpmio/rpmio_internal.h"

-  

-  #include <unistd.h>

- @@ -34,11 +38,6 @@

-  #include "lib/rpmhash.H"

-  #include "lib/rpmhash.C"

-  

- -/* magic value at end of file (64 bits) that indicates this is a transcoded

- - * rpm.

- - */

- -#define MAGIC 3472329499408095051

- -

-  struct digestoffset {

-      const unsigned char * digest;

-      rpm_loff_t pos;

- @@ -51,38 +50,47 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)

-      return (unit - (pos % unit)) % unit;

-  }

-  

- -static int digestor(

- +static struct poptOption optionsTable[] = {

- +    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,

- +    N_("Common options for all rpm modes and executables:"), NULL },

- +

- +    POPT_AUTOALIAS

- +    POPT_AUTOHELP

- +    POPT_TABLEEND

- +};

- +

- +

- +static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){

- +    int algo;

- +

- +    for (algo = 0; algo < algos_len; algo++) {

- +	fdInitDigest(fdi, algos[algo], 0);

- +    }

- +}

- +

- +static int FDWriteDigests(

-      FD_t fdi,

-      FD_t fdo,

- -    FD_t validationo,

-      uint8_t algos[],

- -    uint32_t algos_len

- -)

- +    uint32_t algos_len)

-  {

- -    ssize_t fdilength;

-      const char *filedigest, *algo_name;

-      size_t filedigest_len, len;

-      uint32_t algo_name_len, algo_digest_len;

-      int algo;

-      rpmRC rc = RPMRC_FAIL;

-  

- -    for (algo = 0; algo < algos_len; algo++) {

- -	fdInitDigest(fdi, algos[algo], 0);

- -    }

- -    fdilength = ufdCopy(fdi, fdo);

- -    if (fdilength == -1) {

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

- -	goto exit;

- -    }

- +    ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes;

-  

-      len = sizeof(fdilength);

- -    if (Fwrite(&fdilength, len, 1, validationo) != len) {

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

- @@ -93,24 +101,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;

-  	}

- @@ -120,7 +128,69 @@ static int digestor(

-      return rc;

-  }

-  

- -static rpmRC process_package(FD_t fdi, FD_t validationi)

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

- +    size_t len;

- +    rpmRC rc = RPMRC_FAIL;

- +

- +    if(rpmvsrc){

- +	fprintf(stderr, _("Error verifying package signatures\n"));

- +    }

- +

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

- +    }

- +    if (Fwrite(msg, content_len, 1, fdo) != content_len) {

- +	fprintf(stderr, _("Unable to write signature verification output %s\n"), msg);

- +	goto exit;

- +    }

- +

- +    rc = RPMRC_OK;

- +exit:

- +

- +    return rc;

- +}

- +

- +static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo,

- +	uint8_t algos[],

- +	uint32_t algos_len){

- +    int rpmvsrc;

- +    rpmRC rc = RPMRC_FAIL;

- +    char *msg = NULL;

- +    rpmts ts = rpmtsCreate();

- +

- +    rpmtsSetRootDir(ts, rpmcliRootDir);

- +

- +    FDDigestInit(fdi, algos, algos_len);

- +

- +    rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg);

- +

- +    // Write result of digest computation

- +    if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) {

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

- +}

- +

- +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 +218,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)

-      rpm_mode_t mode;

-      char *rpmio_flags = NULL, *zeros;

-      const unsigned char *digest;

- -    rpm_loff_t pos, size, pad, validation_pos;

- +    rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos;

-      uint32_t offset_ix = 0;

-      size_t len;

-      int next = 0;

- @@ -253,6 +323,16 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)

-      qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),

-  	  digestoffsetCmp);

-  

- +    validation_pos = pos;

- +    ssize_t validation_len = ufdCopy(validationi, fdo);

- +    if (validation_len == -1) {

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

- @@ -278,17 +358,18 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)

-  	    goto exit;

-  	}

-      }

- -    validation_pos = (

- -	pos + sizeof(offset_ix) + sizeof(diglen) +

- +    digest_pos = (

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

-  	offset_ix * (diglen + sizeof(rpm_loff_t))

-      );

-  

- -    ssize_t validation_len = ufdCopy(validationi, fdo);

- -    if (validation_len == -1) {

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

- +    if (digest_len == -1) {

-  	fprintf(stderr, _("digest table ufdCopy failed\n"));

-  	rc = RPMRC_FAIL;

-  	goto exit;

-      }

- +

-      /* add more padding so the last file can be cloned. It doesn't matter that

-       * the table and validation etc are in this space. In fact, it's pretty

-       * efficient if it is.

- @@ -302,17 +383,22 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)

-  	goto exit;

-      }

-      zeros = _free(zeros);

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

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

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

-  	fprintf(stderr, _("Unable to write offset of validation table\n"));

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

- @@ -327,104 +413,200 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)

-      return rc;

-  }

-  

- -int main(int argc, char *argv[]) {

- -    rpmRC rc;

- -    int cprc = 0;

- -    uint8_t algos[argc - 1];

- -    int mainpipefd[2];

- -    int metapipefd[2];

- -    pid_t cpid, w;

- -    int wstatus;

- +static off_t ufdTee(FD_t sfd, FD_t *fds, int len)

- +{

- +    char buf[BUFSIZ];

- +    ssize_t rdbytes, wrbytes;

- +    off_t total = 0;

- +

- +    while (1) {

- +	rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);

- +

- +	if (rdbytes > 0) {

- +	    for(int i=0; i < len; i++) {

- +		wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]);

- +		if (wrbytes != rdbytes) {

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

- +	}

- +    }

-  

- -    xsetprogname(argv[0]);	/* Portability call -- see system.h */

- -    rpmReadConfigFiles(NULL, NULL);

- +    return total;

- +}

-  

- -    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) {

- -	fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);

- -	exit(EXIT_FAILURE);

- -    }

- +static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {

- +    rpmRC rc = RPMRC_FAIL;

- +    off_t offt = -1;

- +    // tee-ed stdin

- +    int processorpipefd[2];

- +    int validatorpipefd[2];

- +    // metadata

- +    int meta_digestpipefd[2];

- +    int meta_rpmsignpipefd[2];

- +

- +    pid_t cpids[2], w;

- +    int wstatus;

- +    FD_t fds[2];

-  

- -    if (argc == 1) {

- -	fprintf(stderr,

- -		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));

- -	exit(EXIT_FAILURE);

- +     if (pipe(processorpipefd) == -1) {

- +	fprintf(stderr, _("Processor pipe failure\n"));

- +	return RPMRC_FAIL;

-      }

-  

- -    for (int x = 0; x < (argc - 1); x++) {

- -	if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)

- -	{

- -	    fprintf(stderr,

- -		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),

- -		    argv[x + 1]);

- -	    exit(EXIT_FAILURE);

- -	}

- +    if (pipe(validatorpipefd) == -1) {

- +	fprintf(stderr, _("Validator pipe failure\n"));

- +	return RPMRC_FAIL;

-      }

-  

- -

- -    if (pipe(mainpipefd) == -1) {

- -	fprintf(stderr, _("Main pipe failure\n"));

- -	exit(EXIT_FAILURE);

- +    if (pipe(meta_digestpipefd) == -1) {

- +	fprintf(stderr, _("Meta digest pipe failure\n"));

- +	return RPMRC_FAIL;

-      }

- -    if (pipe(metapipefd) == -1) {

- -	fprintf(stderr, _("Meta pipe failure\n"));

- -	exit(EXIT_FAILURE);

- +

- +    if (pipe(meta_rpmsignpipefd) == -1) {

- +	fprintf(stderr, _("Meta rpm signature pipe failure\n"));

- +	return RPMRC_FAIL;

-      }

- -    cpid = fork();

- -    if (cpid == 0) {

- -	/* child: digestor */

- -	close(mainpipefd[0]);

- -	close(metapipefd[0]);

- -	FD_t fdi = fdDup(STDIN_FILENO);

- -	FD_t fdo = fdDup(mainpipefd[1]);

- -	FD_t validationo = fdDup(metapipefd[1]);

- -	rc = digestor(fdi, fdo, validationo, algos, argc - 1);

- -	Fclose(validationo);

- -	Fclose(fdo);

- +

- +    cpids[0] = fork();

- +    if (cpids[0] == 0) {

- +	/* child: validator */

- +	close(processorpipefd[0]);

- +	close(processorpipefd[1]);

- +	close(validatorpipefd[1]);

- +	close(meta_digestpipefd[0]);

- +	close(meta_rpmsignpipefd[0]);

- +	FD_t fdi = fdDup(validatorpipefd[0]);

- +	FD_t digesto = fdDup(meta_digestpipefd[1]);

- +	FD_t sigo = fdDup(meta_rpmsignpipefd[1]);

- +	close(meta_digestpipefd[1]);

- +	close(meta_rpmsignpipefd[1]);

- +	rc = validator(fdi, digesto, sigo, algos, algos_len);

- +	if(rc != RPMRC_OK) {

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

- +	}

-  	Fclose(fdi);

- +	Fclose(digesto);

- +	Fclose(sigo);

- +	if (rc != RPMRC_OK) {

- +	    exit(EXIT_FAILURE);

- +	}

- +	exit(EXIT_SUCCESS);

-      } else {

-  	/* parent: main program */

- -	close(mainpipefd[1]);

- -	close(metapipefd[1]);

- -	FD_t fdi = fdDup(mainpipefd[0]);

- -	FD_t validationi = fdDup(metapipefd[0]);

- -	rc = process_package(fdi, validationi);

- -	Fclose(validationi);

- -	/* fdi is normally closed through the stacked file gzdi in the

- -	 * function.

- -	 * Wait for child process (digestor for stdin) to complete.

- -	 */

- -	if (rc != RPMRC_OK) {

- -	    if (kill(cpid, SIGTERM) != 0) {

- -		fprintf(stderr,

- -		        _("Failed to kill digest process when main process failed: %s\n"),

- -			strerror(errno));

- +	cpids[1] = fork();

- +	if (cpids[1] == 0) {

- +	    /* child: process_package */

- +	    close(validatorpipefd[0]);

- +	    close(validatorpipefd[1]);

- +	    close(processorpipefd[1]);

- +	    close(meta_digestpipefd[1]);

- +	    close(meta_rpmsignpipefd[1]);

- +	    FD_t fdi = fdDup(processorpipefd[0]);

- +	    close(processorpipefd[0]);

- +	    FD_t sigi = fdDup(meta_rpmsignpipefd[0]);

- +	    close(meta_rpmsignpipefd[0]);

- +	    FD_t digestori = fdDup(meta_digestpipefd[0]);

- +	    close(meta_digestpipefd[0]);

- +

- +	    rc = process_package(fdi, digestori, sigi);

- +	    if(rc != RPMRC_OK) {

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

-  	    }

- -	}

- -	w = waitpid(cpid, &wstatus, 0);

- -	if (w == -1) {

- -	    fprintf(stderr, _("waitpid failed\n"));

- -	    cprc = EXIT_FAILURE;

- -	} else if (WIFEXITED(wstatus)) {

- -	    cprc = WEXITSTATUS(wstatus);

- -	    if (cprc != 0) {

- -		fprintf(stderr,

- -			_("Digest process non-zero exit code %d\n"),

- -			cprc);

- +	    Fclose(digestori);

- +	    Fclose(sigi);

- +	    /* fdi is normally closed through the stacked file gzdi in the

- +	     * function

- +	     */

- +

- +	    if (rc != RPMRC_OK) {

- +		exit(EXIT_FAILURE);

-  	    }

- -	} else if (WIFSIGNALED(wstatus)) {

- -	    fprintf(stderr,

- -		    _("Digest process was terminated with a signal: %d\n"),

- -		    WTERMSIG(wstatus));

- -	    cprc = EXIT_FAILURE;

- +	    exit(EXIT_SUCCESS);

- +

- +

-  	} else {

- -	    /* Don't think this can happen, but covering all bases */

- -	    fprintf(stderr, _("Unhandled circumstance in waitpid\n"));

- -	    cprc = EXIT_FAILURE;

- +	    /* Actual parent. Read from fdi and write to both processes */

- +	    close(processorpipefd[0]);

- +	    close(validatorpipefd[0]);

- +	    fds[0] = fdDup(processorpipefd[1]);

- +	    fds[1] = fdDup(validatorpipefd[1]);

- +	    close(validatorpipefd[1]);

- +	    close(processorpipefd[1]);

- +	    close(meta_digestpipefd[0]);

- +	    close(meta_digestpipefd[1]);

- +	    close(meta_rpmsignpipefd[0]);

- +	    close(meta_rpmsignpipefd[1]);

- +

- +	    rc = RPMRC_OK;

- +	    offt = ufdTee(fdi, fds, 2);

- +	    if(offt == -1){

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

- +	    }

-  	}

- -	if (cprc != EXIT_SUCCESS) {

- -	    rc = RPMRC_FAIL;

- +    }

- +

- +    return rc;

- +}

- +

- +int main(int argc, char *argv[]) {

- +    rpmRC rc;

- +    poptContext optCon = NULL;

- +    const char **args = NULL;

- +    int nb_algos = 0;

- +

- +    xsetprogname(argv[0]);	/* Portability call -- see system.h */

- +    rpmReadConfigFiles(NULL, NULL);

- +    optCon = rpmcliInit(argc, argv, optionsTable);

- +    poptSetOtherOptionHelp(optCon, "[OPTIONS]* <DIGESTALGO>");

- +

- +    if (poptPeekArg(optCon) == NULL) {

- +	fprintf(stderr,

- +		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));

- +	poptPrintUsage(optCon, stderr, 0);

- +	exit(EXIT_FAILURE);

- +    }

- +

- +    args = poptGetArgs(optCon);

- +

- +    for (nb_algos=0; args[nb_algos]; nb_algos++);

- +    uint8_t algos[nb_algos];

- +    for (int x = 0; x < nb_algos; x++) {

- +	if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0)

- +	{

- +	    fprintf(stderr,

- +		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),

- +		    args[x]);

- +	    exit(EXIT_FAILURE);

-  	}

-      }

- +

- +    FD_t fdi = fdDup(STDIN_FILENO);

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

-      if (rc != RPMRC_OK) {

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

-  	return EXIT_FAILURE;

- diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump

- new file mode 100755

- index 000000000..596a59a49

- --- /dev/null

- +++ b/scripts/rpm2extents_dump

- @@ -0,0 +1,94 @@

- +#!/usr/bin/env python3

- +

- +import argparse

- +import binascii

- +import os

- +import struct

- +import sys

- +

- +MAGIC_SIZE = 8

- +MAGIC_STR = b'KWTSH100'

- +

- +POS_SIZE = 8

- +

- +def keep_position(func):

- +    def wrapper(*args, **kwargs):

- +        curr = args[0].tell()

- +        res = func(*args, **kwargs)

- +        f.seek(curr, os.SEEK_SET)

- +        return res

- +    return wrapper

- +

- +def read_validation_digest(f, validation_offset):

- +	digests = []

- +    # validation

- +	f.seek(validation_offset, os.SEEK_SET)

- +	val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4))

- +	for i in range(val_digests_num):

- +		algo_name_len, digest_len = struct.unpack('=II', f.read(8))

- +		algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len))

- +		digests.append((algo_name, binascii.hexlify(digest)))

- +	return digests

- +

- +

- +def read_digests_table(f, digest_offset):

- +	digests = []

- +    # validation

- +	f.seek(digest_offset, os.SEEK_SET)

- +	table_len, digest_len = struct.unpack('=II', f.read(8))

- +

- +	for i in range(table_len):

- +		digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8))

- +		digests.append((pos, binascii.hexlify(digest)))

- +	return digests

- +

- +def read_signature_output(f, signature_offset):

- +    f.seek(signature_offset, os.SEEK_SET)

- +    signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12))

- +    return signature_rc, f.read(signature_output_len)

- +

- +@keep_position

- +def parse_file(f):

- +	digests = []

- +	pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END)

- +	signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE))

- +

- +	validation_digests = read_validation_digest(f, validation_offset)

- +	digests_table = read_digests_table(f, digest_offset)

- +	signature_ouput = read_signature_output(f, signature_offset)

- +

- +	return validation_digests, digests_table, signature_ouput

- +

- +@keep_position

- +def is_transcoded(f):

- +    f.seek(-MAGIC_SIZE, os.SEEK_END)

- +    magic = f.read(MAGIC_SIZE)

- +    return magic == MAGIC_STR

- +

- +def arg_parse():

- +    parser = argparse.ArgumentParser()

- +    parser.add_argument('--dump-signature', action='store_true')

- +    parser.add_argument('--dump-file-digest-table', action='store_true')

- +    parser.add_argument('--dump-digests', action='store_true')

- +    parser.add_argument('file')

- +

- +    return parser.parse_args()

- +

- +if __name__ == '__main__':

- +    args = arg_parse()

- +    f = open(args.file, 'rb')

- +    if not is_transcoded(f):

- +        sys.exit(1)

- +

- +    validation_digests, digests_table, signature_output = parse_file(f)

- +    if(args.dump_file_digest_table):

- +        for digest in digests_table:

- +            print(f"FileDigest {hex(digest[0])}: {digest[1]}")

- +

- +    if(args.dump_digests):

- +        for validation_digest in validation_digests:

- +            print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}")

- +

- +    if(args.dump_signature):

- +        print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}")

- +

- diff --git a/tests/Makefile.am b/tests/Makefile.am

- index f78e17c3e..fc8a24a5e 100644

- --- a/tests/Makefile.am

- +++ b/tests/Makefile.am

- @@ -36,6 +36,7 @@ TESTSUITE_AT += rpmio.at

-  TESTSUITE_AT += rpmio.at

-  TESTSUITE_AT += rpmorder.at

-  TESTSUITE_AT += rpmvfylevel.at

- +TESTSUITE_AT += rpm2extents.at

-  EXTRA_DIST += $(TESTSUITE_AT)

-  

-  ## testsuite data

- diff --git a/tests/atlocal.in b/tests/atlocal.in

- index c3189d327..a110564e2 100644

- --- a/tests/atlocal.in

- +++ b/tests/atlocal.in

- @@ -29,6 +29,19 @@ else

-  

-  RPM_XFAIL=${RPM_XFAIL-1}

-  

- +FSTYPE=$(stat -f -c %T /)

- +REFLINKABLE_FS=("xfs" "brtfs")

- +

- +REFLINK_DISABLED=true;

- +for item in "${REFLINKABLE_FS[@]}"

- +do

- +    if test "${FSTYPE}" = "${item}"

- +    then

- +	REFLINK_DISABLED=false;

- +	break

- +    fi

- +done

- +

-  function run()

-  {

-      "$@" --define "_tmppath ${RPMTEST}/tmp" --define "_topdir ${TOPDIR}" --dbpath="${RPMTEST}/var/lib/rpm/"

- @@ -40,6 +53,14 @@ function runroot()

-      )

-  }

-  

- +function runroot_plugins()

- +{

- +    (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \

- +     MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns

- +    )

- +}

- +

- +

-  function runroot_other()

-  {

-      (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \

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

- new file mode 100644

- index 000000000..29165c1b9

- --- /dev/null

- +++ b/tests/rpm2extents.at

- @@ -0,0 +1,141 @@

- +#    rpm2extents.at: Some very basic checks

- +#

- +#    Copyright (C) 2022  Manu Bretelle <chantr4@gmail.com>

- +#

- +#    This program is free software; you can redistribute it and/or modify

- +#    it under the terms of the GNU General Public License as published by

- +#    the Free Software Foundation; either version 2 of the License, or

- +#    (at your option) any later version.

- +#

- +#    This program is distributed in the hope that it will be useful,

- +#    but WITHOUT ANY WARRANTY; without even the implied warranty of

- +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

- +#    GNU General Public License for more details.

- +#

- +#    You should have received a copy of the GNU General Public License

- +#    along with this program; if not, write to the Free Software

- +#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

- +

- +AT_BANNER([rpm2extents tests])

- +

- +# ------------------------------

- +

- +# check that transcoder write magic at the end

- +AT_SETUP([rpm2extents magic])

- +AT_KEYWORDS([rpm2extents])

- +AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | tail -c8],

- +[0],

- +[KWTSH100],

- +[ignore])

- +AT_CLEANUP

- +

- +# Check that transcoder writes checksig return code and content.

- +#

- +AT_SETUP([rpm2extents signature])

- +AT_KEYWORDS([rpm2extents])

- +AT_CHECK([

- +RPMDB_INIT

- +

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null

- +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm

- +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm

- +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm

- +],

- +[0],

- +[RPMSignOutput RC 2

- +RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

- +    Header SHA256 digest: OK

- +    Header SHA1 digest: OK

- +    Payload SHA256 digest: OK

- +    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

- +    MD5 digest: OK

- +

- +RPMSignOutput RC 0

- +RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

- +    Header SHA256 digest: OK

- +    Header SHA1 digest: OK

- +    Payload SHA256 digest: OK

- +    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

- +    MD5 digest: OK

- +

- +],

- +[])

- +AT_CLEANUP

- +

- +AT_SETUP([rpm2extents signature verification])

- +AT_KEYWORDS([rpm2extents])

- +AT_CHECK([

- +RPMDB_INIT

- +

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null

- +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

- +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm

- +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?

- +],

- +[0],

- +[/tmp/hello-2.0-1.x86_64-signed.rpm:

- +    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

- +    Header SHA256 digest: OK

- +    Header SHA1 digest: OK

- +    Payload SHA256 digest: OK

- +    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY

- +    MD5 digest: OK

- +1

- +/tmp/hello-2.0-1.x86_64-signed.rpm:

- +    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

- +    Header SHA256 digest: OK

- +    Header SHA1 digest: OK

- +    Payload SHA256 digest: OK

- +    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK

- +    MD5 digest: OK

- +0

- +],

- +[])

- +AT_CLEANUP

- +

- +AT_SETUP([rpm2extents install package])

- +AT_KEYWORDS([rpm2extents reflink])

- +AT_SKIP_IF([$REFLINK_DISABLED])

- +AT_CHECK([

- +RPMDB_INIT

- +

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null

- +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm

- +],

- +[0],

- +[],

- +[])

- +AT_CLEANUP

- +

- +AT_SETUP([reflink hardlink package])

- +AT_KEYWORDS([reflink hardlink])

- +AT_SKIP_IF([$REFLINK_DISABLED])

- +AT_CHECK([

- +RPMDB_INIT

- +

- +PKG=hlinktest-1.0-1.noarch.rpm

- +runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null

- +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}

- +],

- +[0],

- +[],

- +[])

- +AT_CLEANUP

- +

- +AT_SETUP([rpm2extents install package])

- +AT_KEYWORDS([rpm2extents reflink])

- +AT_SKIP_IF([$REFLINK_DISABLED])

- +AT_CHECK([

- +RPMDB_INIT

- +

- +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null

- +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm

- +],

- +[0],

- +[],

- +[])

- +AT_CLEANUP

- +

- +

- diff --git a/tests/rpmtests.at b/tests/rpmtests.at

- index a1adab8e0..205fed6a3 100644

- --- a/tests/rpmtests.at

- +++ b/tests/rpmtests.at

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

-  m4_include([rpmmacro.at])

-  m4_include([rpmpython.at])

-  m4_include([rpmdepmatch.at])

- +m4_include([rpm2extents.at])

file removed
-1176
@@ -1,1176 +0,0 @@ 

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

- index e5c75d7b4..288668819 100644

- --- a/Makefile.am

- +++ b/Makefile.am

- @@ -99,7 +99,7 @@ pkginclude_HEADERS += build/rpmfc.h

-  pkginclude_HEADERS += build/rpmspec.h

-  

-  

- -bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec

- +bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec rpm2extents

-  if WITH_ARCHIVE

-  bin_PROGRAMS += 	rpm2archive 

-  endif

- @@ -154,6 +154,10 @@ rpm2cpio_SOURCES =	rpm2cpio.c debug.h system.h

-  rpm2cpio_LDADD =	lib/librpm.la rpmio/librpmio.la

-  rpm2cpio_LDADD +=	@WITH_POPT_LIB@

-  

- +rpm2extents_SOURCES =	rpm2extents.c debug.h system.h

- +rpm2extents_LDADD =	lib/librpm.la rpmio/librpmio.la

- +rpm2extents_LDADD +=	@WITH_POPT_LIB@

- +

-  rpm2archive_SOURCES =	rpm2archive.c debug.h system.h

-  rpm2archive_LDADD =	lib/librpm.la rpmio/librpmio.la

-  rpm2archive_LDADD +=	@WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@

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

- index 30234df3d..8998afcd3 100644

- --- a/lib/depends.c

- +++ b/lib/depends.c

- @@ -81,6 +81,8 @@ static rpmRC headerCheckPayloadFormat(Header h) {

-       */

-      if (!payloadfmt) return rc;

-  

- +    if (rstreq(payloadfmt, "clon")) return rc;

- +

-      if (!rstreq(payloadfmt, "cpio")) {

-          char *nevra = headerGetAsString(h, RPMTAG_NEVRA);

-          if (payloadfmt && rstreq(payloadfmt, "drpm")) {

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

- index 935a0a5c6..feda3750c 100644

- --- a/lib/fsm.c

- +++ b/lib/fsm.c

- @@ -19,6 +19,7 @@

-  

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

-  #include "lib/fsm.h"

- +#include "lib/rpmlib.h"

-  #include "lib/rpmte_internal.h"	/* XXX rpmfs */

-  #include "lib/rpmplugins.h"	/* rpm plugins hooks */

-  #include "lib/rpmug.h"

- @@ -52,6 +53,7 @@ struct filedata_s {

-      int stage;

-      int setmeta;

-      int skip;

- +    int plugin_contents;

-      rpmFileAction action;

-      const char *suffix;

-      char *fpath;

- @@ -891,6 +893,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

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

-      struct filedata_s *firstlink = NULL;

-  

- +    Header h = rpmteHeader(te);

- +    const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);

- +    int cpio = 1;

- +

- +    if (payloadfmt && rstreq(payloadfmt, "clon")) {

- +	cpio = 0;

- +    }

- +

-      /* transaction id used for temporary path suffix while installing */

-      rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));

-  

- @@ -911,12 +921,23 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-  	/* Remap file perms, owner, and group. */

-  	rc = rpmfiStat(fi, 1, &fp->sb);

-  

- -	setFileState(fs, fx);

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

-  

-  	/* Run fsm file pre hook for all plugins */

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

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

- +	fp->plugin_contents = 0;

- +	switch (rc) {

- +	case RPMRC_OK:

- +	    setFileState(fs, fx);

- +	    break;

- +	case RPMRC_PLUGIN_CONTENTS:

- +	    fp->plugin_contents = 1;

- +	    // reduce reads on cpio to this value. Could be zero if

- +	    // this is from a hard link.

- +	    rc = RPMRC_OK;

- +	    break;

- +	}

-  	fp->stage = FILE_PRE;

-      }

-      fi = rpmfiFree(fi);

- @@ -924,10 +945,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-      if (rc)

-  	goto exit;

-  

- -    fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);

- -    if (fi == NULL) {

- -        rc = RPMERR_BAD_MAGIC;

- -        goto exit;

- +    if (cpio) {

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

- +	if (fi == NULL) {

- +	    rc = RPMERR_BAD_MAGIC;

- +	    goto exit;

- +	}

- +    } else {

- +	fi = rpmfilesIter(files, RPMFI_ITER_FWD);

-      }

-  

-      /* Detect and create directories not explicitly in package. */

- @@ -969,8 +994,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-  

-              if (S_ISREG(fp->sb.st_mode)) {

-  		if (rc == RPMERR_ENOENT) {

- -		    rc = fsmMkfile(fi, fp, files, psm, nodigest,

- -				   &firstlink, &firstlinkfile);

- +		    if(fp->plugin_contents) {

- +			rc = RPMRC_OK;

- +		    }else {

- +			rc = fsmMkfile(fi, fp, files, psm, nodigest,

- +			    &firstlink, &firstlinkfile);

- +		    }

-  		}

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

-                  if (rc == RPMERR_ENOENT) {

- @@ -1078,6 +1107,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,

-      rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));

-  

-  exit:

- +    h = headerFree(h);

-      fi = rpmfiFree(fi);

-      Fclose(payload);

-      free(tid);

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

- index 281275029..90bd0d8a7 100644

- --- a/lib/package.c

- +++ b/lib/package.c

- @@ -404,5 +404,45 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)

-      return rc;

-  }

-  

- +rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp)

- +{

- +    char *msg = NULL;

- +    hdrblob sigblob = hdrblobCreate();

- +    hdrblob blob = hdrblobCreate();

- +    Header h = NULL;

- +    Header sigh = NULL;

- +

- +    rpmRC rc = rpmLeadRead(fd, &msg);

- +    if (rc != RPMRC_OK)

- +	goto exit;

- +

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

- +    if (rc != RPMRC_OK)

- +	goto exit;

- +

- +    rc = hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, blob, &msg);

- +    if (rc != RPMRC_OK)

- +	goto exit;

- +

- +    rc = hdrblobImport(sigblob, 0, &sigh, &msg);

- +    if (rc)

- +	goto exit;

-  

- +    rc = hdrblobImport(blob, 0, &h, &msg);

- +    if (rc)

- +	goto exit;

-  

- +    *sigp = headerLink(sigh);

- +    *hdrp = headerLink(h);

- +

- +exit:

- +    if (rc != RPMRC_OK && msg)

- +	rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg);

- +    hdrblobFree(sigblob);

- +    hdrblobFree(blob);

- +    headerFree(sigh);

- +    headerFree(h);

- +    free(msg);

- +

- +    return rc;

- +}

- diff --git a/lib/rpmlib.h b/lib/rpmlib.h

- index 0879d04e5..a09ba0daf 100644

- --- a/lib/rpmlib.h

- +++ b/lib/rpmlib.h

- @@ -155,6 +155,15 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg);

-  rpmRC rpmReadPackageFile(rpmts ts, FD_t fd,

-  		const char * fn, Header * hdrp);

-  

- +/** \ingroup header

- + * Return package signature, header from file handle, no verification.

- + * @param fd		file handle

- + * @param[out] sigp		address of header (or NULL)

- + * @param[out] hdrp		address of header (or NULL)

- + * @return		RPMRC_OK on success

- + */

- +rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp);

- +

-  /** \ingroup rpmtrans

-   * Install source package.

-   * @param ts		transaction set

- diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c

- index 62d75c4cf..3da3097af 100644

- --- a/lib/rpmplugins.c

- +++ b/lib/rpmplugins.c

- @@ -356,13 +356,28 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path,

-      plugin_fsm_file_pre_func hookFunc;

-      int i;

-      rpmRC rc = RPMRC_OK;

- +    rpmRC hook_rc;

-  

-      for (i = 0; i < plugins->count; i++) {

-  	rpmPlugin plugin = plugins->plugins[i];

-  	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre);

- -	if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) {

- -	    rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);

- -	    rc = RPMRC_FAIL;

- +	if (hookFunc) {

- +	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);

- +	    if (hook_rc == RPMRC_FAIL) {

- +		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);

- +		rc = RPMRC_FAIL;

- +	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {

- +		if (rc == RPMRC_PLUGIN_CONTENTS) {

- +		    /* Another plugin already said it'd handle contents. It's

- +		     * undefined how these would combine, so treat this as a

- +		     * failure condition.

- +		    */

- +		    rc = RPMRC_FAIL;

- +		} else {

- +		    /* Plugin will handle content */

- +		    rc = RPMRC_PLUGIN_CONTENTS;

- +		}

- +	    }

-  	}

-      }

-  

- diff --git a/lib/rpmte.c b/lib/rpmte.c

- index 3663604e7..d43dc41ad 100644

- --- a/lib/rpmte.c

- +++ b/lib/rpmte.c

- @@ -423,6 +423,11 @@ FD_t rpmteSetFd(rpmte te, FD_t fd)

-      return NULL;

-  }

-  

- +FD_t rpmteFd(rpmte te)

- +{

- +    return (te != NULL ? te->fd : NULL);

- +}

- +

-  fnpyKey rpmteKey(rpmte te)

-  {

-      return (te != NULL ? te->key : NULL);

- diff --git a/lib/rpmte.h b/lib/rpmte.h

- index 81acf7a19..6fc0a9f91 100644

- --- a/lib/rpmte.h

- +++ b/lib/rpmte.h

- @@ -209,6 +209,8 @@ const char * rpmteNEVR(rpmte te);

-   */

-  const char * rpmteNEVRA(rpmte te);

-  

- +FD_t rpmteFd(rpmte te);

- +

-  /** \ingroup rpmte

-   * Retrieve key from transaction element.

-   * @param te		transaction element

- diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h

- index e8e69b506..af2611e9e 100644

- --- a/lib/rpmtypes.h

- +++ b/lib/rpmtypes.h

- @@ -106,7 +106,8 @@ typedef	enum rpmRC_e {

-      RPMRC_NOTFOUND	= 1,	/*!< Generic not found code. */

-      RPMRC_FAIL		= 2,	/*!< Generic failure code. */

-      RPMRC_NOTTRUSTED	= 3,	/*!< Signature is OK, but key is not trusted. */

- -    RPMRC_NOKEY		= 4	/*!< Public key is unavailable. */

- +    RPMRC_NOKEY		= 4,	/*!< Public key is unavailable. */

- +    RPMRC_PLUGIN_CONTENTS = 5     /*!< fsm_file_pre plugin is handling content */

-  } rpmRC;

-  

-  #ifdef __cplusplus

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

- index e90cefa9a..363252b0f 100644

- --- a/macros.in

- +++ b/macros.in

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

-  

-  # Transaction plugin macros

-  %__plugindir		%{_libdir}/rpm-plugins

- +%__transaction_reflink		%{__plugindir}/reflink.so

-  %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so

-  %__transaction_selinux		%{__plugindir}/selinux.so

-  %__transaction_syslog		%{__plugindir}/syslog.so

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

- index 3a929d0ce..ad0d3bce7 100644

- --- a/plugins/Makefile.am

- +++ b/plugins/Makefile.am

- @@ -42,6 +42,10 @@ prioreset_la_SOURCES = prioreset.c

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

-  plugins_LTLIBRARIES += prioreset.la

-  

- +reflink_la_SOURCES = reflink.c

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

- +plugins_LTLIBRARIES += reflink.la

- +

-  syslog_la_SOURCES = syslog.c

-  syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la

-  plugins_LTLIBRARIES += syslog.la

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

- new file mode 100644

- index 000000000..513887604

- --- /dev/null

- +++ b/plugins/reflink.c

- @@ -0,0 +1,375 @@

- +#include "system.h"

- +

- +#include <errno.h>

- +#include <sys/resource.h>

- +#include <unistd.h>

- +#include <sys/types.h>

- +#include <sys/stat.h>

- +#include <fcntl.h>

- +#if defined(__linux__)

- +#include <linux/fs.h>        /* For FICLONE */

- +#endif

- +

- +#include <rpm/rpmlog.h>

- +#include "lib/rpmlib.h"

- +#include "lib/rpmplugin.h"

- +#include "lib/rpmte_internal.h"

- +#include <rpm/rpmfileutil.h>

- +#include "rpmio/rpmio_internal.h"

- +

- +

- +#include "debug.h"

- +

- +#include <sys/ioctl.h>

- +

- +/* use hash table to remember inode -> ix (for rpmfilesFN(ix)) lookups */

- +#undef HASHTYPE

- +#undef HTKEYTYPE

- +#undef HTDATATYPE

- +#define HASHTYPE inodeIndexHash

- +#define HTKEYTYPE rpm_ino_t

- +#define HTDATATYPE int

- +#include "lib/rpmhash.H"

- +#include "lib/rpmhash.C"

- +

- +/* We use this in find to indicate a key wasn't found. This is an

- + * unrecoverable error, but we can at least show a decent error. 0 is never a

- + * valid offset because it's the offset of the start of the file.

- + */

- +#define NOT_FOUND 0

- +

- +#define BUFFER_SIZE (1024 * 128)

- +

- +/* magic value at end of file (64 bits) that indicates this is a transcoded

- + * rpm.

- + */

- +#define MAGIC 3472329499408095051

- +

- +struct reflink_state_s {

- +    /* Stuff that's used across rpms */

- +    long fundamental_block_size;

- +    char *buffer;

- +

- +    /* stuff that's used/updated per psm */

- +    uint32_t keys, keysize;

- +

- +    /* table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) */

- +    unsigned char *table;

- +    FD_t fd;

- +    rpmfiles files;

- +    inodeIndexHash inodeIndexes;

- +};

- +

- +typedef struct reflink_state_s * reflink_state;

- +

- +static int inodeCmp(rpm_ino_t a, rpm_ino_t b)

- +{

- +    return (a != b);

- +}

- +

- +static unsigned int inodeId(rpm_ino_t a)

- +{

- +    /* rpm_ino_t is uint32_t so maps safely to unsigned int */

- +    return (unsigned int)a;

- +}

- +

- +static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) {

- +    reflink_state state = rcalloc(1, sizeof(struct reflink_state_s));

- +

- +    /* IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset

- +     * and length arguments to be aligned to the fundamental block size.

- +     *

- +     * The value of "fundamental block size" is directly related to the

- +     * system's page size, so we should use that.

- +     */

- +    state->fundamental_block_size = sysconf(_SC_PAGESIZE);

- +    state->buffer = rcalloc(1, BUFFER_SIZE);

- +    rpmPluginSetData(plugin, state);

- +

- +    return RPMRC_OK;

- +}

- +

- +static void reflink_cleanup(rpmPlugin plugin) {

- +    reflink_state state = rpmPluginGetData(plugin);

- +    free(state->buffer);

- +    free(state);

- +}

- +

- +static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {

- +    reflink_state state = rpmPluginGetData(plugin);

- +    state->fd = rpmteFd(te);

- +    if (state->fd == 0) {

- +	rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));

- +	return RPMRC_OK;

- +    }

- +    rpm_loff_t current = Ftell(state->fd);

- +    uint64_t magic;

- +    if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {

- +	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));

- +	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- +	    /* yes this gets a bit repetitive */

- +	    rpmlog(RPMLOG_ERR,

- +		 _("reflink: unable to seek back to original location\n"));

- +	}

- +	return RPMRC_FAIL;

- +    }

- +    size_t len = sizeof(magic);

- +    if (Fread(&magic, len, 1, state->fd) != len) {

- +	rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));

- +	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- +	    rpmlog(RPMLOG_ERR,

- +		   _("reflink: unable to seek back to original location\n"));

- +	}

- +	return RPMRC_FAIL;

- +    }

- +    if (magic != MAGIC) {

- +	rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));

- +	if (Fseek(state->fd, current, SEEK_SET) < 0) {

- +	    rpmlog(RPMLOG_ERR,

- +		   _("reflink: unable to seek back to original location\n"));

- +	    return RPMRC_FAIL;

- +	}

- +	return RPMRC_OK;

- +    }

- +    rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));

- +    Header h = rpmteHeader(te);

- +

- +    /* replace/add header that main fsm.c can read */

- +    headerDel(h, RPMTAG_PAYLOADFORMAT);

- +    headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");

- +    headerFree(h);

- +    state->files = rpmteFiles(te);

- +    /* tail of file contains offset_table, offset_checksums then magic */

- +    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {

- +	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),

- +	       state->fd);

- +	return RPMRC_FAIL;

- +    }

- +    rpm_loff_t table_start;

- +    len = sizeof(table_start);

- +    if (Fread(&table_start, len, 1, state->fd) != len) {

- +	rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n"));

- +	return RPMRC_FAIL;

- +    }

- +    if (Fseek(state->fd, table_start, SEEK_SET) < 0) {

- +	rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n"));

- +	return RPMRC_FAIL;

- +    }

- +    len = sizeof(state->keys);

- +    if (Fread(&state->keys, len, 1, state->fd) != len) {

- +	rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n"));

- +	return RPMRC_FAIL;

- +    }

- +    len = sizeof(state->keysize);

- +    if (Fread(&state->keysize, len, 1, state->fd) != len) {

- +	rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n"));

- +	return RPMRC_FAIL;

- +    }

- +    rpmlog(

- +	RPMLOG_DEBUG,

- +	_("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"),

- +	table_start, state->keys, state->keysize

- +    );

- +    /* now get digest table if there is a reason to have one. */

- +    if (state->keys == 0 || state->keysize == 0) {

- +	/* no files (or no digests(!)) */

- +	state->table = NULL;

- +    } else {

- +	int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t));

- +	state->table = rcalloc(1, table_size);

- +	if (Fread(state->table, table_size, 1, state->fd) != table_size) {

- +	    rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n"));

- +	    return RPMRC_FAIL;

- +	}

- +	state->inodeIndexes = inodeIndexHashCreate(

- +	    state->keys, inodeId, inodeCmp, NULL, NULL

- +	);

- +    }

- +

- +    /* Seek back to original location.

- +     * Might not be needed if we seek to offset immediately

- +     */

- +    if (Fseek(state->fd, current, SEEK_SET) < 0) {

- +	rpmlog(RPMLOG_ERR,

- +	       _("reflink: unable to seek back to original location\n"));

- +	return RPMRC_FAIL;

- +    }

- +    return RPMRC_OK;

- +}

- +

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

- +{

- +    reflink_state state = rpmPluginGetData(plugin);

- +    state->files = rpmfilesFree(state->files);

- +    if (state->table) {

- +	free(state->table);

- +	state->table = NULL;

- +    }

- +    if (state->inodeIndexes) {

- +	inodeIndexHashFree(state->inodeIndexes);

- +	state->inodeIndexes = NULL;

- +    }

- +    return RPMRC_OK;

- +}

- +

- +

- +/* have a prototype, warnings system */

- +rpm_loff_t find(const unsigned char *digest, reflink_state state);

- +

- +rpm_loff_t find(const unsigned char *digest, reflink_state state) {

- +# if defined(__GNUC__)

- +    /* GCC nested function because bsearch's comparison function can't access

- +     * state-keysize otherwise

- +     */

- +    int cmpdigest(const void *k1, const void *k2) {

- +	rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);

- +	return memcmp(k1, k2, state->keysize);

- +    }

- +# endif

- +    rpmlog(RPMLOG_DEBUG,

- +	   _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"),

- +	   digest, state->table, state->keys,

- +	   state->keysize + sizeof(rpm_loff_t));

- +    char *entry = bsearch(digest, state->table, state->keys,

- +			  state->keysize + sizeof(rpm_loff_t), cmpdigest);

- +    if (entry == NULL) {

- +	return NOT_FOUND;

- +    }

- +    rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize);

- +    return offset;

- +}

- +

- +static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,

- +                                  mode_t file_mode, rpmFsmOp op)

- +{

- +    struct file_clone_range fcr;

- +    rpm_loff_t size;

- +    int dst, rc;

- +    int *hlix;

- +

- +    reflink_state state = rpmPluginGetData(plugin);

- +    if (state->table == NULL) {

- +	/* no table means rpm is not in reflink format, so leave. Now. */

- +	return RPMRC_OK;

- +    }

- +    if (op == FA_TOUCH) {

- +	/* we're not overwriting an existing file. */

- +	return RPMRC_OK;

- +    }

- +    fcr.dest_offset = 0;

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

- +	rpm_ino_t inode = rpmfiFInode(fi);

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

- +	 * the address of the first match.

- +	 */

- +	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL,

- +	                           NULL)) {

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

- +	    char *fn = rpmfilesFN(state->files, hlix[0]);

- +	    if (link(fn, path) != 0) {

- +		rpmlog(RPMLOG_ERR,

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

- +		       fn, path, strerror(errno));

- +		free(fn);

- +		return RPMRC_FAIL;

- +	    }

- +	    free(fn);

- +	    return RPMRC_PLUGIN_CONTENTS;

- +	}

- +	/* if we didn't hard link, then we'll track this inode as being

- +	 * created soon

- +	 */

- +	if (rpmfiFNlink(fi) > 1) {

- +	    /* minor optimization: only store files with more than one link */

- +	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));

- +	}

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

- +	mode_t old_umask = umask(0577);

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

- +	umask(old_umask);

- +	if (dst == -1) {

- +	    rpmlog(RPMLOG_ERR,

- +		   _("reflink: Unable to open %s for writing due to %s, flags = %x\n"),

- +		   path, strerror(errno), rpmfiFFlags(fi));

- +	    return RPMRC_FAIL;

- +	}

- +	size = rpmfiFSize(fi);

- +	if (size > 0) {

- +	    /* round src_length down to fundamental_block_size multiple */

- +	    fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size;

- +	    if ((size % state->fundamental_block_size) > 0) {

- +		/* round up to next fundamental_block_size. We expect the data

- +		 * in the rpm to be similarly padded.

- +		 */

- +		fcr.src_length += state->fundamental_block_size;

- +	    }

- +	    fcr.src_fd = Fileno(state->fd);

- +	    if (fcr.src_fd == -1) {

- +		close(dst);

- +		rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n"));

- +		return RPMRC_FAIL;

- +	    }

- +	    fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state);

- +	    if (fcr.src_offset == NOT_FOUND) {

- +		close(dst);

- +		rpmlog(RPMLOG_ERR, _("reflink: digest not found\n"));

- +		return RPMRC_FAIL;

- +	    }

- +	    rpmlog(RPMLOG_DEBUG,

- +	           _("reflink: Reflinking %llu bytes at %llu to %s orig size=%ld, file=%lld\n"),

- +		   fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);

- +	    rc = ioctl(dst, FICLONERANGE, &fcr);

- +	    if (rc) {

- +		rpmlog(RPMLOG_WARNING,

- +		       _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"),

- +		       path, rc, errno, strerror(errno));

- +		if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) {

- +		    close(dst);

- +		    rpmlog(RPMLOG_ERR,

- +			   _("reflink: unable to seek on copying bits\n"));

- +		    return RPMRC_FAIL;

- +		}

- +		rpm_loff_t left = size;

- +		size_t len, read, written;

- +		while (left) {

- +		    len = (left > BUFFER_SIZE ? BUFFER_SIZE : left);

- +		    read = Fread(state->buffer, len, 1, state->fd);

- +		    if (read != len) {

- +			close(dst);

- +			rpmlog(RPMLOG_ERR,

- +			       _("reflink: short read on copying bits\n"));

- +			return RPMRC_FAIL;

- +		    }

- +		    written = write(dst, state->buffer, len);

- +		    if (read != written) {

- +			close(dst);

- +			rpmlog(RPMLOG_ERR,

- +			       _("reflink: short write on copying bits\n"));

- +			return RPMRC_FAIL;

- +		    }

- +		    left -= len;

- +		}

- +	    } else {

- +		/* reflink worked, so truncate */

- +		rc = ftruncate(dst, size);

- +		if (rc) {

- +		    rpmlog(RPMLOG_ERR,

- +			   _("reflink: Unable to truncate %s to %ld due to %s\n"),

- +			   path, size, strerror(errno));

- +		     return RPMRC_FAIL;

- +		}

- +	    }

- +	}

- +	close(dst);

- +	return RPMRC_PLUGIN_CONTENTS;

- +    }

- +    return RPMRC_OK;

- +}

- +

- +struct rpmPluginHooks_s reflink_hooks = {

- +    .init = reflink_init,

- +    .cleanup = reflink_cleanup,

- +    .psm_pre = reflink_psm_pre,

- +    .psm_post = reflink_psm_post,

- +    .fsm_file_pre = reflink_fsm_file_pre,

- +};

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

- new file mode 100644

- index 000000000..c111be0a2

- --- /dev/null

- +++ b/rpm2extents.c

- @@ -0,0 +1,433 @@

- +/* rpm2extents: convert payload to inline extents */

- +

- +#include "system.h"

- +

- +#include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */

- +#include <rpm/rpmfi.h>

- +#include <rpm/rpmtag.h>

- +#include <rpm/rpmio.h>

- +#include <rpm/rpmpgp.h>

- +

- +#include <rpm/rpmts.h>

- +#include "lib/rpmlead.h"

- +#include "lib/signature.h"

- +#include "lib/header_internal.h"

- +#include "rpmio/rpmio_internal.h"

- +

- +#include <unistd.h>

- +#include <sys/types.h>

- +#include <sys/wait.h>

- +#include <signal.h>

- +#include <errno.h>

- +#include <string.h>

- +

- +#include "debug.h"

- +

- +/* hash of void * (pointers) to file digests to offsets within output.

- + * The length of the key depends on what the FILEDIGESTALGO is.

- + */

- +#undef HASHTYPE

- +#undef HTKEYTYPE

- +#undef HTDATATYPE

- +#define HASHTYPE digestSet

- +#define HTKEYTYPE const unsigned char *

- +#include "lib/rpmhash.H"

- +#include "lib/rpmhash.C"

- +

- +/* magic value at end of file (64 bits) that indicates this is a transcoded

- + * rpm.

- + */

- +#define MAGIC 3472329499408095051

- +

- +struct digestoffset {

- +    const unsigned char * digest;

- +    rpm_loff_t pos;

- +};

- +

- +rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit);

- +

- +rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)

- +{

- +    return (unit - (pos % unit)) % unit;

- +}

- +

- +static int digestor(

- +    FD_t fdi,

- +    FD_t fdo,

- +    FD_t validationo,

- +    uint8_t algos[],

- +    uint32_t algos_len

- +)

- +{

- +    ssize_t fdilength;

- +    const char *filedigest, *algo_name;

- +    size_t filedigest_len, len;

- +    uint32_t algo_name_len, algo_digest_len;

- +    int algo;

- +    rpmRC rc = RPMRC_FAIL;

- +

- +    for (algo = 0; algo < algos_len; algo++) {

- +	fdInitDigest(fdi, algos[algo], 0);

- +    }

- +    fdilength = ufdCopy(fdi, fdo);

- +    if (fdilength == -1) {

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

- +	goto exit;

- +    }

- +

- +    len = sizeof(fdilength);

- +    if (Fwrite(&fdilength, len, 1, validationo) != len) {

- +	fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);

- +	goto exit;

- +    }

- +    len = sizeof(algos_len);

- +    if (Fwrite(&algos_len, len, 1, validationo) != len) {

- +	fprintf(stderr, _("Unable to write number of validation digests\n"));

- +	goto exit;

- +    }

- +    for (algo = 0; algo < algos_len; algo++) {

- +	fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0);

- +

- +	algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]);

- +	algo_name_len = (uint32_t)strlen(algo_name);

- +	algo_digest_len = (uint32_t)filedigest_len;

- +

- +	len = sizeof(algo_name_len);

- +	if (Fwrite(&algo_name_len, len, 1, validationo) != len) {

- +	    fprintf(stderr,

- +		    _("Unable to write validation algo name length\n"));

- +	    goto exit;

- +	}

- +	len = sizeof(algo_digest_len);

- +	if (Fwrite(&algo_digest_len, len, 1, validationo) != len) {

- +	    fprintf(stderr,

- +		    _("Unable to write number of bytes for validation digest\n"));

- +	     goto exit;

- +	}

- +	if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) {

- +	    fprintf(stderr, _("Unable to write validation algo name\n"));

- +	    goto exit;

- +	}

- +	if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) {

- +	    fprintf(stderr,

- +		    _("Unable to write validation digest value %u, %zu\n"),

- +		    algo_digest_len, filedigest_len);

- +	    goto exit;

- +	}

- +    }

- +    rc = RPMRC_OK;

- +exit:

- +    return rc;

- +}

- +

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

- +{

- +    uint32_t diglen;

- +    /* GNU C extension: can use diglen from outer context */

- +    int digestSetCmp(const unsigned char * a, const unsigned char * b) {

- +	return memcmp(a, b, diglen);

- +    }

- +

- +    unsigned int digestSetHash(const unsigned char * digest) {

- +        /* assumes sizeof(unsigned int) < diglen */

- +        return *(unsigned int *)digest;

- +    }

- +

- +    int digestoffsetCmp(const void * a, const void * b) {

- +	return digestSetCmp(

- +	    ((struct digestoffset *)a)->digest,

- +	    ((struct digestoffset *)b)->digest

- +	);

- +    }

- +

- +    FD_t fdo;

- +    FD_t gzdi;

- +    Header h, sigh;

- +    long fundamental_block_size = sysconf(_SC_PAGESIZE);

- +    rpmRC rc = RPMRC_OK;

- +    rpm_mode_t mode;

- +    char *rpmio_flags = NULL, *zeros;

- +    const unsigned char *digest;

- +    rpm_loff_t pos, size, pad, validation_pos;

- +    uint32_t offset_ix = 0;

- +    size_t len;

- +    int next = 0;

- +

- +    fdo = fdDup(STDOUT_FILENO);

- +

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

- +	fprintf(stderr, _("Error reading package\n"));

- +	exit(EXIT_FAILURE);

- +    }

- +

- +    if (rpmLeadWrite(fdo, h))

- +    {

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

- +		Fstrerror(fdo));

- +	exit(EXIT_FAILURE);

- +    }

- +

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

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

- +	exit(EXIT_FAILURE);

- +    }

- +

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

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

- +	exit(EXIT_FAILURE);

- +    }

- +

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

- +    {

- +	const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);

- +	rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);

- +    }

- +

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

- +    free(rpmio_flags);

- +

- +    if (gzdi == NULL) {

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

- +	exit(EXIT_FAILURE);

- +    }

- +

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

- +    rpmfi fi = rpmfiNewArchiveReader(gzdi, files,

- +				     RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST);

- +

- +    /* this is encoded in the file format, so needs to be fixed size (for

- +     * now?)

- +     */

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

- +    digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp,

- +				   NULL);

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

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

- +

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

- +    pos += pad_to(pos, 8);

- +    pos += headerSizeof(h, HEADER_MAGIC_YES);

- +

- +    zeros = xcalloc(fundamental_block_size, 1);

- +

- +    while (next >= 0) {

- +	next = rpmfiNext(fi);

- +	if (next == RPMERR_ITER_END) {

- +	    rc = RPMRC_OK;

- +	    break;

- +	}

- +	mode = rpmfiFMode(fi);

- +	if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) {

- +	    /* not a regular file, or the archive doesn't contain any content

- +	     * for this entry.

- +	    */

- +	    continue;

- +	}

- +	digest = rpmfiFDigest(fi, NULL, NULL);

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

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

- +	    continue;

- +	}

- +	pad = pad_to(pos, fundamental_block_size);

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

- +	    fprintf(stderr, _("Unable to write padding\n"));

- +	    rc = RPMRC_FAIL;

- +	    goto exit;

- +	}

- +	/* round up to next fundamental_block_size */

- +	pos += pad;

- +	digestSetAddEntry(ds, digest);

- +	offsets[offset_ix].digest = digest;

- +	offsets[offset_ix].pos = pos;

- +	offset_ix++;

- +	size = rpmfiFSize(fi);

- +	rc = rpmfiArchiveReadToFile(fi, fdo, 0);

- +	if (rc != RPMRC_OK) {

- +	    fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);

- +	    goto exit;

- +	}

- +	pos += size;

- +    }

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

- +

- +    qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),

- +	  digestoffsetCmp);

- +

- +    len = sizeof(offset_ix);

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

- +	fprintf(stderr, _("Unable to write length of table\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +    len = sizeof(diglen);

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

- +	fprintf(stderr, _("Unable to write length of digest\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +    len = sizeof(rpm_loff_t);

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

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

- +	    fprintf(stderr, _("Unable to write digest\n"));

- +	    rc = RPMRC_FAIL;

- +	    goto exit;

- +	}

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

- +	    fprintf(stderr, _("Unable to write offset\n"));

- +	    rc = RPMRC_FAIL;

- +	    goto exit;

- +	}

- +    }

- +    validation_pos = (

- +	pos + sizeof(offset_ix) + sizeof(diglen) +

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

- +    );

- +

- +    ssize_t validation_len = ufdCopy(validationi, fdo);

- +    if (validation_len == -1) {

- +	fprintf(stderr, _("digest table ufdCopy failed\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +    /* add more padding so the last file can be cloned. It doesn't matter that

- +     * the table and validation etc are in this space. In fact, it's pretty

- +     * efficient if it is.

- +    */

- +

- +    pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) +

- +		 sizeof(uint64_t)), fundamental_block_size);

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

- +	fprintf(stderr, _("Unable to write final padding\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +    zeros = _free(zeros);

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

- +	fprintf(stderr, _("Unable to write offset of digest table\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

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

- +	fprintf(stderr, _("Unable to write offset of validation table\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +    uint64_t magic = MAGIC;

- +    len = sizeof(magic);

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

- +	fprintf(stderr, _("Unable to write magic\n"));

- +	rc = RPMRC_FAIL;

- +	goto exit;

- +    }

- +

- +exit:

- +    rpmfilesFree(files);

- +    rpmfiFree(fi);

- +    headerFree(h);

- +    return rc;

- +}

- +

- +int main(int argc, char *argv[]) {

- +    rpmRC rc;

- +    int cprc = 0;

- +    uint8_t algos[argc - 1];

- +    int mainpipefd[2];

- +    int metapipefd[2];

- +    pid_t cpid, w;

- +    int wstatus;

- +

- +    xsetprogname(argv[0]);	/* Portability call -- see system.h */

- +    rpmReadConfigFiles(NULL, NULL);

- +

- +    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) {

- +	fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);

- +	exit(EXIT_FAILURE);

- +    }

- +

- +    if (argc == 1) {

- +	fprintf(stderr,

- +		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));

- +	exit(EXIT_FAILURE);

- +    }

- +

- +    for (int x = 0; x < (argc - 1); x++) {

- +	if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)

- +	{

- +	    fprintf(stderr,

- +		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),

- +		    argv[x + 1]);

- +	    exit(EXIT_FAILURE);

- +	}

- +    }

- +

- +

- +    if (pipe(mainpipefd) == -1) {

- +	fprintf(stderr, _("Main pipe failure\n"));

- +	exit(EXIT_FAILURE);

- +    }

- +    if (pipe(metapipefd) == -1) {

- +	fprintf(stderr, _("Meta pipe failure\n"));

- +	exit(EXIT_FAILURE);

- +    }

- +    cpid = fork();

- +    if (cpid == 0) {

- +	/* child: digestor */

- +	close(mainpipefd[0]);

- +	close(metapipefd[0]);

- +	FD_t fdi = fdDup(STDIN_FILENO);

- +	FD_t fdo = fdDup(mainpipefd[1]);

- +	FD_t validationo = fdDup(metapipefd[1]);

- +	rc = digestor(fdi, fdo, validationo, algos, argc - 1);

- +	Fclose(validationo);

- +	Fclose(fdo);

- +	Fclose(fdi);

- +    } else {

- +	/* parent: main program */

- +	close(mainpipefd[1]);

- +	close(metapipefd[1]);

- +	FD_t fdi = fdDup(mainpipefd[0]);

- +	FD_t validationi = fdDup(metapipefd[0]);

- +	rc = process_package(fdi, validationi);

- +	Fclose(validationi);

- +	/* fdi is normally closed through the stacked file gzdi in the

- +	 * function.

- +	 * Wait for child process (digestor for stdin) to complete.

- +	 */

- +	if (rc != RPMRC_OK) {

- +	    if (kill(cpid, SIGTERM) != 0) {

- +		fprintf(stderr,

- +		        _("Failed to kill digest process when main process failed: %s\n"),

- +			strerror(errno));

- +	    }

- +	}

- +	w = waitpid(cpid, &wstatus, 0);

- +	if (w == -1) {

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

- +	    cprc = EXIT_FAILURE;

- +	} else if (WIFEXITED(wstatus)) {

- +	    cprc = WEXITSTATUS(wstatus);

- +	    if (cprc != 0) {

- +		fprintf(stderr,

- +			_("Digest process non-zero exit code %d\n"),

- +			cprc);

- +	    }

- +	} else if (WIFSIGNALED(wstatus)) {

- +	    fprintf(stderr,

- +		    _("Digest process was terminated with a signal: %d\n"),

- +		    WTERMSIG(wstatus));

- +	    cprc = EXIT_FAILURE;

- +	} else {

- +	    /* Don't think this can happen, but covering all bases */

- +	    fprintf(stderr, _("Unhandled circumstance in waitpid\n"));

- +	    cprc = EXIT_FAILURE;

- +	}

- +	if (cprc != EXIT_SUCCESS) {

- +	    rc = RPMRC_FAIL;

- +	}

- +    }

- +    if (rc != RPMRC_OK) {

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

- +	return EXIT_FAILURE;

- +    }

- +    return EXIT_SUCCESS;

- +}

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

- index 015c15a5c..7b972b4a6 100644

- --- a/rpmio/rpmpgp.c

- +++ b/rpmio/rpmpgp.c

- @@ -283,6 +283,16 @@ int pgpValTok(pgpValTbl vs, const char * s, const char * se)

-      return vs->val;

-  }

-  

- +int pgpStringVal(pgpValType type, const char *str, uint8_t *val)

- +{

- +    pgpValTbl tbl = pgpValTable(type);

- +    if (tbl == NULL) return -1;

- +    int v = pgpValTok(tbl, str, str + strlen(str));

- +    if (v == -1) return -1;

- +    *val = (uint8_t)v;

- +    return 0;

- +}

- +

-  /** \ingroup rpmpgp

-   * Decode length from 1, 2, or 5 octet body length encoding, used in

-   * new format packet headers and V4 signature subpackets.

- diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h

- index c53e29b01..2b57318ba 100644

- --- a/rpmio/rpmpgp.h

- +++ b/rpmio/rpmpgp.h

- @@ -973,6 +973,15 @@ typedef rpmFlags rpmDigestFlags;

-   */

-  const char * pgpValString(pgpValType type, uint8_t val);

-  

- +/** \ingroup rpmpgp

- + * Return  OpenPGP value for a string.

- + * @param type		type of value

- + * @param str		string to lookup

- + * @param[out] val  byte value associated with string

- + * @return		0 on success else -1

- + */

- +int pgpStringVal(pgpValType type, const char *str, uint8_t *val);

- +

-  /** \ingroup rpmpgp

-   * Return (native-endian) integer from big-endian representation.

-   * @param s		pointer to big-endian integer

file modified
+41 -5
@@ -42,7 +42,7 @@ 

  

  %global rpmver 4.14.3

  #global snapver rc2

- %global rel 21.5

+ %global rel 21.6

  

  %global srcver %{version}%{?snapver:-%{snapver}}

  %global srcdir %{?snapver:testing}%{!?snapver:%{name}-%(echo %{version} | cut -d'.' -f1-2).x}
@@ -198,13 +198,44 @@ 

  Patch9001: ndb_query_ro_media.patch

  %endif

  

- Patch9989: 1534.patch

- Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch

+ Patch9800: 1534.patch

+ Patch9801: https://github.com/rpm-software-management/rpm/pull/1381.patch

  Provides: rpm(pr1381)

- Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff

+ 

+ # Copy-on-Write

+ Patch9901: 0001-RPM-with-Copy-on-Write.patch

+ Patch9902: 0002-Remove-use-of-bool-type-for-consistency.patch

+ Patch9903: 0003-Match-formatting-style-of-existing-code.patch

+ Patch9904: 0004-Fix-printf-formatting-in-reflink.c.patch

+ Patch9905: 0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch

+ Patch9906: 0006-rpm2extents-verify-package-signature-during-transcod.patch

+ Patch9907: 0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch

+ Patch9908: 0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch

+ Patch9909: 0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch

+ Patch9910: 0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch

+ Patch9911: 0011-rpm2extents-Perform-digest-computation-within-the-va.patch

+ Patch9912: 0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch

+ Patch9913: 0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch

+ Patch9914: 0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch

+ Patch9915: 0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch

+ Patch9916: 0016-test-new-runroot_plugins-function-to-run-command-in-.patch

+ Patch9917: 0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch

+ Patch9918: 0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch

+ Patch9919: 0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch

+ Patch9920: 0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch

+ Patch9921: 0021-tests-Fix-tests-AT_KEYWORDS-usage.patch

+ Patch9922: 0022-reflink-fix-support-for-hardlinks.patch

+ Patch9923: 0023-rpm2extents-Improve-logging.patch

+ Patch9924: 0024-rpm2extents-create-footer-struct-and-helpers.patch

+ Patch9925: 0025-extents-move-more-functions-helpers-behind-rpmextent.patch

+ Patch9926: 0026-fix-integer-underflow-in-vfyFDCb.patch

+ Patch9927: 0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch

+ Patch9928: 0028-reflink-remove-requirement-for-executable-stack-flag.patch

+ Patch9929: 0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch

+ Patch9930: 0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch

  Provides: rpm(pr1470)

- Patch9992: https://github.com/chantra/rpm/compare/cow...cow_signvalidation.diff

  Provides: rpm(pr1470_1)

+ 

  Patch9999: measure.patch

  

  # Partially GPL/LGPL dual-licensed and some bits with BSD
@@ -799,6 +830,11 @@ 

  %doc doc/librpm/html/*

  

  %changelog

+ * Fri Mar 25 2022 Manu Bretelle <chantra@fb.com> - 4.14.3-21.6

+ - Make `rpm -i` work without `--nodigest`

+ - Remove need of executable stack for reflink plugin

+ - Split RPM CoW diffs in individual patches

+ 

  * Fri Mar 04 2022 Manu Bretelle <chantra@fb.com> - 4.14.3-21.5

  - Backport GH#1040 to allow ndb DB query on read-only FS

  

4.14.3-18 to 4.14.3-21

rebased onto 7b1d935

2 years ago

Pull-Request has been closed by dcavalca

2 years ago
Metadata
Changes Summary 33
+1344
file added
SOURCES/0001-RPM-with-Copy-on-Write.patch
+53
file added
SOURCES/0002-Remove-use-of-bool-type-for-consistency.patch
+1249
file added
SOURCES/0003-Match-formatting-style-of-existing-code.patch
+27
file added
SOURCES/0004-Fix-printf-formatting-in-reflink.c.patch
+73
file added
SOURCES/0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch
+431
file added
SOURCES/0006-rpm2extents-verify-package-signature-during-transcod.patch
+282
file added
SOURCES/0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch
+116
file added
SOURCES/0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch
+70
file added
SOURCES/0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch
+204
file added
SOURCES/0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch
+389
file added
SOURCES/0011-rpm2extents-Perform-digest-computation-within-the-va.patch
+299
file added
SOURCES/0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch
+109
file added
SOURCES/0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch
+79
file added
SOURCES/0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch
+33
file added
SOURCES/0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch
+32
file added
SOURCES/0016-test-new-runroot_plugins-function-to-run-command-in-.patch
+63
file added
SOURCES/0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch
+97
file added
SOURCES/0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch
+156
file added
SOURCES/0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch
+35
file added
SOURCES/0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch
+43
file added
SOURCES/0021-tests-Fix-tests-AT_KEYWORDS-usage.patch
+132
file added
SOURCES/0022-reflink-fix-support-for-hardlinks.patch
+393
file added
SOURCES/0023-rpm2extents-Improve-logging.patch
+200
file added
SOURCES/0024-rpm2extents-create-footer-struct-and-helpers.patch
+176
file added
SOURCES/0025-extents-move-more-functions-helpers-behind-rpmextent.patch
+25
file added
SOURCES/0026-fix-integer-underflow-in-vfyFDCb.patch
+169
file added
SOURCES/0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch
+117
file added
SOURCES/0028-reflink-remove-requirement-for-executable-stack-flag.patch
+109
file added
SOURCES/0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch
+91
file added
SOURCES/0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch
-1640
file removed
SOURCES/cow...cow_signvalidation.diff
-1176
file removed
SOURCES/master...cow.diff
+41 -5
file changed
SPECS/rpm.spec