diff --git a/.gitignore b/.gitignore
index 448633a..b822708 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/rpm-ostree-2020.7.tar.xz
+SOURCES/rpm-ostree-2021.5.tar.xz
diff --git a/.rpm-ostree.metadata b/.rpm-ostree.metadata
index 5457220..e49e9f6 100644
--- a/.rpm-ostree.metadata
+++ b/.rpm-ostree.metadata
@@ -1 +1 @@
-d17719c3f3574bc49348124d5636035a55c8ccdc SOURCES/rpm-ostree-2020.7.tar.xz
+0bd34ca2600986c3416961ee028946d0e68da21f SOURCES/rpm-ostree-2021.5.tar.xz
diff --git a/SOURCES/0001-github-jmesmon-rust-systemd-pr200.patch b/SOURCES/0001-github-jmesmon-rust-systemd-pr200.patch
new file mode 100644
index 0000000..631f458
--- /dev/null
+++ b/SOURCES/0001-github-jmesmon-rust-systemd-pr200.patch
@@ -0,0 +1,87 @@
+diff --git rpm-ostree-2021.5/vendor/libsystemd-sys/.cargo-checksum.json rpm-ostree-2021.5/vendor/libsystemd-sys/.cargo-checksum.json
+index ef0104f..18671cd 100644
+--- rpm-ostree-2021.5/vendor/libsystemd-sys/.cargo-checksum.json
++++ rpm-ostree-2021.5/vendor/libsystemd-sys/.cargo-checksum.json
+@@ -1 +1 @@
+-{"files": {"Cargo.toml": "29598137eb17a38f964952574d6f13f17d30b1f6d262a35ae5a1e9cc6f7ee180", "build.rs": "cb99af17b48e1874905513f956e99a1e5d7d55305b4d587c219719481d37ba1c", "src/bus/mod.rs": "ecd83596dd27674a43369dbeb88b9446e0907abb146e9f17e0499cda77aec22c", "src/bus/protocol.rs": "db8b3f47ba396b12868b4700b627683748682936ad4aad98ec4489c7c29450bd", "src/bus/vtable.rs": "547c322b93466b1919de3086304fa7f0ed650bde3556b349b5eebf35ec5fd0d1", "src/daemon.rs": "8ac4c744fe37beca7dfcd3e3d2ccab7150a709969d41ed38b980635a4f585f4b", "src/event.rs": "defaeb91eaddf36aa848e05d54def27351e35605dde5b451230866ce35c8a242", "src/id128.rs": "b7d7767fef5e2799450f694a4b7bfaf483819ffdc6b1fc3b9b21b41d0e9cfa30", "src/journal.rs": "af4a90da1bf57d2601248588766df3ad92a44558e026092850e7b555088787fa", "src/lib.rs": "7239092e42a3e7d0039f8436402a946a2cc8ffd3070fe4b6d81e255533cc4088", "src/login.rs": "eae9145e5ffe7f5050c58fa4757202a6eb5a5885b52d8ff1ebe44484608065c8", "tests/journal-send.rs": "ca18cc28bad06f8a55e910501de9681b410718afb6e1bd603c6b5063a07b6a63"}, "package": "6e03fd580bcecda68dcdcd5297085ade6a3dc552cd8b030d2b94a9b089ef7ab8"}
+\ No newline at end of file
++{"files": {}, "package": "6e03fd580bcecda68dcdcd5297085ade6a3dc552cd8b030d2b94a9b089ef7ab8"}
+diff --git rpm-ostree-2021.5/vendor/libsystemd-sys/src/journal.rs rpm-ostree-2021.5/vendor/libsystemd-sys/src/journal.rs
+index 5980b81..68e4e68 100644
+--- rpm-ostree-2021.5/vendor/libsystemd-sys/src/journal.rs
++++ rpm-ostree-2021.5/vendor/libsystemd-sys/src/journal.rs
+@@ -27,11 +27,6 @@ extern "C" {
+     // (we don't need to do c-style format strings)
+ 
+     pub fn sd_journal_open(ret: *mut *mut sd_journal, flags: c_int) -> c_int;
+-    pub fn sd_journal_open_namespace(
+-        ret: *mut *mut sd_journal,
+-        namespace: *const c_char,
+-        flags: c_int,
+-    ) -> c_int;
+     pub fn sd_journal_open_directory(
+         ret: *mut *mut sd_journal,
+         path: *const c_char,
+diff --git rpm-ostree-2021.5/vendor/systemd/.cargo-checksum.json rpm-ostree-2021.5/vendor/systemd/.cargo-checksum.json
+index bc8d44c..9136865 100644
+--- rpm-ostree-2021.5/vendor/systemd/.cargo-checksum.json
++++ rpm-ostree-2021.5/vendor/systemd/.cargo-checksum.json
+@@ -1 +1 @@
+-{"files": {"Cargo.toml": "70c57c11076fa8ba3826afe5db35c07ebc7312286ac4586b5387dc80d2adf719", "README.md": "13664522a229dc1e862dc11dd1f840cf4aa238c64578a0fc906f0caa3f7f494d", "src/bus/mod.rs": "559f1e10c6eb881629cfa3eb936890cdfbe1a44c186de95ea720c765be83cb39", "src/bus/types.rs": "25abd29a6c8695a37f42e708aa6cf26d7f833941742a80a78b3fc9012ad9e981", "src/daemon.rs": "f8d5e7584ea6543c3b5d8f45033d125af4511d8a0014f783944323458b693db3", "src/id128.rs": "a58b281ab0e52dd378bffd3fe161b9c73f2983a795e951ed70b3b0a8b333c62f", "src/journal.rs": "2281b81cfd7dc6c614b7a5fea4a4851a0f61e1dc780b11e10270264398d00345", "src/lib.rs": "6ee64a876e7b4157b2edea726e4bdf086f294df5a44c4d7eb09eee8883945755", "src/login.rs": "abed971dba12598f062cb78da3fc97e3c73db71eab3dade46c4f68fe9e4d7ca4"}, "package": "f722cabda922e471742300045f56dbaa53fafbb4520fca304e51258019bfe91d"}
+\ No newline at end of file
++{"files": {}, "package": "f722cabda922e471742300045f56dbaa53fafbb4520fca304e51258019bfe91d"}
+diff --git rpm-ostree-2021.5/vendor/systemd/src/journal.rs rpm-ostree-2021.5/vendor/systemd/src/journal.rs
+index c49e891..6757a40 100644
+--- rpm-ostree-2021.5/vendor/systemd/src/journal.rs
++++ rpm-ostree-2021.5/vendor/systemd/src/journal.rs
+@@ -396,22 +396,7 @@ impl OpenOptions {
+     ///
+     /// `sd_journal_open()`: https://www.freedesktop.org/software/systemd/man/sd_journal_open.html
+     pub fn open(&self) -> Result<Journal> {
+-        Journal::open_with_opts_ns::<&std::ffi::CStr>(None, self)
+-    }
+-
+-    /// Open the log journal for reading in the given namespace. Entries included are dependent on
+-    /// options.
+-    ///
+-    /// Note that some options (`SD_JOURNAL_ALL_NAMESPACES`) affect whether `namespace` is
+-    /// considered. Our API doesn't check for unused data here, but users are encouraged to avoid
+-    /// passing unused data by using [`OpenOptions::open()`] instead when a namespace argument is
+-    /// not required.
+-    ///
+-    /// This corresponds to [`sd_journal_open_namespace()`]
+-    ///
+-    /// `sd_journal_open_namespace()`: https://www.freedesktop.org/software/systemd/man/sd_journal_open.html
+-    pub fn open_namespace<A: CStrArgument>(&self, namespace: A) -> Result<Journal> {
+-        Journal::open_with_opts_ns(Some(namespace), self)
++        Journal::open_with_opts::<&std::ffi::CStr>(self)
+     }
+ }
+ 
+@@ -508,10 +493,7 @@ impl OpenFilesOptions {
+ }
+ 
+ impl Journal {
+-    fn open_with_opts_ns<A: CStrArgument>(
+-        namespace: Option<A>,
+-        opts: &OpenOptions,
+-    ) -> Result<Journal> {
++    fn open_with_opts<A: CStrArgument>(opts: &OpenOptions) -> Result<Journal> {
+         let mut flags = opts.extra_raw_flags;
+         if opts.current_user {
+             flags |= ffi::SD_JOURNAL_CURRENT_USER;
+@@ -535,13 +517,8 @@ impl Journal {
+             flags |= ffi::SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
+         }
+ 
+-        let ns = namespace.map(|a| a.into_cstr());
+-        let ns_p = ns
+-            .as_ref()
+-            .map(|a| a.as_ref().as_ptr())
+-            .unwrap_or(ptr::null());
+         let mut jp = MaybeUninit::uninit();
+-        crate::ffi_result(unsafe { ffi::sd_journal_open_namespace(jp.as_mut_ptr(), ns_p, flags) })?;
++        crate::ffi_result(unsafe { ffi::sd_journal_open(jp.as_mut_ptr(), flags) })?;
+         Ok(unsafe { Journal::from_ptr(jp.assume_init()) })
+     }
+ 
diff --git a/SOURCES/0002-github-coreos-rpm-ostree-pr2869.patch b/SOURCES/0002-github-coreos-rpm-ostree-pr2869.patch
new file mode 100644
index 0000000..78cf393
--- /dev/null
+++ b/SOURCES/0002-github-coreos-rpm-ostree-pr2869.patch
@@ -0,0 +1,798 @@
+From 63275852afc4ab0bd0c45ac80f8f865c222759ac Mon Sep 17 00:00:00 2001
+From: Luca BRUNO <luca.bruno@coreos.com>
+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 <string.h>
+ #include <stdlib.h>
+-#include <rpm/rpmver.h>
+ #include "libglnx.h"
+ 
+ #include "rpmostree-shlib-ipc-private.h"
+ #include "rpmostree-package-priv.h"
+ 
++#if BUILDOPT_HAVE_RPMVER
++#include <rpm/rpmver.h>
++#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 <glib.h>
++#define xmalloc(_size) g_malloc((_size))
++#define free(_mem) g_free((_mem))
++#include "rpmver-private.h"
++
++#include <rpm/rpmstring.h>
++#include <stdlib.h>
++
++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 <rpm/rpmtypes.h>
++#include <rpm/rpmds.h>		/* sense flags */
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++// NOTE(lucab): Backported from <rpm/rpmtypes.h>.
++#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 <luca.bruno@coreos.com>
+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 <rpm/rpmtypes.h>
+-#include <rpm/rpmds.h>		/* sense flags */
++#include <rpm/rpmds.h>                /* 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);
+ 
diff --git a/SOURCES/0003-github-coreos-rpm-ostree-pr2870.patch b/SOURCES/0003-github-coreos-rpm-ostree-pr2870.patch
new file mode 100644
index 0000000..c0f7f1c
--- /dev/null
+++ b/SOURCES/0003-github-coreos-rpm-ostree-pr2870.patch
@@ -0,0 +1,29 @@
+From c497411e3bc24538a26bc249245ebcebe8fff337 Mon Sep 17 00:00:00 2001
+From: Luca BRUNO <luca.bruno@coreos.com>
+Date: Wed, 2 Jun 2021 11:53:54 +0000
+Subject: [PATCH] makefile/lib: filter out private sources from gir list
+
+This excludes C source files too (instead of just headers) from the list of
+source files scanned by gir.
+It fixes the following error:
+```
+src/lib/rpmver-private.c:27: Error: RpmOstree: identifier not found on the first line:
+ * Split EVR into epoch, version, and release components.
+```
+---
+ Makefile-lib.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile-lib.am b/Makefile-lib.am
+index f9d67f5c5..cc4b4de61 100644
+--- a/Makefile-lib.am
++++ b/Makefile-lib.am
+@@ -44,7 +44,7 @@ RpmOstree_1_0_gir_INCLUDES = OSTree-1.0 Gio-2.0
+ RpmOstree_1_0_gir_CFLAGS = $(librpmostree_1_la_CFLAGS)
+ RpmOstree_1_0_gir_LIBS = librpmostree-1.la
+ RpmOstree_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=RpmOstree --symbol-prefix=rpm_ostree
+-RpmOstree_1_0_gir_FILES = $(librpmostreeinclude_HEADERS) $(filter-out %-private.h,$(librpmostree_1_la_SOURCES))
++RpmOstree_1_0_gir_FILES = $(librpmostreeinclude_HEADERS) $(filter-out %-private.c %-private.h,$(librpmostree_1_la_SOURCES))
+ INTROSPECTION_GIRS += RpmOstree-1.0.gir
+ gir_DATA += RpmOstree-1.0.gir
+ typelib_DATA += RpmOstree-1.0.typelib
diff --git a/SOURCES/0004-github-coreos-rpm-ostree-pr2872.patch b/SOURCES/0004-github-coreos-rpm-ostree-pr2872.patch
new file mode 100644
index 0000000..4beefd5
--- /dev/null
+++ b/SOURCES/0004-github-coreos-rpm-ostree-pr2872.patch
@@ -0,0 +1,97 @@
+From eca59b67b3350d3b49db5844eb143b428c95ca37 Mon Sep 17 00:00:00 2001
+From: Luca BRUNO <luca.bruno@coreos.com>
+Date: Wed, 2 Jun 2021 12:40:44 +0000
+Subject: [PATCH 1/2] libdnf-sys/build: gracefully handle older gpgme versions
+
+This tries to gracefully handle environments where gpgme
+library cannot be directly discovered via pkg-config.
+Older gpgme versions (including 1.13) do not provide a
+.pc file, so in that case the build-script attempts to
+fallback to hardcoded library flags.
+---
+ rust/libdnf-sys/Cargo.toml | 3 ++-
+ rust/libdnf-sys/build.rs   | 5 +++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/rust/libdnf-sys/Cargo.toml b/rust/libdnf-sys/Cargo.toml
+index 8695ad8c08..e20f04d0c8 100644
+--- a/rust/libdnf-sys/Cargo.toml
++++ b/rust/libdnf-sys/Cargo.toml
+@@ -26,7 +26,8 @@ rpm = "4"
+ librepo = "1"
+ libsolv = "0.7"
+ libsolvext = "0.7"
+-gpgme = "1"
++# Older libgpgme did not provide a pkg-config file
++gpgme = { version = "1", optional = true }
+ openssl = "1"
+ libcurl = "7"
+ sqlite3 = "3"
+diff --git a/rust/libdnf-sys/build.rs b/rust/libdnf-sys/build.rs
+index 43f61a3544..ec86ca6a6f 100644
+--- a/rust/libdnf-sys/build.rs
++++ b/rust/libdnf-sys/build.rs
+@@ -3,6 +3,7 @@ use anyhow::Result;
+ 
+ fn main() -> Result<()> {
+     let libs = system_deps::Config::new().probe()?;
++    let has_gpgme_pkgconfig = libs.get_by_name("gpgme").is_some();
+ 
+     // first, the submodule proper
+     let libdnf = cmake::Config::new("../../libdnf")
+@@ -31,6 +32,10 @@ fn main() -> Result<()> {
+         .always_configure(false)
+         .build_target("all")
+         .build();
++    // NOTE(lucab): consider using `gpgme-config` it this stops working.
++    if !has_gpgme_pkgconfig {
++        println!("cargo:rustc-link-lib=gpgme");
++    }
+     println!(
+         "cargo:rustc-link-search=native={}/build/libdnf",
+         libdnf.display()
+
+From d874de293c27f2ed762f212ab65fb054a8006fc3 Mon Sep 17 00:00:00 2001
+From: Luca BRUNO <luca.bruno@coreos.com>
+Date: Wed, 2 Jun 2021 13:10:24 +0000
+Subject: [PATCH 2/2] libdnf-sys/build: auto-enable zchunk if present
+
+This adds an optional dependencies on the `zck` library (zchunk),
+and automatically forwards it to libdnf configuration.
+It allows to gracefully degrade in older environments where zchunk
+is not present, and also matches autoconf behavior.
+---
+ rust/libdnf-sys/Cargo.toml | 1 +
+ rust/libdnf-sys/build.rs   | 3 +++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/rust/libdnf-sys/Cargo.toml b/rust/libdnf-sys/Cargo.toml
+index e20f04d0c8..13e0eca318 100644
+--- a/rust/libdnf-sys/Cargo.toml
++++ b/rust/libdnf-sys/Cargo.toml
+@@ -34,3 +34,4 @@ sqlite3 = "3"
+ modulemd = { name = "modulemd-2.0", version = "2" }
+ jsonc = { name = "json-c", version = "0" }
+ glib = { name = "glib-2.0", version = "2" }
++zck = { version = "0.9", optional = true }
+diff --git a/rust/libdnf-sys/build.rs b/rust/libdnf-sys/build.rs
+index ec86ca6a6f..df07cc7cb7 100644
+--- a/rust/libdnf-sys/build.rs
++++ b/rust/libdnf-sys/build.rs
+@@ -4,6 +4,7 @@ use anyhow::Result;
+ fn main() -> Result<()> {
+     let libs = system_deps::Config::new().probe()?;
+     let has_gpgme_pkgconfig = libs.get_by_name("gpgme").is_some();
++    let with_zck: u8 = libs.get_by_name("zck").is_some().into();
+ 
+     // first, the submodule proper
+     let libdnf = cmake::Config::new("../../libdnf")
+@@ -24,6 +25,8 @@ fn main() -> Result<()> {
+         // We don't need docs
+         .define("WITH_HTML:BOOL", "0")
+         .define("WITH_MAN:BOOL", "0")
++        // Auto-enable zchunk, if present
++        .define("WITH_ZCHUNK:BOOL", format!("{}", with_zck))
+         // Don't need bindings
+         .define("WITH_BINDINGS:BOOL", "0")
+         // Needed in Koji at least because timestamps(?)
diff --git a/SPECS/rpm-ostree.spec b/SPECS/rpm-ostree.spec
index 47be5eb..f5d565e 100644
--- a/SPECS/rpm-ostree.spec
+++ b/SPECS/rpm-ostree.spec
@@ -1,30 +1,46 @@
 # The canonical copy of this spec file is upstream at:
-# https://github.com/projectatomic/rpm-ostree/blob/master/packaging/rpm-ostree.spec.in
+# https://github.com/coreos/rpm-ostree/blob/main/packaging/rpm-ostree.spec.in
 
 Summary: Hybrid image/package system
 Name: rpm-ostree
-Version: 2020.7
-Release: 3%{?dist}
-#VCS: https://github.com/cgwalters/rpm-ostree
-# This tarball is generated via "cd packaging && make -f Makefile.dist-packaging dist-snapshot"
-# in the upstream git.  If rust is enabled, it contains vendored sources.
-Source0: rpm-ostree-%{version}.tar.xz
+Version: 2021.5
+Release: 1%{?dist}
 License: LGPLv2+
-URL: https://github.com/projectatomic/rpm-ostree
+URL: https://github.com/coreos/rpm-ostree
+# This tarball is generated via "cd packaging && make -f Makefile.dist-packaging dist-snapshot"
+# in the upstream git.  It also contains vendored Rust sources.
+Source0: https://github.com/coreos/rpm-ostree/releases/download/v%{version}/rpm-ostree-%{version}.tar.xz
 
-%if !%{defined rust_arches}
-# It's not defined yet in the base CentOS7 root
-%define rust_arches x86_64 i686 armv7hl aarch64 ppc64 ppc64le s390x
-%endif # defined rust_arches
+Patch01:  0001-github-jmesmon-rust-systemd-pr200.patch
+Patch02:  0002-github-coreos-rpm-ostree-pr2869.patch
+Patch03:  0003-github-coreos-rpm-ostree-pr2870.patch
+Patch04:  0004-github-coreos-rpm-ostree-pr2872.patch
 
 ExclusiveArch: %{rust_arches}
 
-%if 0%{?fedora}
+BuildRequires: make
+%if 0%{?rhel} && !0%{?eln}
+BuildRequires: rust-toolset
+%else
+BuildRequires: rust-packaging
 BuildRequires: cargo
 BuildRequires: rust
+%endif
+
+# Enable ASAN + UBSAN
+%bcond_with sanitizers
+
+# RHEL8 doesn't ship zchunk today.  See also the comments
+# in configure.ac around this as libdnf/librepo need to be in
+# sync, and today we bundle libdnf but not librepo.
+%if 0%{?rhel} && 0%{?rhel} <= 8
+%bcond_with zchunk
 %else
-# assume el8
-BuildRequires: rust-toolset
+%bcond_without zchunk
+%endif
+
+%if 0%{?fedora} >= 34
+%define sqlite_rpmdb_default "--enable-sqlite-rpmdb-default"
 %endif
 
 # For the autofiles bits below
@@ -34,54 +50,60 @@ BuildRequires: autoconf automake libtool git
 # For docs
 BuildRequires: chrpath
 BuildRequires: gtk-doc
-BuildRequires: gperf
 BuildRequires: gnome-common
 BuildRequires: /usr/bin/g-ir-scanner
 # Core requirements
 # One way to check this: `objdump -p /path/to/rpm-ostree | grep LIBOSTREE` and pick the highest (though that might miss e.g. new struct members)
-BuildRequires: pkgconfig(ostree-1) >= 2019.2
+BuildRequires: pkgconfig(ostree-1) >= 2020.7
 BuildRequires: pkgconfig(polkit-gobject-1)
 BuildRequires: pkgconfig(json-glib-1.0)
-BuildRequires: pkgconfig(rpm)
+BuildRequires: pkgconfig(rpm) >= 4.14.0
 BuildRequires: pkgconfig(libarchive)
 BuildRequires: pkgconfig(libsystemd)
 BuildRequires: libcap-devel
 BuildRequires: libattr-devel
 
-# We currently interact directly with librepo
+# We currently interact directly with librepo (libdnf below also pulls it in,
+# but duplicating to be clear)
 BuildRequires: pkgconfig(librepo)
 
 # Needed by curl-rust
 BuildRequires: pkgconfig(libcurl)
 
-# libdnf bundling
-# We're using RPATH to pick up our bundled version
-%global __requires_exclude ^libdnf[.]so[.].*$
-
-# Our bundled libdnf.so.2 is for us only
-%global __provides_exclude_from ^%{_libdir}/%{name}/.*$
-
 BuildRequires: cmake
 BuildRequires: pkgconfig(expat)
 BuildRequires: pkgconfig(check)
+
+# We use some libsolv types directly too (libdnf below also pulls it in,
+# but duplicating to be clear)
 BuildRequires: pkgconfig(libsolv)
 
 # We need g++ for libdnf
 BuildRequires: gcc-c++
 
 
-# more libdnf build deps (see libdnf's spec for versions)
+# more libdnf build deps (see libdnf's spec for versions; maintain ordering)
+%global libsolv_version 0.7.17
+%global libmodulemd_version 2.11.2-2
+%global librepo_version 1.13.0
 %global swig_version 3.0.12
-%global libmodulemd_version 2.5.0
 BuildRequires:  swig >= %{swig_version}
 BuildRequires:  pkgconfig(modulemd-2.0) >= %{libmodulemd_version}
+BuildRequires:  pkgconfig(librepo) >= %{librepo_version}
+BuildRequires:  libsolv-devel >= %{libsolv_version}
 BuildRequires:  pkgconfig(json-c)
 BuildRequires:  pkgconfig(cppunit)
 BuildRequires:  pkgconfig(sqlite3)
 BuildRequires:  pkgconfig(smartcols)
+%if %{with zchunk}
+BuildRequires:  pkgconfig(zck) >= 0.9.11
+%endif
 BuildRequires:  gpgme-devel
 
+# Runtime libdnf deps
 Requires:       libmodulemd%{?_isa} >= %{libmodulemd_version}
+Requires:       libsolv%{?_isa} >= %{libsolv_version}
+Requires:       librepo%{?_isa} >= %{librepo_version}
 
 # For now...see https://github.com/projectatomic/rpm-ostree/pull/637
 # and https://github.com/fedora-infra/fedmsg-atomic-composer/pull/17
@@ -119,11 +141,18 @@ The %{name}-devel package includes the header files for %{name}-libs.
 
 %build
 env NOCONFIGURE=1 ./autogen.sh
-%configure --disable-silent-rules --enable-gtk-doc
-make %{?_smp_mflags}
+# Since we're hybrid C++/Rust we need to propagate this manually;
+# the %%configure macro today assumes (reasonably) that one is building
+# C/C++ and sets C{,XX}FLAGS
+%if 0%{?build_rustflags:1}
+export RUSTFLAGS="%{build_rustflags}"
+%endif
+%configure --disable-silent-rules --enable-gtk-doc %{?sqlite_rpmdb_default} %{?with_sanitizers:--enable-sanitizers}
+
+%make_build
 
 %install
-make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p -c"
+%make_install INSTALL="install -p -c"
 find $RPM_BUILD_ROOT -name '*.la' -delete
 
 # I try to do continuous delivery via rpmdistro-gitoverlay while
@@ -184,9 +213,14 @@ $PYTHON autofiles.py > files.devel \
 %files devel -f files.devel
 
 %changelog
-* Wed Feb 24 2021 Jonathan Lebon <jonathan@jlebon.com> - 2020.7-3
-- Respin to restore order in the world (of RPM versions)
-  Resolves: #1894061
+* Wed May 12 2021 Luca BRUNO <lucab@lucabruno.net> - 2021.5-1
+- New upstream version
+  https://github.com/coreos/rpm-ostree/releases/tag/v2021.5
+  Resolves: rhbz#1959874
+
+* Tue Mar 30 2021 Colin Walters <walters@verbum.org> - 2020.7-4
+- Backport https://github.com/coreos/rpm-ostree/pull/2386/commits/aa8e49aaeddfc5d38651fa08f46e059655818fd1
+  Resolves: #1944760
 
 * Thu Nov 05 2020 Colin Walters <walters@verbum.org> - 2020.7-2
 - Update to 2020.7