From: Richard Phibel <richardphibel@meta.com>
Subject: RPM with Copy on Write: add deny list mechanism
commit 3431550e6c92ba4bc6d091cb244f70c158dfbbaa
Author: Richard Phibel <richardphibel@meta.com>
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 <richardphibel@meta.com>
---
build/pack.c | 2 +-
lib/package.c | 6 +--
lib/rpmlead.c | 43 +++++++++---------
lib/rpmlead.h | 37 +++++++++++++++-
rpm2extents.c | 102 ++++++++++++++++++++++++++++++++-----------
sign/rpmgensig.c | 2 +-
tests/rpm2extents.at | 11 +++++
7 files changed, 148 insertions(+), 55 deletions(-)
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
@@ -410,11 +410,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
@@ -610,7 +610,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])