From ca8a4443beba86901937ea3ccbba500c5dbe897a Mon Sep 17 00:00:00 2001 From: Richard Phibel Date: Nov 30 2022 14:44:17 +0000 Subject: Add deny list support and workaround for RPM CoW 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. 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. --- diff --git a/SOURCES/0031-rpmcow-denylist.patch b/SOURCES/0031-rpmcow-denylist.patch new file mode 100644 index 0000000..da7aee3 --- /dev/null +++ b/SOURCES/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/SOURCES/0032-rpmcow-workaround.patch b/SOURCES/0032-rpmcow-workaround.patch new file mode 100644 index 0000000..69d5178 --- /dev/null +++ b/SOURCES/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/SPECS/rpm.spec b/SPECS/rpm.spec index e304eff..ab48010 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -42,7 +42,7 @@ %global rpmver 4.14.3 #global snapver rc2 -%global rel 24.2 +%global rel 24.3 %global srcver %{version}%{?snapver:-%{snapver}} %global srcdir %{?snapver:testing}%{!?snapver:%{name}-%(echo %{version} | cut -d'.' -f1-2).x} @@ -240,6 +240,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) @@ -837,6 +839,9 @@ make check || cat tests/rpmtests.log %doc doc/librpm/html/* %changelog +* Wed Nov 30 2022 Richard Phibel - 4.14.3-24.3 +- Add deny list support and workaround for RPM CoW + * Tue Nov 29 2022 Aleksandr Kazakov - 4.14.3-24.2 - Backport support for multi-threaded zstd compression