From 63275852afc4ab0bd0c45ac80f8f865c222759ac Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Mon, 31 May 2021 10:15:55 +0000 Subject: [PATCH 1/6] lib: temporarily fork rpmver logic This adds an internal temporary copy of the rpm version comparison logic from librpmio (LGPL). It allows relaxing the minimum library version to >= 4.14. --- Makefile-lib.am | 1 + configure.ac | 6 + src/lib/rpmostree-package.c | 7 +- src/lib/rpmver-private.c | 223 ++++++++++++++++++++++++++++++++++++ src/lib/rpmver-private.h | 108 +++++++++++++++++ 5 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 src/lib/rpmver-private.c create mode 100644 src/lib/rpmver-private.h diff --git a/Makefile-lib.am b/Makefile-lib.am index bef049e1e7..f9d67f5c57 100644 --- a/Makefile-lib.am +++ b/Makefile-lib.am @@ -28,6 +28,7 @@ librpmostree_1_la_SOURCES = \ src/lib/rpmostree.c \ src/lib/rpmostree-db.c \ src/lib/rpmostree-package.c \ + src/lib/rpmver-private.c \ $(NULL) librpmostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libpriv -I$(srcdir)/src/lib \ diff --git a/configure.ac b/configure.ac index e763781566..ef5551657f 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,12 @@ dnl RHEL8.1 has old libarchive AS_IF([pkg-config --atleast-version=3.3.3 libarchive], [AC_DEFINE([HAVE_LIBARCHIVE_ZSTD], 1, [Define if we have libarchive with zstd])]) +# rpmver is available in rpm >= 4.16, el8 is still on 4.14 +AC_SEARCH_LIBS([rpmverCmp], [rpm], + AC_DEFINE([BUILDOPT_HAVE_RPMVER], 1, [Whether rpmver API is available in librpmio]), + AC_DEFINE([BUILDOPT_HAVE_RPMVER], 0, [Whether rpmver API is available in librpmio]) +) + dnl We don't *actually* use this ourself, but librepo does, and libdnf gets confused dnl if librepo doesn't support it. have_zchunk=no diff --git a/src/lib/rpmostree-package.c b/src/lib/rpmostree-package.c index 8575c4a5ed..3bb03bfef0 100644 --- a/src/lib/rpmostree-package.c +++ b/src/lib/rpmostree-package.c @@ -32,12 +32,17 @@ #include #include -#include #include "libglnx.h" #include "rpmostree-shlib-ipc-private.h" #include "rpmostree-package-priv.h" +#if BUILDOPT_HAVE_RPMVER +#include +#else +#include "rpmver-private.h" +#endif + typedef GObjectClass RpmOstreePackageClass; /* Since the class is small, we perform a poor man's polymorphism; RpmOstreePackage objects diff --git a/src/lib/rpmver-private.c b/src/lib/rpmver-private.c new file mode 100644 index 0000000000..117df79206 --- /dev/null +++ b/src/lib/rpmver-private.c @@ -0,0 +1,223 @@ +// Temporary forked internal copy of: +// https://github.com/rpm-software-management/rpm/blob/rpm-4.16.1.3/rpmio/rpmver.c +// SPDX-License-Identifier: LGPL-2.1-or-later + +/* NOTE(lucab): compatibility changes to avoid touching the source. + * The *malloc functions have different behaviors when size=0, but this is not + * a concern here because all calls in this file have strictly-positive size. + */ +#include "config.h" +#if !BUILDOPT_HAVE_RPMVER +#include +#define xmalloc(_size) g_malloc((_size)) +#define free(_mem) g_free((_mem)) +#include "rpmver-private.h" + +#include +#include + +struct rpmver_s { + const char *e; + const char *v; + const char *r; + char arena[]; +}; + +/** + * Split EVR into epoch, version, and release components. + * @param evr [epoch:]version[-release] string + * @retval *ep pointer to epoch + * @retval *vp pointer to version + * @retval *rp pointer to release + */ +static +void parseEVR(char * evr, + const char ** ep, + const char ** vp, + const char ** rp) +{ + const char *epoch; + const char *version; /* assume only version is present */ + const char *release; + char *s, *se; + + s = evr; + while (*s && risdigit(*s)) s++; /* s points to epoch terminator */ + se = strrchr(s, '-'); /* se points to version terminator */ + + if (*s == ':') { + epoch = evr; + *s++ = '\0'; + version = s; + if (*epoch == '\0') epoch = "0"; + } else { + epoch = NULL; /* XXX disable epoch compare if missing */ + version = evr; + } + if (se) { + *se++ = '\0'; + release = se; + } else { + release = NULL; + } + + if (ep) *ep = epoch; + if (vp) *vp = version; + if (rp) *rp = release; +} + +int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2) +{ + int sense = 0; + int result = 0; + + /* Compare {A,B} [epoch:]version[-release] */ + if (v1->e && *v1->e && v2->e && *v2->e) + sense = rpmvercmp(v1->e, v2->e); + else if (v1->e && *v1->e && atol(v1->e) > 0) { + sense = 1; + } else if (v2->e && *v2->e && atol(v2->e) > 0) + sense = -1; + + if (sense == 0) { + sense = rpmvercmp(v1->v, v2->v); + if (sense == 0) { + if (v1->r && *v1->r && v2->r && *v2->r) { + sense = rpmvercmp(v1->r, v2->r); + } else { + /* always matches if the side with no release has SENSE_EQUAL */ + if ((v1->r && *v1->r && (f2 & RPMSENSE_EQUAL)) || + (v2->r && *v2->r && (f1 & RPMSENSE_EQUAL))) { + result = 1; + goto exit; + } + } + } + } + + /* Detect overlap of {A,B} range. */ + if (sense < 0 && ((f1 & RPMSENSE_GREATER) || (f2 & RPMSENSE_LESS))) { + result = 1; + } else if (sense > 0 && ((f1 & RPMSENSE_LESS) || (f2 & RPMSENSE_GREATER))) { + result = 1; + } else if (sense == 0 && + (((f1 & RPMSENSE_EQUAL) && (f2 & RPMSENSE_EQUAL)) || + ((f1 & RPMSENSE_LESS) && (f2 & RPMSENSE_LESS)) || + ((f1 & RPMSENSE_GREATER) && (f2 & RPMSENSE_GREATER)))) { + result = 1; + } + +exit: + return result; +} + +static int compare_values(const char *str1, const char *str2) +{ + if (!str1 && !str2) + return 0; + else if (str1 && !str2) + return 1; + else if (!str1 && str2) + return -1; + return rpmvercmp(str1, str2); +} + +int rpmverCmp(rpmver v1, rpmver v2) +{ + const char *e1 = (v1->e != NULL) ? v1->e : "0"; + const char *e2 = (v2->e != NULL) ? v2->e : "0"; + + int rc = compare_values(e1, e2); + if (!rc) { + rc = compare_values(v1->v, v2->v); + if (!rc) + rc = compare_values(v1->r, v2->r); + } + return rc; +} + +uint32_t rpmverEVal(rpmver rv) +{ + return (rv != NULL && rv->e != NULL) ? atol(rv->e) : 0; +} + +const char *rpmverE(rpmver rv) +{ + return (rv != NULL) ? rv->e : NULL; +} + +const char *rpmverV(rpmver rv) +{ + return (rv != NULL) ? rv->v : NULL; +} + +const char *rpmverR(rpmver rv) +{ + return (rv != NULL) ? rv->r : NULL; +} + +char *rpmverEVR(rpmver rv) +{ + char *EVR = NULL; + if (rv) { + rstrscat(&EVR, rv->e ? rv-> e : "", rv->e ? ":" : "", + rv->v, + rv->r ? "-" : "", rv->r ? rv->r : "", NULL); + } + return EVR; +} + +rpmver rpmverParse(const char *evr) +{ + rpmver rv = NULL; + if (evr && *evr) { + size_t evrlen = strlen(evr) + 1; + rv = xmalloc(sizeof(*rv) + evrlen); + memcpy(rv->arena, evr, evrlen); + parseEVR(rv->arena, &rv->e, &rv->v, &rv->r); + } + return rv; +} + +rpmver rpmverNew(const char *e, const char *v, const char *r) +{ + rpmver rv = NULL; + + if (v && *v) { + size_t nb = strlen(v) + 1; + nb += (e != NULL) ? strlen(e) + 1 : 0; + nb += (r != NULL) ? strlen(r) + 1 : 0; + rv = xmalloc(sizeof(*rv) + nb); + + rv->e = NULL; + rv->v = NULL; + rv->r = NULL; + + char *p = rv->arena; + if (e) { + rv->e = p; + p = stpcpy(p, e); + p++; + } + + rv->v = p; + p = stpcpy(p, v); + p++; + + if (r) { + rv->r = p; + p = stpcpy(p, r); + p++; + } + } + return rv; +} + +rpmver rpmverFree(rpmver rv) +{ + if (rv) { + free(rv); + } + return NULL; +} +#endif diff --git a/src/lib/rpmver-private.h b/src/lib/rpmver-private.h new file mode 100644 index 0000000000..f875c2f9d3 --- /dev/null +++ b/src/lib/rpmver-private.h @@ -0,0 +1,108 @@ +// Temporary forked internal copy of +// https://github.com/rpm-software-management/rpm/blob/rpm-4.16.1.3/rpmio/rpmver.h +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef _RPMVER_H +#define _RPMVER_H + +#include +#include /* sense flags */ + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE(lucab): Backported from . +#ifndef rpmver +typedef struct rpmver_s * rpmver; +#endif + +/** \ingroup rpmver + * Segmented string compare for version or release strings. + * + * @param a 1st string + * @param b 2nd string + * @return +1 if a is "newer", 0 if equal, -1 if b is "newer" + */ +int rpmvercmp(const char * a, const char * b); + +/** \ingroup rpmver + * Parse rpm version handle from evr string + * + * @param evr [epoch:]version[-release] string + * @return rpm version, NULL on invalid evr + */ +rpmver rpmverParse(const char *evr); + +/** \ingroup rpmver + * Create new rpm version handle from e, v, r components + * + * @param e epoch (or NULL) + * @param v version + * @param r release (or NULL) + * @return rpm version, NULL on invalid + */ +rpmver rpmverNew(const char *e, const char *v, const char *r); + +/** \ingroup rpmver + * Free rpm version handle + * + * @param rv rpm version handle + * @return NULL always + */ +rpmver rpmverFree(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return numerical value of epoch + */ +uint32_t rpmverEVal(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return epoch portion + */ +const char *rpmverE(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return version portion + */ +const char *rpmverV(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return release portion + */ +const char *rpmverR(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return formatted [E:]V[-R] string (malloced) + */ +char *rpmverEVR(rpmver rv); + +/** \ingroup rpmver + * Compare two rpm version handles + * + * @param v1 1st version handle + * @param v2 2nd version handle + * @return 0 if equal, -1 if v1 smaller, 1 if greater, than v2 + */ +int rpmverCmp(rpmver v1, rpmver v2); + +/** \ingroup rpmver + * Determine whether two versioned ranges overlap. + * @param v1 1st version + * @param f1 1st sense flags + * @param v2 2nd version + * @param f2 2nd sense flags + * @return 1 if ranges overlap, 0 otherwise + */ +int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMVER_H */ From a74a3163b2ae315b5f95d9d7642e868794c47628 Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Mon, 31 May 2021 11:00:10 +0000 Subject: [PATCH 2/6] lib/rpmver: replace tabs with spaces --- src/lib/rpmver-private.c | 160 +++++++++++++++++++-------------------- src/lib/rpmver-private.h | 60 +++++++-------- 2 files changed, 110 insertions(+), 110 deletions(-) diff --git a/src/lib/rpmver-private.c b/src/lib/rpmver-private.c index 117df79206..6882be0d1e 100644 --- a/src/lib/rpmver-private.c +++ b/src/lib/rpmver-private.c @@ -25,40 +25,40 @@ struct rpmver_s { /** * Split EVR into epoch, version, and release components. - * @param evr [epoch:]version[-release] string - * @retval *ep pointer to epoch - * @retval *vp pointer to version - * @retval *rp pointer to release + * @param evr [epoch:]version[-release] string + * @retval *ep pointer to epoch + * @retval *vp pointer to version + * @retval *rp pointer to release */ static void parseEVR(char * evr, - const char ** ep, - const char ** vp, - const char ** rp) + const char ** ep, + const char ** vp, + const char ** rp) { const char *epoch; - const char *version; /* assume only version is present */ + const char *version; /* assume only version is present */ const char *release; char *s, *se; s = evr; - while (*s && risdigit(*s)) s++; /* s points to epoch terminator */ - se = strrchr(s, '-'); /* se points to version terminator */ + while (*s && risdigit(*s)) s++; /* s points to epoch terminator */ + se = strrchr(s, '-'); /* se points to version terminator */ if (*s == ':') { - epoch = evr; - *s++ = '\0'; - version = s; - if (*epoch == '\0') epoch = "0"; + epoch = evr; + *s++ = '\0'; + version = s; + if (*epoch == '\0') epoch = "0"; } else { - epoch = NULL; /* XXX disable epoch compare if missing */ - version = evr; + epoch = NULL; /* XXX disable epoch compare if missing */ + version = evr; } if (se) { - *se++ = '\0'; - release = se; + *se++ = '\0'; + release = se; } else { - release = NULL; + release = NULL; } if (ep) *ep = epoch; @@ -73,38 +73,38 @@ int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2) /* Compare {A,B} [epoch:]version[-release] */ if (v1->e && *v1->e && v2->e && *v2->e) - sense = rpmvercmp(v1->e, v2->e); + sense = rpmvercmp(v1->e, v2->e); else if (v1->e && *v1->e && atol(v1->e) > 0) { - sense = 1; + sense = 1; } else if (v2->e && *v2->e && atol(v2->e) > 0) - sense = -1; + sense = -1; if (sense == 0) { - sense = rpmvercmp(v1->v, v2->v); - if (sense == 0) { - if (v1->r && *v1->r && v2->r && *v2->r) { - sense = rpmvercmp(v1->r, v2->r); - } else { - /* always matches if the side with no release has SENSE_EQUAL */ - if ((v1->r && *v1->r && (f2 & RPMSENSE_EQUAL)) || - (v2->r && *v2->r && (f1 & RPMSENSE_EQUAL))) { - result = 1; - goto exit; - } - } - } + sense = rpmvercmp(v1->v, v2->v); + if (sense == 0) { + if (v1->r && *v1->r && v2->r && *v2->r) { + sense = rpmvercmp(v1->r, v2->r); + } else { + /* always matches if the side with no release has SENSE_EQUAL */ + if ((v1->r && *v1->r && (f2 & RPMSENSE_EQUAL)) || + (v2->r && *v2->r && (f1 & RPMSENSE_EQUAL))) { + result = 1; + goto exit; + } + } + } } /* Detect overlap of {A,B} range. */ if (sense < 0 && ((f1 & RPMSENSE_GREATER) || (f2 & RPMSENSE_LESS))) { - result = 1; + result = 1; } else if (sense > 0 && ((f1 & RPMSENSE_LESS) || (f2 & RPMSENSE_GREATER))) { - result = 1; + result = 1; } else if (sense == 0 && - (((f1 & RPMSENSE_EQUAL) && (f2 & RPMSENSE_EQUAL)) || - ((f1 & RPMSENSE_LESS) && (f2 & RPMSENSE_LESS)) || - ((f1 & RPMSENSE_GREATER) && (f2 & RPMSENSE_GREATER)))) { - result = 1; + (((f1 & RPMSENSE_EQUAL) && (f2 & RPMSENSE_EQUAL)) || + ((f1 & RPMSENSE_LESS) && (f2 & RPMSENSE_LESS)) || + ((f1 & RPMSENSE_GREATER) && (f2 & RPMSENSE_GREATER)))) { + result = 1; } exit: @@ -114,11 +114,11 @@ int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2) static int compare_values(const char *str1, const char *str2) { if (!str1 && !str2) - return 0; + return 0; else if (str1 && !str2) - return 1; + return 1; else if (!str1 && str2) - return -1; + return -1; return rpmvercmp(str1, str2); } @@ -129,9 +129,9 @@ int rpmverCmp(rpmver v1, rpmver v2) int rc = compare_values(e1, e2); if (!rc) { - rc = compare_values(v1->v, v2->v); - if (!rc) - rc = compare_values(v1->r, v2->r); + rc = compare_values(v1->v, v2->v); + if (!rc) + rc = compare_values(v1->r, v2->r); } return rc; } @@ -160,9 +160,9 @@ char *rpmverEVR(rpmver rv) { char *EVR = NULL; if (rv) { - rstrscat(&EVR, rv->e ? rv-> e : "", rv->e ? ":" : "", - rv->v, - rv->r ? "-" : "", rv->r ? rv->r : "", NULL); + rstrscat(&EVR, rv->e ? rv-> e : "", rv->e ? ":" : "", + rv->v, + rv->r ? "-" : "", rv->r ? rv->r : "", NULL); } return EVR; } @@ -171,10 +171,10 @@ rpmver rpmverParse(const char *evr) { rpmver rv = NULL; if (evr && *evr) { - size_t evrlen = strlen(evr) + 1; - rv = xmalloc(sizeof(*rv) + evrlen); - memcpy(rv->arena, evr, evrlen); - parseEVR(rv->arena, &rv->e, &rv->v, &rv->r); + size_t evrlen = strlen(evr) + 1; + rv = xmalloc(sizeof(*rv) + evrlen); + memcpy(rv->arena, evr, evrlen); + parseEVR(rv->arena, &rv->e, &rv->v, &rv->r); } return rv; } @@ -184,31 +184,31 @@ rpmver rpmverNew(const char *e, const char *v, const char *r) rpmver rv = NULL; if (v && *v) { - size_t nb = strlen(v) + 1; - nb += (e != NULL) ? strlen(e) + 1 : 0; - nb += (r != NULL) ? strlen(r) + 1 : 0; - rv = xmalloc(sizeof(*rv) + nb); - - rv->e = NULL; - rv->v = NULL; - rv->r = NULL; - - char *p = rv->arena; - if (e) { - rv->e = p; - p = stpcpy(p, e); - p++; - } - - rv->v = p; - p = stpcpy(p, v); - p++; - - if (r) { - rv->r = p; - p = stpcpy(p, r); - p++; - } + size_t nb = strlen(v) + 1; + nb += (e != NULL) ? strlen(e) + 1 : 0; + nb += (r != NULL) ? strlen(r) + 1 : 0; + rv = xmalloc(sizeof(*rv) + nb); + + rv->e = NULL; + rv->v = NULL; + rv->r = NULL; + + char *p = rv->arena; + if (e) { + rv->e = p; + p = stpcpy(p, e); + p++; + } + + rv->v = p; + p = stpcpy(p, v); + p++; + + if (r) { + rv->r = p; + p = stpcpy(p, r); + p++; + } } return rv; } @@ -216,7 +216,7 @@ rpmver rpmverNew(const char *e, const char *v, const char *r) rpmver rpmverFree(rpmver rv) { if (rv) { - free(rv); + free(rv); } return NULL; } diff --git a/src/lib/rpmver-private.h b/src/lib/rpmver-private.h index f875c2f9d3..458279e202 100644 --- a/src/lib/rpmver-private.h +++ b/src/lib/rpmver-private.h @@ -6,7 +6,7 @@ #define _RPMVER_H #include -#include /* sense flags */ +#include /* sense flags */ #ifdef __cplusplus extern "C" { @@ -20,84 +20,84 @@ typedef struct rpmver_s * rpmver; /** \ingroup rpmver * Segmented string compare for version or release strings. * - * @param a 1st string - * @param b 2nd string - * @return +1 if a is "newer", 0 if equal, -1 if b is "newer" + * @param a 1st string + * @param b 2nd string + * @return +1 if a is "newer", 0 if equal, -1 if b is "newer" */ int rpmvercmp(const char * a, const char * b); /** \ingroup rpmver * Parse rpm version handle from evr string * - * @param evr [epoch:]version[-release] string - * @return rpm version, NULL on invalid evr + * @param evr [epoch:]version[-release] string + * @return rpm version, NULL on invalid evr */ rpmver rpmverParse(const char *evr); /** \ingroup rpmver * Create new rpm version handle from e, v, r components * - * @param e epoch (or NULL) - * @param v version - * @param r release (or NULL) - * @return rpm version, NULL on invalid + * @param e epoch (or NULL) + * @param v version + * @param r release (or NULL) + * @return rpm version, NULL on invalid */ rpmver rpmverNew(const char *e, const char *v, const char *r); /** \ingroup rpmver * Free rpm version handle * - * @param rv rpm version handle - * @return NULL always + * @param rv rpm version handle + * @return NULL always */ rpmver rpmverFree(rpmver rv); /** \ingroup rpmver - * @param rv rpm version handle - * @return numerical value of epoch + * @param rv rpm version handle + * @return numerical value of epoch */ uint32_t rpmverEVal(rpmver rv); /** \ingroup rpmver - * @param rv rpm version handle - * @return epoch portion + * @param rv rpm version handle + * @return epoch portion */ const char *rpmverE(rpmver rv); /** \ingroup rpmver - * @param rv rpm version handle - * @return version portion + * @param rv rpm version handle + * @return version portion */ const char *rpmverV(rpmver rv); /** \ingroup rpmver - * @param rv rpm version handle - * @return release portion + * @param rv rpm version handle + * @return release portion */ const char *rpmverR(rpmver rv); /** \ingroup rpmver - * @param rv rpm version handle - * @return formatted [E:]V[-R] string (malloced) + * @param rv rpm version handle + * @return formatted [E:]V[-R] string (malloced) */ char *rpmverEVR(rpmver rv); /** \ingroup rpmver * Compare two rpm version handles * - * @param v1 1st version handle - * @param v2 2nd version handle - * @return 0 if equal, -1 if v1 smaller, 1 if greater, than v2 + * @param v1 1st version handle + * @param v2 2nd version handle + * @return 0 if equal, -1 if v1 smaller, 1 if greater, than v2 */ int rpmverCmp(rpmver v1, rpmver v2); /** \ingroup rpmver * Determine whether two versioned ranges overlap. - * @param v1 1st version - * @param f1 1st sense flags - * @param v2 2nd version - * @param f2 2nd sense flags - * @return 1 if ranges overlap, 0 otherwise + * @param v1 1st version + * @param f1 1st sense flags + * @param v2 2nd version + * @param f2 2nd sense flags + * @return 1 if ranges overlap, 0 otherwise */ int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2);