Blob Blame History Raw
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>

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