diff --git a/0031-rpmcow-denylist.patch b/0031-rpmcow-denylist.patch new file mode 100644 index 0000000..da7aee3 --- /dev/null +++ b/0031-rpmcow-denylist.patch @@ -0,0 +1,386 @@ +From: Richard Phibel + +Subject: RPM with Copy on Write: add deny list mechanism + +commit 3431550e6c92ba4bc6d091cb244f70c158dfbbaa +Author: Richard Phibel +Date: Wed Oct 19 23:05:17 2022 +0200 + + RPM with Copy on Write: add deny list mechanism + + A couple of issues were encountered when using RPM CoW for some + packages. This change adds a deny list mechanism to disable RPM CoW for + specific packages. + +Signed-off-by: Richard Phibel + +diff --git a/build/pack.c b/build/pack.c +index 8d6f74935ebb8a4d65aa2bcb666825a99162be9c..e2f05b6412d3e0d814a1160a0dcdfad5c323a8f8 100644 +--- a/build/pack.c ++++ b/build/pack.c +@@ -493,7 +493,7 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, + } + + /* Write the lead section into the package. */ +- if (rpmLeadWrite(fd, pkg->header)) { ++ if (rpmLeadWriteFromHeader(fd, pkg->header)) { + rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd)); + goto exit; + } +diff --git a/lib/package.c b/lib/package.c +index 90bd0d8a719353e8965c140b40e2dceef80a2ed1..fd41abbf6b4df07afa2caf1c47d9724765ee84e8 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -412,11 +412,7 @@ rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp) + Header h = NULL; + Header sigh = NULL; + +- rpmRC rc = rpmLeadRead(fd, &msg); +- if (rc != RPMRC_OK) +- goto exit; +- +- rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg); ++ rpmRC rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg); + if (rc != RPMRC_OK) + goto exit; + +diff --git a/lib/rpmlead.c b/lib/rpmlead.c +index 45b1c6f8edacb65549ba8d321de8f6ce8cef4503..82105122724a8efb071aecefdb278ef96ea5e70d 100644 +--- a/lib/rpmlead.c ++++ b/lib/rpmlead.c +@@ -24,24 +24,6 @@ static unsigned char const lead_magic[] = { + RPMLEAD_MAGIC0, RPMLEAD_MAGIC1, RPMLEAD_MAGIC2, RPMLEAD_MAGIC3 + }; + +-/** \ingroup lead +- * The lead data structure. +- * The lead needs to be 8 byte aligned. +- * @deprecated The lead (except for signature_type) is legacy. +- * @todo Don't use any information from lead. +- */ +-struct rpmlead_s { +- unsigned char magic[4]; +- unsigned char major; +- unsigned char minor; +- short type; +- short archnum; +- char name[66]; +- short osnum; +- short signature_type; /*!< Signature header type (RPMSIG_HEADERSIG) */ +- char reserved[16]; /*!< Pad to 96 bytes -- 8 byte aligned! */ +-}; +- + static int rpmLeadFromHeader(Header h, struct rpmlead_s *l) + { + if (h != NULL) { +@@ -70,13 +52,23 @@ static int rpmLeadFromHeader(Header h, struct rpmlead_s *l) + } + + /* The lead needs to be 8 byte aligned */ +-rpmRC rpmLeadWrite(FD_t fd, Header h) ++rpmRC rpmLeadWriteFromHeader(FD_t fd, Header h) + { + rpmRC rc = RPMRC_FAIL; + struct rpmlead_s l; + +- if (rpmLeadFromHeader(h, &l)) { +- ++ if (rpmLeadFromHeader(h, &l)) { ++ rc = rpmLeadWrite(fd, l); ++ } ++ ++ return rc; ++} ++ ++/* The lead needs to be 8 byte aligned */ ++rpmRC rpmLeadWrite(FD_t fd, struct rpmlead_s l) ++{ ++ rpmRC rc = RPMRC_FAIL; ++ + l.type = htons(l.type); + l.archnum = htons(l.archnum); + l.osnum = htons(l.osnum); +@@ -84,7 +76,6 @@ rpmRC rpmLeadWrite(FD_t fd, Header h) + + if (Fwrite(&l, 1, sizeof(l), fd) == sizeof(l)) + rc = RPMRC_OK; +- } + + return rc; + } +@@ -107,6 +98,11 @@ static rpmRC rpmLeadCheck(struct rpmlead_s *lead, char **msg) + } + + rpmRC rpmLeadRead(FD_t fd, char **emsg) ++{ ++ return rpmLeadReadAndReturn(fd, emsg, NULL); ++} ++ ++rpmRC rpmLeadReadAndReturn(FD_t fd, char **emsg, struct rpmlead_s * ret) + { + rpmRC rc = RPMRC_OK; + struct rpmlead_s l; +@@ -136,5 +132,8 @@ rpmRC rpmLeadRead(FD_t fd, char **emsg) + free(err); + } + ++ if (ret) ++ *ret = l; ++ + return rc; + } +diff --git a/lib/rpmlead.h b/lib/rpmlead.h +index 9d86a8d73b3250d3c306b3a3ac4a33486e6920ec..8a592abc1d0e69822f438c4c7b248cce1cb5ee72 100644 +--- a/lib/rpmlead.h ++++ b/lib/rpmlead.h +@@ -19,13 +19,39 @@ extern "C" { + + #define RPMLEAD_SIZE 96 /*!< Don't rely on sizeof(struct) */ + ++/** \ingroup lead ++ * The lead data structure. ++ * The lead needs to be 8 byte aligned. ++ * @deprecated The lead (except for signature_type) is legacy. ++ * @todo Don't use any information from lead. ++ */ ++struct rpmlead_s { ++ unsigned char magic[4]; ++ unsigned char major; ++ unsigned char minor; ++ short type; ++ short archnum; ++ char name[66]; ++ short osnum; ++ short signature_type; /*!< Signature header type (RPMSIG_HEADERSIG) */ ++ char reserved[16]; /*!< Pad to 96 bytes -- 8 byte aligned! */ ++}; ++ + /** \ingroup lead + * Write lead to file handle. + * @param fd file handle + * @param h package header + * @return RPMRC_OK on success, RPMRC_FAIL on error + */ +-rpmRC rpmLeadWrite(FD_t fd, Header h); ++rpmRC rpmLeadWriteFromHeader(FD_t fd, Header h); ++ ++/** \ingroup lead ++ * Write lead to file handle. ++ * @param fd file handle ++ * @param l lead ++ * @return RPMRC_OK on success, RPMRC_FAIL on error ++ */ ++rpmRC rpmLeadWrite(FD_t fd, struct rpmlead_s l); + + /** \ingroup lead + * Read lead from file handle. +@@ -35,6 +61,15 @@ rpmRC rpmLeadWrite(FD_t fd, Header h); + */ + rpmRC rpmLeadRead(FD_t fd, char **emsg); + ++/** \ingroup lead ++ * Read lead from file handle and return it. ++ * @param fd file handle ++ * @param[out] emsg failure message on error (malloced) ++ * @param[out] ret address of lead ++ * @return RPMRC_OK on success, RPMRC_FAIL/RPMRC_NOTFOUND on error ++ */ ++rpmRC rpmLeadReadAndReturn(FD_t fd, char **emsg, struct rpmlead_s * ret); ++ + #ifdef __cplusplus + } + #endif +diff --git a/rpm2extents.c b/rpm2extents.c +index 7dd5128decb03781411bd714339b3b6e9b805842..702d3765d76faf618992ca112011d2312d7fcdcc 100644 +--- a/rpm2extents.c ++++ b/rpm2extents.c +@@ -134,6 +134,28 @@ exit: + return rc; + } + ++/** ++ * Check if package is in deny list. ++ * @param package_name package name ++ * @return true if package is in deny list ++ */ ++static inline int isInDenyList(char * package_name) ++{ ++ int is_in_deny_list = 0; ++ if (package_name) { ++ char *e_denylist = getenv("LIBREPO_TRANSCODE_RPMS_DENYLIST"); ++ char *denytlist_item = strtok(e_denylist, ","); ++ while (denytlist_item) { ++ if (strstr(package_name, denytlist_item)) { ++ is_in_deny_list = 1; ++ break; ++ } ++ denytlist_item = strtok(NULL, ","); ++ } ++ } ++ return is_in_deny_list; ++} ++ + static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) { + size_t len; + rpmRC rc = RPMRC_FAIL; +@@ -239,15 +261,41 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + uint32_t offset_ix = 0; + size_t len; + int next = 0; ++ struct rpmlead_s l; ++ rpmfiles files = NULL; ++ rpmfi fi = NULL; ++ char *msg = NULL; + + fdo = fdDup(STDOUT_FILENO); + ++ rc = rpmLeadReadAndReturn(fdi, &msg, &l); ++ if (rc != RPMRC_OK) ++ goto exit; ++ ++ /* Skip conversion if package is in deny list */ ++ if (isInDenyList(l.name)) { ++ if (rpmLeadWrite(fdo, l)) { ++ fprintf(stderr, _("Unable to write package lead: %s\n"), ++ Fstrerror(fdo)); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ ++ ssize_t fdilength = ufdCopy(fdi, fdo); ++ if (fdilength == -1) { ++ fprintf(stderr, _("process_package cat failed\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ ++ goto exit; ++ } else { + if (rpmReadPackageRaw(fdi, &sigh, &h)) { + rpmlog(RPMLOG_ERR, _("Error reading package\n")); + exit(EXIT_FAILURE); + } + +- if (rpmLeadWrite(fdo, h)) ++ if (rpmLeadWriteFromHeader(fdo, h)) + { + rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"), + Fstrerror(fdo)); +@@ -264,38 +312,41 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + exit(EXIT_FAILURE); + } + +- /* Retrieve payload size and compression type. */ +- { +- const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); +- rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); +- } ++ /* Retrieve payload size and compression type. */ ++ { ++ const char *compr = ++ headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); ++ rpmio_flags = ++ rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); ++ } + +- gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ +- free(rpmio_flags); ++ gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ ++ free(rpmio_flags); + + if (gzdi == NULL) { + rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); + exit(EXIT_FAILURE); + } + +- rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); +- rpmfi fi = rpmfiNewArchiveReader(gzdi, files, +- RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); ++ files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); ++ fi = rpmfiNewArchiveReader(gzdi, files, ++ RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); + +- /* this is encoded in the file format, so needs to be fixed size (for +- * now?) +- */ +- diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi)); +- digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp, +- NULL); +- struct digestoffset offsets[rpmfiFC(fi)]; +- pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES); ++ /* this is encoded in the file format, so needs to be fixed size (for ++ * now?) ++ */ ++ diglen = (uint32_t) rpmDigestLength(rpmfiDigestAlgo(fi)); ++ digestSet ds = ++ digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp, ++ NULL); ++ struct digestoffset offsets[rpmfiFC(fi)]; ++ pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES); + +- /* main headers are aligned to 8 byte boundry */ +- pos += pad_to(pos, 8); +- pos += headerSizeof(h, HEADER_MAGIC_YES); ++ /* main headers are aligned to 8 byte boundry */ ++ pos += pad_to(pos, 8); ++ pos += headerSizeof(h, HEADER_MAGIC_YES); + +- zeros = xcalloc(fundamental_block_size, 1); ++ zeros = xcalloc(fundamental_block_size, 1); + + while (next >= 0) { + next = rpmfiNext(fi); +@@ -342,8 +393,8 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + } + Fclose(gzdi); /* XXX gzdi == fdi */ + +- qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), +- digestoffsetCmp); ++ qsort(offsets, (size_t) offset_ix, sizeof(struct digestoffset), ++ digestoffsetCmp); + + validation_pos = pos; + ssize_t validation_len = ufdCopy(validationi, fdo); +@@ -412,6 +463,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + rc = RPMRC_FAIL; + goto exit; + } ++ } + + exit: + rpmfilesFree(files); +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index b4897f1de444080c8dc6c4913b5f33de20796822..d54e254ff41a0441d04fbcf27f26e4809f563b79 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -672,7 +672,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + } + + /* Write the lead/signature of the output rpm */ +- rc = rpmLeadWrite(ofd, h); ++ rc = rpmLeadWriteFromHeader(ofd, h); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm, + Fstrerror(ofd)); +diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at +index 5135c9cf83d9e75e9d9bc0b84186ab10cc0cbcac..c9c79c5acd22b86704460f295712ce7ab5ee3259 100644 +--- a/tests/rpm2extents.at ++++ b/tests/rpm2extents.at +@@ -95,6 +95,17 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $? + []) + AT_CLEANUP + ++# check that package in denylist is not transcoded ++AT_SETUP([rpm2extents denylist]) ++AT_KEYWORDS([rpm2extents]) ++AT_CHECK([ ++export LIBREPO_TRANSCODE_RPMS_DENYLIST="vim,hello,cowsay" ++runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | runroot_other cmp /data/RPMS/hello-2.0-1.x86_64.rpm -], ++[0], ++[], ++[ignore]) ++AT_CLEANUP ++ + AT_SETUP([rpm2extents install package]) + AT_KEYWORDS([rpm2extents reflink]) + AT_SKIP_IF([$REFLINK_DISABLED]) diff --git a/0032-rpmcow-workaround.patch b/0032-rpmcow-workaround.patch new file mode 100644 index 0000000..69d5178 --- /dev/null +++ b/0032-rpmcow-workaround.patch @@ -0,0 +1,385 @@ +From: Richard Phibel + +Subject: RPM with Copy on Write: workaround for corrupt signature header + +commit 7976c921f60ec5d20c50c4c702ec5636b39210ba +Author: Richard Phibel +Date: Wed Oct 26 18:57:19 2022 +0200 + + RPM with Copy on Write: workaround for corrupt signature header + + Some packages have errors in the signature header. These errors do not + prevent installation of the package but cause issues in rpm2extents + conversion. This commit implements the same fix as in + unloadImmutableRegion. + +Signed-off-by: Richard Phibel + +diff --git a/rpm2extents.c b/rpm2extents.c +index 702d3765d76faf618992ca112011d2312d7fcdcc..b641511fff884e3c8fb396f28acbd8e2c736e28a 100644 +--- a/rpm2extents.c ++++ b/rpm2extents.c +@@ -139,7 +139,7 @@ exit: + * @param package_name package name + * @return true if package is in deny list + */ +-static inline int isInDenyList(char * package_name) ++static inline int isInDenyList(char *package_name) + { + int is_in_deny_list = 0; + if (package_name) { +@@ -153,7 +153,7 @@ static inline int isInDenyList(char * package_name) + denytlist_item = strtok(NULL, ","); + } + } +- return is_in_deny_list; ++ return is_in_deny_list; + } + + static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) { +@@ -229,6 +229,22 @@ exit: + return rc; + } + ++static void sanitizeSignatureHeader(Header * sigh) ++{ ++ struct rpmtd_s td; ++ ++ /* This is inspired by the code in unloadImmutableRegion. See https://github.com/rpm-software-management/rpm/pull/1330 */ ++ if (!headerGet(*sigh, RPMTAG_HEADERSIGNATURES, &td, HEADERGET_DEFAULT)) { ++ /* Signature header corrupt/missing */ ++ rpmlog(RPMLOG_WARNING, _("Error verifying signature header\n")); ++ rpmtdFreeData(&td); ++ Header nh = headerCopy(*sigh); ++ headerFree(*sigh); ++ *sigh = headerLink(nh); ++ headerFree(nh); ++ } ++} ++ + static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + { + uint32_t diglen; +@@ -261,7 +277,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + uint32_t offset_ix = 0; + size_t len; + int next = 0; +- struct rpmlead_s l; ++ struct rpmlead_s l; + rpmfiles files = NULL; + rpmfi fi = NULL; + char *msg = NULL; +@@ -274,43 +290,47 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + + /* Skip conversion if package is in deny list */ + if (isInDenyList(l.name)) { ++ rpmlog(RPMLOG_WARNING, _("package %s is in deny list: conversion skipped\n"), l.name); + if (rpmLeadWrite(fdo, l)) { +- fprintf(stderr, _("Unable to write package lead: %s\n"), +- Fstrerror(fdo)); ++ rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"), ++ Fstrerror(fdo)); + rc = RPMRC_FAIL; + goto exit; + } + + ssize_t fdilength = ufdCopy(fdi, fdo); + if (fdilength == -1) { +- fprintf(stderr, _("process_package cat failed\n")); ++ rpmlog(RPMLOG_ERR, _("process_package cat failed\n")); + rc = RPMRC_FAIL; + goto exit; + } + + goto exit; + } else { +- if (rpmReadPackageRaw(fdi, &sigh, &h)) { +- rpmlog(RPMLOG_ERR, _("Error reading package\n")); +- exit(EXIT_FAILURE); +- } ++ if (rpmReadPackageRaw(fdi, &sigh, &h)) { ++ rpmlog(RPMLOG_ERR, _("Error reading package\n")); ++ exit(EXIT_FAILURE); ++ } + +- if (rpmLeadWriteFromHeader(fdo, h)) +- { +- rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"), +- Fstrerror(fdo)); +- exit(EXIT_FAILURE); +- } ++ sanitizeSignatureHeader(&sigh); + +- if (rpmWriteSignature(fdo, sigh)) { +- rpmlog(RPMLOG_ERR, _("Unable to write signature: %s\n"), Fstrerror(fdo)); +- exit(EXIT_FAILURE); +- } ++ if (rpmLeadWriteFromHeader(fdo, h)) { ++ rpmlog(RPMLOG_ERR, _("Unable to write package lead: %s\n"), ++ Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } + +- if (headerWrite(fdo, h, HEADER_MAGIC_YES)) { +- rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo)); +- exit(EXIT_FAILURE); +- } ++ if (rpmWriteSignature(fdo, sigh)) { ++ rpmlog(RPMLOG_ERR, _("Unable to write signature: %s\n"), ++ Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (headerWrite(fdo, h, HEADER_MAGIC_YES)) { ++ rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), ++ Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } + + /* Retrieve payload size and compression type. */ + { +@@ -323,10 +343,11 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ + free(rpmio_flags); + +- if (gzdi == NULL) { +- rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); +- exit(EXIT_FAILURE); +- } ++ if (gzdi == NULL) { ++ rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), ++ Fstrerror(gzdi)); ++ exit(EXIT_FAILURE); ++ } + + files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); + fi = rpmfiNewArchiveReader(gzdi, files, +@@ -348,124 +369,128 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) + + zeros = xcalloc(fundamental_block_size, 1); + +- while (next >= 0) { +- next = rpmfiNext(fi); +- if (next == RPMERR_ITER_END) { +- rc = RPMRC_OK; +- break; ++ while (next >= 0) { ++ next = rpmfiNext(fi); ++ if (next == RPMERR_ITER_END) { ++ rc = RPMRC_OK; ++ break; ++ } ++ mode = rpmfiFMode(fi); ++ if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) { ++ /* not a regular file, or the archive doesn't contain any content ++ * for this entry. ++ */ ++ continue; ++ } ++ digest = rpmfiFDigest(fi, NULL, NULL); ++ if (digestSetGetEntry(ds, digest, NULL)) { ++ /* This specific digest has already been included, so skip it. */ ++ continue; ++ } ++ pad = pad_to(pos, fundamental_block_size); ++ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { ++ rpmlog(RPMLOG_ERR, _("Unable to write padding\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ /* round up to next fundamental_block_size */ ++ pos += pad; ++ digestSetAddEntry(ds, digest); ++ offsets[offset_ix].digest = digest; ++ offsets[offset_ix].pos = pos; ++ offset_ix++; ++ size = rpmfiFSize(fi); ++ rc = rpmfiArchiveReadToFile(fi, fdo, 0); ++ if (rc != RPMRC_OK) { ++ char *errstr = rpmfileStrerror(rc); ++ rpmlog(RPMLOG_ERR, ++ _("rpmfiArchiveReadToFile failed while extracting " ++ "\"%s\" with RC %d: %s\n"), ++ rpmfiFN(fi), rc, errstr); ++ free(errstr); ++ goto exit; ++ } ++ pos += size; + } +- mode = rpmfiFMode(fi); +- if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) { +- /* not a regular file, or the archive doesn't contain any content +- * for this entry. +- */ +- continue; ++ Fclose(gzdi); /* XXX gzdi == fdi */ ++ ++ qsort(offsets, (size_t) offset_ix, sizeof(struct digestoffset), ++ digestoffsetCmp); ++ ++ validation_pos = pos; ++ ssize_t validation_len = ufdCopy(validationi, fdo); ++ if (validation_len == -1) { ++ rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n")); ++ rc = RPMRC_FAIL; ++ goto exit; + } +- digest = rpmfiFDigest(fi, NULL, NULL); +- if (digestSetGetEntry(ds, digest, NULL)) { +- /* This specific digest has already been included, so skip it. */ +- continue; ++ ++ digest_table_pos = validation_pos + validation_len; ++ ++ len = sizeof(offset_ix); ++ if (Fwrite(&offset_ix, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write length of table\n")); ++ rc = RPMRC_FAIL; ++ goto exit; + } +- pad = pad_to(pos, fundamental_block_size); +- if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { +- rpmlog(RPMLOG_ERR, _("Unable to write padding\n")); ++ len = sizeof(diglen); ++ if (Fwrite(&diglen, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write length of digest\n")); + rc = RPMRC_FAIL; + goto exit; + } +- /* round up to next fundamental_block_size */ +- pos += pad; +- digestSetAddEntry(ds, digest); +- offsets[offset_ix].digest = digest; +- offsets[offset_ix].pos = pos; +- offset_ix++; +- size = rpmfiFSize(fi); +- rc = rpmfiArchiveReadToFile(fi, fdo, 0); +- if (rc != RPMRC_OK) { +- char *errstr = rpmfileStrerror(rc); +- rpmlog(RPMLOG_ERR, +- _("rpmfiArchiveReadToFile failed while extracting "\ +- "\"%s\" with RC %d: %s\n"), +- rpmfiFN(fi), rc, errstr); +- free(errstr); ++ len = sizeof(rpm_loff_t); ++ for (int x = 0; x < offset_ix; x++) { ++ if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) { ++ rpmlog(RPMLOG_ERR, _("Unable to write digest\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write offset\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ } ++ digest_pos = ++ (digest_table_pos + sizeof(offset_ix) + sizeof(diglen) + ++ offset_ix * (diglen + sizeof(rpm_loff_t)) ++ ); ++ ++ ssize_t digest_len = ufdCopy(digestori, fdo); ++ if (digest_len == -1) { ++ rpmlog(RPMLOG_ERR, _("digest table ufdCopy failed\n")); ++ rc = RPMRC_FAIL; + goto exit; + } +- pos += size; +- } +- Fclose(gzdi); /* XXX gzdi == fdi */ +- +- qsort(offsets, (size_t) offset_ix, sizeof(struct digestoffset), +- digestoffsetCmp); + +- validation_pos = pos; +- ssize_t validation_len = ufdCopy(validationi, fdo); +- if (validation_len == -1) { +- rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- +- digest_table_pos = validation_pos + validation_len; ++ /* add more padding so the last file can be cloned. It doesn't matter that ++ * the table and validation etc are in this space. In fact, it's pretty ++ * efficient if it is. ++ */ + +- len = sizeof(offset_ix); +- if (Fwrite(&offset_ix, len, 1, fdo) != len) { +- rpmlog(RPMLOG_ERR, _("Unable to write length of table\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- len = sizeof(diglen); +- if (Fwrite(&diglen, len, 1, fdo) != len) { +- rpmlog(RPMLOG_ERR, _("Unable to write length of digest\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- len = sizeof(rpm_loff_t); +- for (int x = 0; x < offset_ix; x++) { +- if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) { +- rpmlog(RPMLOG_ERR, _("Unable to write digest\n")); ++ pad = ++ pad_to((validation_pos + validation_len + ++ 2 * sizeof(rpm_loff_t) + sizeof(uint64_t)), ++ fundamental_block_size); ++ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { ++ rpmlog(RPMLOG_ERR, _("Unable to write final padding\n")); + rc = RPMRC_FAIL; + goto exit; + } +- if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) { +- rpmlog(RPMLOG_ERR, _("Unable to write offset\n")); ++ zeros = _free(zeros); ++ struct extents_footer_t footer = {.offsets = ++ { validation_pos, digest_table_pos, digest_pos },.magic = ++ EXTENTS_MAGIC }; ++ len = sizeof(footer); ++ if (Fwrite(&footer, len, 1, fdo) != len) { ++ rpmlog(RPMLOG_ERR, _("Unable to write footer\n")); + rc = RPMRC_FAIL; + goto exit; + } + } +- digest_pos = ( +- digest_table_pos + sizeof(offset_ix) + sizeof(diglen) + +- offset_ix * (diglen + sizeof(rpm_loff_t)) +- ); +- +- ssize_t digest_len = ufdCopy(digestori, fdo); +- if (digest_len == -1) { +- rpmlog(RPMLOG_ERR, _("digest table ufdCopy failed\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- +- /* add more padding so the last file can be cloned. It doesn't matter that +- * the table and validation etc are in this space. In fact, it's pretty +- * efficient if it is. +- */ + +- pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + +- sizeof(uint64_t)), fundamental_block_size); +- if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { +- rpmlog(RPMLOG_ERR, _("Unable to write final padding\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- zeros = _free(zeros); +- struct extents_footer_t footer = {.offsets = {validation_pos, digest_table_pos, digest_pos}, .magic = EXTENTS_MAGIC}; +- len = sizeof(footer); +- if (Fwrite(&footer, len, 1, fdo) != len) { +- rpmlog(RPMLOG_ERR, _("Unable to write footer\n")); +- rc = RPMRC_FAIL; +- goto exit; +- } +- } +- +-exit: ++ exit: + rpmfilesFree(files); + rpmfiFree(fi); + headerFree(h); diff --git a/rpm.spec b/rpm.spec index 0edc5a1..d4ff062 100644 --- a/rpm.spec +++ b/rpm.spec @@ -42,7 +42,7 @@ %global rpmver 4.16.1.3 #global snapver rc1 -%global rel 19.1 +%global rel 19.2 %global sover 9 %global srcver %{rpmver}%{?snapver:-%{snapver}} @@ -181,6 +181,8 @@ Patch9927: 0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch Patch9928: 0028-reflink-remove-requirement-for-executable-stack-flag.patch Patch9929: 0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch Patch9930: 0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch +Patch9931: 0031-rpmcow-denylist.patch +Patch9932: 0032-rpmcow-workaround.patch Provides: rpm(pr1470) Provides: rpm(pr1470_1) @@ -812,6 +814,9 @@ fi %{_datadir}/selinux/packages/rpm_hs.pp.bz2 %changelog +* Wed Nov 30 2022 Richard Phibel - 4.16.1.3-19.2 +- Add deny list support and workaround for RPM CoW + * Sat Oct 29 2022 Richard Phibel - 4.16.1.3-19.1 - Merge upstream changes for Hyperscale