| |
@@ -0,0 +1,385 @@
|
| |
+ From: Richard Phibel <richardphibel@meta.com>
|
| |
+
|
| |
+ Subject: RPM with Copy on Write: workaround for corrupt signature header
|
| |
+
|
| |
+ commit 7976c921f60ec5d20c50c4c702ec5636b39210ba
|
| |
+ Author: Richard Phibel <richardphibel@meta.com>
|
| |
+ Date: Wed Oct 26 18:57:19 2022 +0200
|
| |
+
|
| |
+ RPM with Copy on Write: workaround for corrupt signature header
|
| |
+
|
| |
+ Some packages have errors in the signature header. These errors do not
|
| |
+ prevent installation of the package but cause issues in rpm2extents
|
| |
+ conversion. This commit implements the same fix as in
|
| |
+ unloadImmutableRegion.
|
| |
+
|
| |
+ Signed-off-by: Richard Phibel <richardphibel@meta.com>
|
| |
+
|
| |
+ 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);
|
| |
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.
CBS build: https://cbs.centos.org/koji/taskinfo?taskID=3113112