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