From c89eec3ca0767984f69e87f55c4c9840faa8e21f Mon Sep 17 00:00:00 2001 From: chantra Date: Mar 01 2022 18:12:19 +0000 Subject: support rpmkeys validation --- diff --git a/cow...cow_signvalidation.diff b/cow...cow_signvalidation.diff new file mode 100644 index 0000000..4ca1bfe --- /dev/null +++ b/cow...cow_signvalidation.diff @@ -0,0 +1,1845 @@ +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..c9fc3bbc9 100644 +--- a/lib/rpmchecksig.c ++++ b/lib/rpmchecksig.c +@@ -20,6 +20,7 @@ + #include "rpmio/rpmio_internal.h" /* fdSetBundle() */ + #include "lib/rpmlead.h" + #include "lib/header_internal.h" ++#include "lib/rpmextents_internal.h" + #include "lib/rpmvs.h" + + #include "debug.h" +@@ -221,36 +222,24 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd, + } + + static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, +- FD_t fd, const char *fn) ++ FD_t fd, rpmsinfoCb cb, void *cbdata) + { + char *msg = NULL; +- struct vfydata_s vd = { .seen = 0, +- .bad = 0, +- .verbose = rpmIsVerbose(), +- }; + int rc; +- struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); + +- rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : ""); ++ ++ if(isTranscodedRpm(fd) == RPMRC_OK){ ++ return extentsVerifySigs(fd); ++ } ++ ++ struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); + + rc = rpmpkgRead(vs, fd, NULL, NULL, &msg); + + if (rc) + goto exit; + +- rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd); +- +- if (!vd.verbose) { +- if (vd.seen & RPMSIG_DIGEST_TYPE) { +- rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_DIGEST_TYPE) ? +- _("DIGESTS") : _("digests")); +- } +- if (vd.seen & RPMSIG_SIGNATURE_TYPE) { +- rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_SIGNATURE_TYPE) ? +- _("SIGNATURES") : _("signatures")); +- } +- rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK")); +- } ++ rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata); + + exit: + if (rc && msg) +@@ -260,15 +249,39 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, + return rc; + } + ++static void rpmkgVerifySigsPreLogging(struct vfydata_s *vd, const char *fn){ ++ rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd->verbose ? "\n" : ""); ++} ++ ++static void rpmkgVerifySigsPostLogging(struct vfydata_s *vd, int rc){ ++ if (!vd->verbose) { ++ if (vd->seen & RPMSIG_DIGEST_TYPE) { ++ rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_DIGEST_TYPE) ? ++ _("DIGESTS") : _("digests")); ++ } ++ if (vd->seen & RPMSIG_SIGNATURE_TYPE) { ++ rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_SIGNATURE_TYPE) ? ++ _("SIGNATURES") : _("signatures")); ++ } ++ rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK")); ++ } ++} ++ + /* Wrapper around rpmkVerifySigs to preserve API */ + int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn) + { + int rc = 1; /* assume failure */ ++ struct vfydata_s vd = { .seen = 0, ++ .bad = 0, ++ .verbose = rpmIsVerbose(), ++ }; + if (ts && qva && fd && fn) { + rpmKeyring keyring = rpmtsGetKeyring(ts, 1); + rpmVSFlags vsflags = rpmtsVfyFlags(ts); + int vfylevel = rpmtsVfyLevel(ts); +- rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, fn); ++ rpmkgVerifySigsPreLogging(&vd, fn); ++ rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, vfyCb, &vd); ++ rpmkgVerifySigsPostLogging(&vd, rc); + rpmKeyringFree(keyring); + } + return rc; +@@ -290,12 +303,22 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) + + while ((arg = *argv++) != NULL) { + FD_t fd = Fopen(arg, "r.ufdio"); ++ struct vfydata_s vd = { .seen = 0, ++ .bad = 0, ++ .verbose = rpmIsVerbose(), ++ }; + if (fd == NULL || Ferror(fd)) { + rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), + arg, Fstrerror(fd)); + res++; +- } else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, arg)) { ++ } else { ++ rpmkgVerifySigsPreLogging(&vd, arg); ++ int rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, ++ vfyCb, &vd); ++ rpmkgVerifySigsPostLogging(&vd, rc); ++ if (rc) { + res++; ++ } + } + + Fclose(fd); +@@ -304,3 +327,53 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) + rpmKeyringFree(keyring); + return res; + } ++ ++struct vfydatafd_s { ++ size_t len; ++ char msg[BUFSIZ]; ++}; ++ ++ ++static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata) ++{ ++ struct vfydatafd_s *vd = cbdata; ++ char *vmsg, *msg; ++ size_t n; ++ size_t remainder = BUFSIZ - vd->len >= 0 ? BUFSIZ - vd->len : 0; ++ ++ vmsg = rpmsinfoMsg(sinfo); ++ rasprintf(&msg, " %s\n", vmsg); ++ n = rstrlcpy(vd->msg + vd->len, msg, remainder); ++ free(vmsg); ++ free(msg); ++ if(n <= remainder){ ++ vd->len += n; ++ } ++ return 1; ++} ++ ++ ++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg) ++{ ++ rpmRC rc = RPMRC_FAIL; ++ rpmKeyring keyring = rpmtsGetKeyring(ts, 1); ++ rpmVSFlags vsflags = rpmtsVfyFlags(ts); ++ int vfylevel = rpmtsVfyLevel(ts); ++ struct vfydatafd_s vd = {.len = 0}; ++ ++ vsflags |= rpmcliVSFlags; ++ if (rpmcliVfyLevelMask) { ++ vfylevel &= ~rpmcliVfyLevelMask; ++ rpmtsSetVfyLevel(ts, vfylevel); ++ } ++ ++ if (!rpmpkgVerifySigs(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) { ++ rc = RPMRC_OK; ++ } ++ *msg = strdup(vd.msg); ++ rpmsqPoll(); ++ ++ rpmKeyringFree(keyring); ++ return rc; ++} ++ +diff --git a/lib/rpmcli.h b/lib/rpmcli.h +index 906fe9951..7ff48b37a 100644 +--- a/lib/rpmcli.h ++++ b/lib/rpmcli.h +@@ -411,6 +411,16 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv); + */ + int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv); + ++ ++/** \ingroup rpmcli ++ * Verify package signatures. ++ * @param ts transaction set ++ * @param fd a file descriptor to verify ++ * @param msg a string containing textual information about the verification, similar to rpmcliVerifySignatures output. ++ * @return 0 on success ++ */ ++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/rpmextents.c b/lib/rpmextents.c +new file mode 100644 +index 000000000..59ba427a4 +--- /dev/null ++++ b/lib/rpmextents.c +@@ -0,0 +1,109 @@ ++ ++#include "system.h" ++ ++#include ++#include ++#include ++#include ++ ++ ++#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; ++ rpm_loff_t current; ++ size_t len; ++ ++ // If the file is not seekable, we cannot detect whether or not it is transcoded. ++ if(Fseek(fd, 0, SEEK_CUR) < 0) { ++ return RPMRC_FAIL; ++ } ++ current = Ftell(fd); ++ ++ len = sizeof(struct extents_footer_t); ++ if(Fseek(fd, -len, SEEK_END) < 0) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for footer: %s\n"), strerror(errno)); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (Fread(footer, len, 1, fd) != len) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read footer\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (footer->magic != EXTENTS_MAGIC) { ++ rc = RPMRC_NOTFOUND; ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ if (Fseek(fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n")); ++ rc = RPMRC_FAIL; ++ } ++ return rc; ++} ++ ++rpmRC isTranscodedRpm(FD_t fd) { ++ struct extents_footer_t footer; ++ return extentsFooterFromFD(fd, &footer); ++} ++ ++ +diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h +new file mode 100644 +index 000000000..380c08425 +--- /dev/null ++++ b/lib/rpmextents_internal.h +@@ -0,0 +1,57 @@ ++#ifndef _RPMEXTENTS_INTERNAL_H ++#define _RPMEXTENTS_INTERNAL_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++ ++/** \ingroup rpmextents ++ * RPM extents library ++ */ ++ ++/* magic value at end of file (64 bits) that indicates this is a transcoded ++ * rpm. ++ */ ++#define EXTENTS_MAGIC 3472329499408095051 ++ ++typedef uint64_t extents_magic_t; ++ ++struct __attribute__ ((__packed__)) extents_footer_offsets_t { ++ off64_t checksig_offset; ++ off64_t table_offset; ++ off64_t csum_offset; ++}; ++ ++struct __attribute__ ((__packed__)) extents_footer_t { ++ struct extents_footer_offsets_t offsets; ++ extents_magic_t magic; ++}; ++ ++/** \ingroup rpmextents ++ * Checks the results of the signature verification ran during transcoding. ++ * @param fd The FD_t of the transcoded RPM ++ * @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. ++ * @param fd The FD_t of the transcoded RPM ++ * @param[out] footer A pointer to an allocated extents_footer_t with a copy of the footer. ++ * @return RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure. ++ */ ++rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer); ++ ++/** \ingroup rpmextents ++ * Check if a RPM is a transcoded RPM ++ * @param fd The FD_t of the transcoded RPM ++ * return RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure. ++ */ ++rpmRC isTranscodedRpm(FD_t fd); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* _RPMEXTENTS_INTERNAL_H */ +diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h +index fd81aec8d..6dbbcff35 100644 +--- a/lib/rpmplugin.h ++++ b/lib/rpmplugin.h +@@ -60,6 +60,13 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi, + const char* path, + const char *dest, + mode_t file_mode, rpmFsmOp op); ++typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi, ++ const char* path, ++ mode_t file_mode, rpmFsmOp op); ++typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin, ++ FD_t payload, ++ rpmfiles files, rpmfi *fi); ++ + + typedef struct rpmPluginHooks_s * rpmPluginHooks; + struct rpmPluginHooks_s { +@@ -80,6 +87,8 @@ struct rpmPluginHooks_s { + plugin_fsm_file_pre_func fsm_file_pre; + plugin_fsm_file_post_func fsm_file_post; + plugin_fsm_file_prepare_func fsm_file_prepare; ++ plugin_fsm_file_install_func fsm_file_install; ++ plugin_fsm_file_archive_reader_func fsm_file_archive_reader; + }; + + #ifdef __cplusplus +diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c +index 3da3097af..901af1ac5 100644 +--- a/lib/rpmplugins.c ++++ b/lib/rpmplugins.c +@@ -421,3 +421,74 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, + + return rc; + } ++ ++rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi, ++ const char *path, mode_t file_mode, ++ rpmFsmOp op) ++{ ++ plugin_fsm_file_install_func hookFunc; ++ int i; ++ rpmRC rc = RPMRC_OK; ++ rpmRC hook_rc; ++ ++ for (i = 0; i < plugins->count; i++) { ++ rpmPlugin plugin = plugins->plugins[i]; ++ RPMPLUGINS_SET_HOOK_FUNC(fsm_file_install); ++ if (hookFunc) { ++ hook_rc = hookFunc(plugin, fi, path, file_mode, op); ++ if (hook_rc == RPMRC_FAIL) { ++ rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install failed\n", plugin->name); ++ rc = RPMRC_FAIL; ++ } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) { ++ if (rc == RPMRC_PLUGIN_CONTENTS) { ++ /* Another plugin already said it'd handle contents. It's ++ * undefined how these would combine, so treat this as a ++ * failure condition. ++ */ ++ rc = RPMRC_FAIL; ++ } else { ++ /* Plugin will handle content */ ++ rc = RPMRC_PLUGIN_CONTENTS; ++ } ++ } ++ } ++ } ++ ++ return rc; ++} ++ ++rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload, ++ rpmfiles files, rpmfi *fi) ++{ ++ plugin_fsm_file_archive_reader_func hookFunc; ++ int i; ++ rpmRC rc = RPMRC_OK; ++ rpmRC hook_rc; ++ ++ for (i = 0; i < plugins->count; i++) { ++ rpmPlugin plugin = plugins->plugins[i]; ++ RPMPLUGINS_SET_HOOK_FUNC(fsm_file_archive_reader); ++ if (hookFunc) { ++ hook_rc = hookFunc(plugin, payload, files, fi); ++ if (hook_rc == RPMRC_FAIL) { ++ rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader failed\n", plugin->name); ++ rc = RPMRC_FAIL; ++ } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) { ++ if (rc == RPMRC_PLUGIN_CONTENTS) { ++ /* Another plugin already said it'd handle contents. It's ++ * undefined how these would combine, so treat this as a ++ * failure condition. ++ */ ++ rc = RPMRC_FAIL; ++ } else { ++ /* Plugin will handle content */ ++ rc = RPMRC_PLUGIN_CONTENTS; ++ } ++ } ++ } ++ } ++ ++ return rc; ++} ++ ++ +diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h +index 39762c376..88807c53c 100644 +--- a/lib/rpmplugins.h ++++ b/lib/rpmplugins.h +@@ -167,6 +167,23 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, + const char *path, const char *dest, + mode_t mode, rpmFsmOp op); + ++/** \ingroup rpmplugins ++ * Call the fsm file install plugin hook ++ * @param plugins plugins structure ++ * @param fi file info iterator (or NULL) ++ * @param path file object path ++ * @param file_mode file object mode ++ * @param op file operation + associated flags ++ * @return RPMRC_OK on success, RPMRC_FAIL otherwise ++ */ ++RPM_GNUC_INTERNAL ++rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi, ++ const char* path, mode_t file_mode, ++ rpmFsmOp op); ++ ++RPM_GNUC_INTERNAL ++rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload, ++ rpmfiles files, rpmfi *fi); + #ifdef __cplusplus + } + #endif +diff --git a/plugins/reflink.c b/plugins/reflink.c +index 513887604..4fc1d74d1 100644 +--- a/plugins/reflink.c ++++ b/plugins/reflink.c +@@ -13,6 +13,7 @@ + #include + #include "lib/rpmlib.h" + #include "lib/rpmplugin.h" ++#include "lib/rpmextents_internal.h" + #include "lib/rpmte_internal.h" + #include + #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..7dd5128de 100644 +--- a/rpm2extents.c ++++ b/rpm2extents.c +@@ -2,7 +2,9 @@ + + #include "system.h" + ++#include + #include /* rpmReadPackageFile .. */ ++#include + #include + #include + #include +@@ -10,8 +12,10 @@ + + #include + #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 +@@ -34,11 +38,6 @@ + #include "lib/rpmhash.H" + #include "lib/rpmhash.C" + +-/* magic value at end of file (64 bits) that indicates this is a transcoded +- * rpm. +- */ +-#define MAGIC 3472329499408095051 +- + struct digestoffset { + const unsigned char * digest; + rpm_loff_t pos; +@@ -51,38 +50,49 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit) + return (unit - (pos % unit)) % unit; + } + +-static int digestor( ++static struct poptOption optionsTable[] = { ++ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0, ++ N_("Common options for all rpm modes and executables:"), NULL }, ++ ++ POPT_AUTOALIAS ++ POPT_AUTOHELP ++ POPT_TABLEEND ++}; ++ ++ ++static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){ ++ int algo; ++ ++ for (algo = 0; algo < algos_len; algo++) { ++ fdInitDigest(fdi, algos[algo], 0); ++ } ++} ++ ++static int FDWriteDigests( + FD_t fdi, + FD_t fdo, +- FD_t validationo, + uint8_t algos[], +- uint32_t algos_len +-) ++ uint32_t algos_len) + { +- ssize_t fdilength; + const char *filedigest, *algo_name; + size_t filedigest_len, len; + uint32_t algo_name_len, algo_digest_len; + int algo; + rpmRC rc = RPMRC_FAIL; + +- for (algo = 0; algo < algos_len; algo++) { +- fdInitDigest(fdi, algos[algo], 0); +- } +- fdilength = ufdCopy(fdi, fdo); +- if (fdilength == -1) { +- fprintf(stderr, _("digest cat failed\n")); +- goto exit; +- } ++ ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes; + + len = sizeof(fdilength); +- if (Fwrite(&fdilength, len, 1, validationo) != len) { +- fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); ++ if (Fwrite(&fdilength, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write input length %zd: %d, %s\n"), ++ fdilength, errno, strerror(errno)); + goto exit; + } + len = sizeof(algos_len); +- if (Fwrite(&algos_len, len, 1, validationo) != len) { +- fprintf(stderr, _("Unable to write number of validation digests\n")); ++ if (Fwrite(&algos_len, len, 1, fdo) != len) { ++ algo_digest_len = (uint32_t)filedigest_len; ++ rpmlog(RPMLOG_ERR, _("Unable to write number of digests: %d, %s\n"), ++ errno, strerror(errno)); + goto exit; + } + for (algo = 0; algo < algos_len; algo++) { +@@ -93,25 +103,29 @@ static int digestor( + algo_digest_len = (uint32_t)filedigest_len; + + len = sizeof(algo_name_len); +- if (Fwrite(&algo_name_len, len, 1, validationo) != len) { +- fprintf(stderr, +- _("Unable to write validation algo name length\n")); ++ if (Fwrite(&algo_name_len, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write digest algo name length: %d, %s\n"), ++ errno, strerror(errno)); + goto exit; + } + len = sizeof(algo_digest_len); +- if (Fwrite(&algo_digest_len, len, 1, validationo) != len) { +- fprintf(stderr, +- _("Unable to write number of bytes for validation digest\n")); ++ if (Fwrite(&algo_digest_len, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write number of bytes for digest: %d, %s\n"), ++ errno, strerror(errno)); + goto exit; + } +- if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) { +- fprintf(stderr, _("Unable to write validation algo name\n")); ++ if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write digest algo name: %d, %s\n"), ++ errno, strerror(errno)); + goto exit; + } +- if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) { +- fprintf(stderr, +- _("Unable to write validation digest value %u, %zu\n"), +- algo_digest_len, filedigest_len); ++ if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write digest value %u, %zu: %d, %s\n"), ++ algo_digest_len, filedigest_len, ++ errno, strerror(errno)); + goto exit; + } + } +@@ -120,7 +134,80 @@ static int digestor( + return rc; + } + +-static rpmRC process_package(FD_t fdi, FD_t validationi) ++static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) { ++ size_t len; ++ rpmRC rc = RPMRC_FAIL; ++ ++ if(rpmvsrc){ ++ rpmlog(RPMLOG_WARNING, ++ _("Error verifying package signatures:\n%s\n"), msg); ++ } ++ ++ len = sizeof(rpmvsrc); ++ if (Fwrite(&rpmvsrc, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write signature verification RC code %d: %d, %s\n"), ++ rpmvsrc, errno, strerror(errno)); ++ goto exit; ++ } ++ size_t content_len = msg ? strlen(msg) : 0; ++ len = sizeof(content_len); ++ if (Fwrite(&content_len, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write signature verification output length %zd: %d, %s\n"), ++ content_len, errno, strerror(errno)); ++ goto exit; ++ } ++ if (Fwrite(msg, content_len, 1, fdo) != content_len) { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to write signature verification output %s: %d, %s\n"), ++ msg, errno, strerror(errno)); ++ goto exit; ++ } ++ ++ rc = RPMRC_OK; ++exit: ++ ++ return rc; ++} ++ ++static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo, ++ uint8_t algos[], ++ uint32_t algos_len){ ++ int rpmvsrc; ++ rpmRC rc = RPMRC_FAIL; ++ char *msg = NULL; ++ rpmts ts = rpmtsCreate(); ++ ++ rpmtsSetRootDir(ts, rpmcliRootDir); ++ ++ FDDigestInit(fdi, algos, algos_len); ++ ++ rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg); ++ ++ // Write result of digest computation ++ if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) { ++ rpmlog(RPMLOG_ERR, _("Failed to write digests: %d, %s\n"), ++ errno, strerror(errno)); ++ goto exit; ++ } ++ ++ // Write result of signature validation. ++ if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) { ++ rpmlog(RPMLOG_ERR, ++ _("Failed to write signature verification result: %d, %s\n"), ++ errno, strerror(errno)); ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ if(msg) { ++ free(msg); ++ } ++ return rc; ++} ++ ++static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + { + uint32_t diglen; + /* GNU C extension: can use diglen from outer context */ +@@ -148,7 +235,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + rpm_mode_t mode; + char *rpmio_flags = NULL, *zeros; + const unsigned char *digest; +- rpm_loff_t pos, size, pad, validation_pos; ++ rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos; + uint32_t offset_ix = 0; + size_t len; + int next = 0; +@@ -156,24 +243,24 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + fdo = fdDup(STDOUT_FILENO); + + if (rpmReadPackageRaw(fdi, &sigh, &h)) { +- fprintf(stderr, _("Error reading package\n")); ++ rpmlog(RPMLOG_ERR, _("Error reading package\n")); + exit(EXIT_FAILURE); + } + + if (rpmLeadWrite(fdo, h)) + { +- fprintf(stderr, _("Unable to write package lead: %s\n"), ++ rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"), + Fstrerror(fdo)); + exit(EXIT_FAILURE); + } + + if (rpmWriteSignature(fdo, sigh)) { +- fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo)); ++ rpmlog(RPMLOG_ERR, _("Unable to write signature: %s\n"), Fstrerror(fdo)); + exit(EXIT_FAILURE); + } + + if (headerWrite(fdo, h, HEADER_MAGIC_YES)) { +- fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo)); ++ rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo)); + exit(EXIT_FAILURE); + } + +@@ -187,7 +274,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + free(rpmio_flags); + + if (gzdi == NULL) { +- fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); ++ rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); + exit(EXIT_FAILURE); + } + +@@ -230,7 +317,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + } + pad = pad_to(pos, fundamental_block_size); + if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { +- fprintf(stderr, _("Unable to write padding\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write padding\n")); + rc = RPMRC_FAIL; + goto exit; + } +@@ -243,7 +330,12 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + size = rpmfiFSize(fi); + rc = rpmfiArchiveReadToFile(fi, fdo, 0); + if (rc != RPMRC_OK) { +- fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); ++ char *errstr = rpmfileStrerror(rc); ++ rpmlog(RPMLOG_ERR, ++ _("rpmfiArchiveReadToFile failed while extracting "\ ++ "\"%s\" with RC %d: %s\n"), ++ rpmfiFN(fi), rc, errstr); ++ free(errstr); + goto exit; + } + pos += size; +@@ -253,42 +345,53 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), + digestoffsetCmp); + ++ validation_pos = pos; ++ ssize_t validation_len = ufdCopy(validationi, fdo); ++ if (validation_len == -1) { ++ rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ ++ digest_table_pos = validation_pos + validation_len; ++ + len = sizeof(offset_ix); + if (Fwrite(&offset_ix, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write length of table\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write length of table\n")); + rc = RPMRC_FAIL; + goto exit; + } + len = sizeof(diglen); + if (Fwrite(&diglen, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write length of digest\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write length of digest\n")); + rc = RPMRC_FAIL; + goto exit; + } + len = sizeof(rpm_loff_t); + for (int x = 0; x < offset_ix; x++) { + if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) { +- fprintf(stderr, _("Unable to write digest\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write digest\n")); + rc = RPMRC_FAIL; + goto exit; + } + if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write offset\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write offset\n")); + rc = RPMRC_FAIL; + goto exit; + } + } +- validation_pos = ( +- pos + sizeof(offset_ix) + sizeof(diglen) + ++ digest_pos = ( ++ digest_table_pos + sizeof(offset_ix) + sizeof(diglen) + + offset_ix * (diglen + sizeof(rpm_loff_t)) + ); + +- ssize_t validation_len = ufdCopy(validationi, fdo); +- if (validation_len == -1) { +- fprintf(stderr, _("digest table ufdCopy failed\n")); ++ ssize_t digest_len = ufdCopy(digestori, fdo); ++ if (digest_len == -1) { ++ rpmlog(RPMLOG_ERR, _("digest table ufdCopy failed\n")); + rc = RPMRC_FAIL; + goto exit; + } ++ + /* add more padding so the last file can be cloned. It doesn't matter that + * the table and validation etc are in this space. In fact, it's pretty + * efficient if it is. +@@ -297,25 +400,15 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + + sizeof(uint64_t)), fundamental_block_size); + if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { +- fprintf(stderr, _("Unable to write final padding\n")); ++ rpmlog(RPMLOG_ERR, _("Unable to write final padding\n")); + rc = RPMRC_FAIL; + goto exit; + } + zeros = _free(zeros); +- if (Fwrite(&pos, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write offset of digest table\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- if (Fwrite(&validation_pos, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write offset of validation table\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- uint64_t magic = MAGIC; +- len = sizeof(magic); +- if (Fwrite(&magic, len, 1, fdo) != len) { +- fprintf(stderr, _("Unable to write magic\n")); ++ struct extents_footer_t footer = {.offsets = {validation_pos, digest_table_pos, digest_pos}, .magic = EXTENTS_MAGIC}; ++ len = sizeof(footer); ++ if (Fwrite(&footer, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write footer\n")); + rc = RPMRC_FAIL; + goto exit; + } +@@ -327,104 +420,202 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) + return rc; + } + +-int main(int argc, char *argv[]) { +- rpmRC rc; +- int cprc = 0; +- uint8_t algos[argc - 1]; +- int mainpipefd[2]; +- int metapipefd[2]; +- pid_t cpid, w; +- int wstatus; ++static off_t ufdTee(FD_t sfd, FD_t *fds, int len) ++{ ++ char buf[BUFSIZ]; ++ ssize_t rdbytes, wrbytes; ++ off_t total = 0; ++ ++ while (1) { ++ rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd); ++ ++ if (rdbytes > 0) { ++ for(int i=0; i < len; i++) { ++ wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]); ++ if (wrbytes != rdbytes) { ++ rpmlog(RPMLOG_ERR, ++ _("Error wriing to FD %d: %s\n"), ++ i, Fstrerror(fds[i])); ++ total = -1; ++ break; ++ } ++ } ++ if(total == -1){ ++ break; ++ } ++ total += wrbytes; ++ } else { ++ if (rdbytes < 0) ++ total = -1; ++ break; ++ } ++ } + +- xsetprogname(argv[0]); /* Portability call -- see system.h */ +- rpmReadConfigFiles(NULL, NULL); ++ return total; ++} + +- if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) { +- fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); +- exit(EXIT_FAILURE); +- } ++static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) { ++ rpmRC rc = RPMRC_FAIL; ++ off_t offt = -1; ++ // tee-ed stdin ++ int processorpipefd[2]; ++ int validatorpipefd[2]; ++ // metadata ++ int meta_digestpipefd[2]; ++ int meta_rpmsignpipefd[2]; ++ ++ pid_t cpids[2], w; ++ int wstatus; ++ FD_t fds[2]; + +- if (argc == 1) { +- fprintf(stderr, +- _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); +- exit(EXIT_FAILURE); ++ if (pipe(processorpipefd) == -1) { ++ rpmlog(RPMLOG_ERR, _("Processor pipe failure\n")); ++ return RPMRC_FAIL; + } + +- for (int x = 0; x < (argc - 1); x++) { +- if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) +- { +- fprintf(stderr, +- _("Unable to resolve '%s' as a digest algorithm, exiting\n"), +- argv[x + 1]); +- exit(EXIT_FAILURE); +- } ++ if (pipe(validatorpipefd) == -1) { ++ rpmlog(RPMLOG_ERR, _("Validator pipe failure\n")); ++ return RPMRC_FAIL; + } + +- +- if (pipe(mainpipefd) == -1) { +- fprintf(stderr, _("Main pipe failure\n")); +- exit(EXIT_FAILURE); ++ if (pipe(meta_digestpipefd) == -1) { ++ rpmlog(RPMLOG_ERR, _("Meta digest pipe failure\n")); ++ return RPMRC_FAIL; + } +- if (pipe(metapipefd) == -1) { +- fprintf(stderr, _("Meta pipe failure\n")); +- exit(EXIT_FAILURE); ++ ++ if (pipe(meta_rpmsignpipefd) == -1) { ++ rpmlog(RPMLOG_ERR, _("Meta rpm signature pipe failure\n")); ++ return RPMRC_FAIL; + } +- cpid = fork(); +- if (cpid == 0) { +- /* child: digestor */ +- close(mainpipefd[0]); +- close(metapipefd[0]); +- FD_t fdi = fdDup(STDIN_FILENO); +- FD_t fdo = fdDup(mainpipefd[1]); +- FD_t validationo = fdDup(metapipefd[1]); +- rc = digestor(fdi, fdo, validationo, algos, argc - 1); +- Fclose(validationo); +- Fclose(fdo); ++ ++ cpids[0] = fork(); ++ if (cpids[0] == 0) { ++ /* child: validator */ ++ close(processorpipefd[0]); ++ close(processorpipefd[1]); ++ close(validatorpipefd[1]); ++ close(meta_digestpipefd[0]); ++ close(meta_rpmsignpipefd[0]); ++ FD_t fdi = fdDup(validatorpipefd[0]); ++ FD_t digesto = fdDup(meta_digestpipefd[1]); ++ FD_t sigo = fdDup(meta_rpmsignpipefd[1]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[1]); ++ rc = validator(fdi, digesto, sigo, algos, algos_len); ++ if(rc != RPMRC_OK) { ++ rpmlog(RPMLOG_ERR, _("Validator failed with RC %d\n"), rc); ++ } + Fclose(fdi); ++ Fclose(digesto); ++ Fclose(sigo); ++ if (rc != RPMRC_OK) { ++ exit(EXIT_FAILURE); ++ } ++ exit(EXIT_SUCCESS); + } else { + /* parent: main program */ +- close(mainpipefd[1]); +- close(metapipefd[1]); +- FD_t fdi = fdDup(mainpipefd[0]); +- FD_t validationi = fdDup(metapipefd[0]); +- rc = process_package(fdi, validationi); +- Fclose(validationi); +- /* fdi is normally closed through the stacked file gzdi in the +- * function. +- * Wait for child process (digestor for stdin) to complete. +- */ +- if (rc != RPMRC_OK) { +- if (kill(cpid, SIGTERM) != 0) { +- fprintf(stderr, +- _("Failed to kill digest process when main process failed: %s\n"), +- strerror(errno)); ++ cpids[1] = fork(); ++ if (cpids[1] == 0) { ++ /* child: process_package */ ++ close(validatorpipefd[0]); ++ close(validatorpipefd[1]); ++ close(processorpipefd[1]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[1]); ++ FD_t fdi = fdDup(processorpipefd[0]); ++ close(processorpipefd[0]); ++ FD_t sigi = fdDup(meta_rpmsignpipefd[0]); ++ close(meta_rpmsignpipefd[0]); ++ FD_t digestori = fdDup(meta_digestpipefd[0]); ++ close(meta_digestpipefd[0]); ++ ++ rc = process_package(fdi, digestori, sigi); ++ if(rc != RPMRC_OK) { ++ rpmlog(RPMLOG_ERR, _("Package processor failed: %d\n"), rc); + } +- } +- w = waitpid(cpid, &wstatus, 0); +- if (w == -1) { +- fprintf(stderr, _("waitpid failed\n")); +- cprc = EXIT_FAILURE; +- } else if (WIFEXITED(wstatus)) { +- cprc = WEXITSTATUS(wstatus); +- if (cprc != 0) { +- fprintf(stderr, +- _("Digest process non-zero exit code %d\n"), +- cprc); ++ Fclose(digestori); ++ Fclose(sigi); ++ /* fdi is normally closed through the stacked file gzdi in the ++ * function ++ */ ++ ++ if (rc != RPMRC_OK) { ++ exit(EXIT_FAILURE); + } +- } else if (WIFSIGNALED(wstatus)) { +- fprintf(stderr, +- _("Digest process was terminated with a signal: %d\n"), +- WTERMSIG(wstatus)); +- cprc = EXIT_FAILURE; ++ exit(EXIT_SUCCESS); ++ ++ + } else { +- /* Don't think this can happen, but covering all bases */ +- fprintf(stderr, _("Unhandled circumstance in waitpid\n")); +- cprc = EXIT_FAILURE; ++ /* Actual parent. Read from fdi and write to both processes */ ++ close(processorpipefd[0]); ++ close(validatorpipefd[0]); ++ fds[0] = fdDup(processorpipefd[1]); ++ fds[1] = fdDup(validatorpipefd[1]); ++ close(validatorpipefd[1]); ++ close(processorpipefd[1]); ++ close(meta_digestpipefd[0]); ++ close(meta_digestpipefd[1]); ++ close(meta_rpmsignpipefd[0]); ++ close(meta_rpmsignpipefd[1]); ++ ++ rc = RPMRC_OK; ++ offt = ufdTee(fdi, fds, 2); ++ if(offt == -1){ ++ rpmlog(RPMLOG_ERR, _("Failed to tee RPM\n")); ++ rc = RPMRC_FAIL; ++ } ++ Fclose(fds[0]); ++ Fclose(fds[1]); ++ w = waitpid(cpids[0], &wstatus, 0); ++ if (w == -1) { ++ rpmlog(RPMLOG_ERR, _("waitpid cpids[0] failed\n")); ++ rc = RPMRC_FAIL; ++ } ++ w = waitpid(cpids[1], &wstatus, 0); ++ if (w == -1) { ++ rpmlog(RPMLOG_ERR, _("waitpid cpids[1] failed\n")); ++ rc = RPMRC_FAIL; ++ } + } +- if (cprc != EXIT_SUCCESS) { +- rc = RPMRC_FAIL; ++ } ++ ++ return rc; ++} ++ ++int main(int argc, char *argv[]) { ++ rpmRC rc; ++ poptContext optCon = NULL; ++ const char **args = NULL; ++ int nb_algos = 0; ++ ++ xsetprogname(argv[0]); /* Portability call -- see system.h */ ++ rpmReadConfigFiles(NULL, NULL); ++ optCon = rpmcliInit(argc, argv, optionsTable); ++ poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); ++ ++ if (poptPeekArg(optCon) == NULL) { ++ rpmlog(RPMLOG_ERR, ++ _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); ++ poptPrintUsage(optCon, stderr, 0); ++ exit(EXIT_FAILURE); ++ } ++ ++ args = poptGetArgs(optCon); ++ ++ for (nb_algos=0; args[nb_algos]; nb_algos++); ++ uint8_t algos[nb_algos]; ++ for (int x = 0; x < nb_algos; x++) { ++ if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0) ++ { ++ rpmlog(RPMLOG_ERR, ++ _("Unable to resolve '%s' as a digest algorithm, exiting\n"), ++ args[x]); ++ exit(EXIT_FAILURE); + } + } ++ ++ FD_t fdi = fdDup(STDIN_FILENO); ++ rc = teeRpm(fdi, algos, nb_algos); + if (rc != RPMRC_OK) { + /* translate rpmRC into generic failure return code. */ + return EXIT_FAILURE; +diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump +new file mode 100755 +index 000000000..596a59a49 +--- /dev/null ++++ b/scripts/rpm2extents_dump +@@ -0,0 +1,94 @@ ++#!/usr/bin/env python3 ++ ++import argparse ++import binascii ++import os ++import struct ++import sys ++ ++MAGIC_SIZE = 8 ++MAGIC_STR = b'KWTSH100' ++ ++POS_SIZE = 8 ++ ++def keep_position(func): ++ def wrapper(*args, **kwargs): ++ curr = args[0].tell() ++ res = func(*args, **kwargs) ++ f.seek(curr, os.SEEK_SET) ++ return res ++ return wrapper ++ ++def read_validation_digest(f, validation_offset): ++ digests = [] ++ # validation ++ f.seek(validation_offset, os.SEEK_SET) ++ val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4)) ++ for i in range(val_digests_num): ++ algo_name_len, digest_len = struct.unpack('=II', f.read(8)) ++ algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len)) ++ digests.append((algo_name, binascii.hexlify(digest))) ++ return digests ++ ++ ++def read_digests_table(f, digest_offset): ++ digests = [] ++ # validation ++ f.seek(digest_offset, os.SEEK_SET) ++ table_len, digest_len = struct.unpack('=II', f.read(8)) ++ ++ for i in range(table_len): ++ digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8)) ++ digests.append((pos, binascii.hexlify(digest))) ++ return digests ++ ++def read_signature_output(f, signature_offset): ++ f.seek(signature_offset, os.SEEK_SET) ++ signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12)) ++ return signature_rc, f.read(signature_output_len) ++ ++@keep_position ++def parse_file(f): ++ digests = [] ++ pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END) ++ signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE)) ++ ++ validation_digests = read_validation_digest(f, validation_offset) ++ digests_table = read_digests_table(f, digest_offset) ++ signature_ouput = read_signature_output(f, signature_offset) ++ ++ return validation_digests, digests_table, signature_ouput ++ ++@keep_position ++def is_transcoded(f): ++ f.seek(-MAGIC_SIZE, os.SEEK_END) ++ magic = f.read(MAGIC_SIZE) ++ return magic == MAGIC_STR ++ ++def arg_parse(): ++ parser = argparse.ArgumentParser() ++ parser.add_argument('--dump-signature', action='store_true') ++ parser.add_argument('--dump-file-digest-table', action='store_true') ++ parser.add_argument('--dump-digests', action='store_true') ++ parser.add_argument('file') ++ ++ return parser.parse_args() ++ ++if __name__ == '__main__': ++ args = arg_parse() ++ f = open(args.file, 'rb') ++ if not is_transcoded(f): ++ sys.exit(1) ++ ++ validation_digests, digests_table, signature_output = parse_file(f) ++ if(args.dump_file_digest_table): ++ for digest in digests_table: ++ print(f"FileDigest {hex(digest[0])}: {digest[1]}") ++ ++ if(args.dump_digests): ++ for validation_digest in validation_digests: ++ print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}") ++ ++ if(args.dump_signature): ++ print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}") ++ +diff --git a/tests/Makefile.am b/tests/Makefile.am +index f78e17c3e..fc8a24a5e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -36,6 +36,7 @@ TESTSUITE_AT += rpmsigdig.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 +@@ -50,6 +50,19 @@ else + CAP_DISABLED=true; + fi + ++FSTYPE=$(stat -f -c %T /) ++REFLINKABLE_FS=("xfs" "brtfs") ++ ++REFLINK_DISABLED=true; ++for item in "${REFLINKABLE_FS[@]}" ++do ++ if test "${FSTYPE}" = "${item}" ++ then ++ REFLINK_DISABLED=false; ++ break ++ fi ++done ++ + function setup_env() + { + if [ -d testing ]; then +@@ -82,6 +95,15 @@ function runroot() + ) + } + ++function runroot_plugins() ++{ ++ setup_env ++ (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \ ++ MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns ++ ) ++} ++ ++ + function runroot_other() + { + setup_env +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 ++# ++# 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([rpmconfig.at]) + m4_include([rpmconfig2.at]) + m4_include([rpmconfig3.at]) ++m4_include([rpm2extents.at]) diff --git a/rpm.spec b/rpm.spec index 388c103..738a543 100644 --- a/rpm.spec +++ b/rpm.spec @@ -137,6 +137,8 @@ Patch1983: 0034-rpmsign-Adopting-PKCS11-opaque-keys-support-in-libfsverity-for-f Patch9989: 1534.patch Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff 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 @@ -622,6 +624,7 @@ fi %files plugin-reflink %{_bindir}/rpm2extents +%{_bindir}/rpm2extents_dump %{_libdir}/rpm-plugins/reflink.so %files plugin-prioreset @@ -697,6 +700,7 @@ fi - Added fsverity backport - Added measure plugin support - Apply GH1534 in preparation of RPM cow patch +- Add support for RPM CoW * Mon Feb 14 2022 Michal Domonkos - 4.16.1.3-11 - Fix IMA signature lengths assumed constant, take III (#2018937)