From dfba66e0b467a0063dfc91cde2be818febbf7271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Tue, 11 Jun 2024 12:47:43 +0200 Subject: [PATCH] Fix reinstalling packages which conflicts with themselves in dnf_transaction_commit() Resolves: RHEL-32919 --- ...sAddReinstallElement-when-doing-a-re.patch | 229 ++++++++++++++++++ ...tsAddReinstallElement-rpm-also-unins.patch | 78 ++++++ libdnf.spec | 8 +- 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch create mode 100644 0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch diff --git a/0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch b/0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch new file mode 100644 index 0000000..9dea089 --- /dev/null +++ b/0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch @@ -0,0 +1,229 @@ +From 85432dfd048912083897ab687488087038a9ac96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Mon, 8 Apr 2024 07:32:31 +0200 +Subject: [PATCH] context: use `rpmtsAddReinstallElement()` when doing a + reinstall +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +`rpmtsAddInstallElement()` doesn't work for all reinstall cases, such as +when a package `Provides` and `Conflicts` with the same capability. + +Fixes: https://github.com/rpm-software-management/microdnf/issues/137 +Signed-off-by: Petr Písař +--- + libdnf/dnf-rpmts-private.hpp | 6 ++ + libdnf/dnf-rpmts.cpp | 108 +++++++++++++++++++++++------------ + libdnf/dnf-transaction.cpp | 8 ++- + 3 files changed, 85 insertions(+), 37 deletions(-) + +diff --git a/libdnf/dnf-rpmts-private.hpp b/libdnf/dnf-rpmts-private.hpp +index 94ad6b45..7a8f70fb 100644 +--- a/libdnf/dnf-rpmts-private.hpp ++++ b/libdnf/dnf-rpmts-private.hpp +@@ -31,4 +31,10 @@ gboolean dnf_rpmts_add_install_filename2(rpmts ts, + DnfPackage *pkg, + GError **error); + ++gboolean dnf_rpmts_add_reinstall_filename(rpmts ts, ++ const gchar *filename, ++ gboolean allow_untrusted, ++ GError **error); ++ ++ + #endif /* __DNF_RPMTS_PRIVATE_HPP */ +diff --git a/libdnf/dnf-rpmts.cpp b/libdnf/dnf-rpmts.cpp +index ec3d3706..9c0152fc 100644 +--- a/libdnf/dnf-rpmts.cpp ++++ b/libdnf/dnf-rpmts.cpp +@@ -88,94 +88,132 @@ test_fail_safe(Header * hdr, DnfPackage * pkg, GError **error) + return ret; + } + +-gboolean +-dnf_rpmts_add_install_filename2(rpmts ts, +- const gchar *filename, +- gboolean allow_untrusted, +- gboolean is_update, +- DnfPackage * pkg, +- GError **error) try +-{ +- gboolean ret = TRUE; +- gint res; +- Header hdr; +- FD_t fd; +- +- /* open this */ +- fd = Fopen(filename, "r.ufdio"); +- res = rpmReadPackageFile(ts, fd, filename, &hdr); +- ++static gboolean ++result_is_accepted(gint result, gboolean allow_untrusted, const gchar *filename, GError **error) { + /* be less strict when we're allowing untrusted transactions */ + if (allow_untrusted) { +- switch(res) { ++ switch(result) { + case RPMRC_NOKEY: + case RPMRC_NOTFOUND: + case RPMRC_NOTTRUSTED: + case RPMRC_OK: +- break; ++ return TRUE; + case RPMRC_FAIL: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("signature does not verify for %s"), + filename); +- goto out; ++ return FALSE; + default: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("failed to open(generic error): %s"), + filename); +- goto out; ++ return FALSE; + } + } else { +- switch(res) { ++ switch(result) { + case RPMRC_OK: +- break; ++ return TRUE; + case RPMRC_NOTTRUSTED: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("failed to verify key for %s"), + filename); +- goto out; ++ return FALSE; + case RPMRC_NOKEY: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("public key unavailable for %s"), + filename); +- goto out; ++ return FALSE; + case RPMRC_NOTFOUND: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("signature not found for %s"), + filename); +- goto out; ++ return FALSE; + case RPMRC_FAIL: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("signature does not verify for %s"), + filename); +- goto out; ++ return FALSE; + default: +- ret = FALSE; + g_set_error(error, + DNF_ERROR, + DNF_ERROR_INTERNAL_ERROR, + _("failed to open(generic error): %s"), + filename); +- goto out; ++ return FALSE; + } + } ++} ++ ++gboolean ++dnf_rpmts_add_reinstall_filename(rpmts ts, ++ const gchar *filename, ++ gboolean allow_untrusted, ++ GError **error) try ++{ ++ gboolean ret = TRUE; ++ gint res; ++ Header hdr; ++ FD_t fd; ++ ++ /* open this */ ++ fd = Fopen(filename, "r.ufdio"); ++ res = rpmReadPackageFile(ts, fd, filename, &hdr); ++ ++ if (!result_is_accepted(res, allow_untrusted, filename, error)) { ++ ret = FALSE; ++ goto out; ++ } ++ ++ /* add to the transaction */ ++ res = rpmtsAddReinstallElement(ts, hdr, (fnpyKey) filename); ++ if (res != 0) { ++ ret = FALSE; ++ g_set_error(error, ++ DNF_ERROR, ++ DNF_ERROR_INTERNAL_ERROR, ++ _("failed to add reinstall element: %1$s [%2$i]"), ++ filename, res); ++ goto out; ++ } ++out: ++ Fclose(fd); ++ headerFree(hdr); ++ return ret; ++} CATCH_TO_GERROR(FALSE) ++ ++gboolean ++dnf_rpmts_add_install_filename2(rpmts ts, ++ const gchar *filename, ++ gboolean allow_untrusted, ++ gboolean is_update, ++ DnfPackage * pkg, ++ GError **error) try ++{ ++ gboolean ret = TRUE; ++ gint res; ++ Header hdr; ++ FD_t fd; ++ ++ /* open this */ ++ fd = Fopen(filename, "r.ufdio"); ++ res = rpmReadPackageFile(ts, fd, filename, &hdr); ++ ++ if (!result_is_accepted(res, allow_untrusted, filename, error)) { ++ ret = FALSE; ++ goto out; ++ } + if (pkg) { + if (!test_fail_safe(&hdr, pkg, error)) { + ret = FALSE; +diff --git a/libdnf/dnf-transaction.cpp b/libdnf/dnf-transaction.cpp +index c4c5e02b..35b2ff95 100644 +--- a/libdnf/dnf-transaction.cpp ++++ b/libdnf/dnf-transaction.cpp +@@ -1222,8 +1222,12 @@ dnf_transaction_commit(DnfTransaction *transaction, HyGoal goal, DnfState *state + filename = dnf_package_get_filename(pkg); + allow_untrusted = (priv->flags & DNF_TRANSACTION_FLAG_ONLY_TRUSTED) == 0; + is_update = action == DNF_STATE_ACTION_UPDATE || action == DNF_STATE_ACTION_DOWNGRADE; +- ret = dnf_rpmts_add_install_filename2( +- priv->ts, filename, allow_untrusted, is_update, pkg, error); ++ if (action == DNF_STATE_ACTION_REINSTALL) { ++ ret = dnf_rpmts_add_reinstall_filename(priv->ts, filename, allow_untrusted, error); ++ } else { ++ ret = dnf_rpmts_add_install_filename2( ++ priv->ts, filename, allow_untrusted, is_update, pkg, error); ++ } + if (!ret) + goto out; + +-- +2.45.2 + diff --git a/0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch b/0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch new file mode 100644 index 0000000..373886e --- /dev/null +++ b/0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch @@ -0,0 +1,78 @@ +From bc371683ab69d51127952b037bde209a56e44105 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Fri, 3 May 2024 08:55:47 +0200 +Subject: [PATCH] Since we use rpmtsAddReinstallElement rpm also uninstalls the + package +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It calls callbacks for `RPMCALLBACK_INST_START` and +`RPMCALLBACK_INST_PROGRESS` just like before when the reinstall was done +through regural install (rpmtsAddInstallElement) but in addition it also +calls `RPMCALLBACK_UNINST_START` and `RPMCALLBACK_UNINST_PROGRESS`. To +ensure they find the `DnfPackage` add it to `remove_helper` array. + +Unfortunaly this means that the reinstall action is reported twice to +the clients (one install and one uninstall). We could try to hide one of +the them but I think a better solution is to report what is actually +happening and report one install and one uninstall. + +This is for the context part of libdnf (microdnf, packagekit, ...) + +Fixes: https://github.com/rpm-software-management/libdnf/issues/1653 +Signed-off-by: Petr Písař +--- + libdnf/dnf-transaction.cpp | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/libdnf/dnf-transaction.cpp b/libdnf/dnf-transaction.cpp +index 35b2ff95..fcb1152f 100644 +--- a/libdnf/dnf-transaction.cpp ++++ b/libdnf/dnf-transaction.cpp +@@ -602,7 +602,7 @@ dnf_transaction_ts_progress_cb(const void *arg, + + /* map to correct action code */ + action = dnf_package_get_action(pkg); +- if (action == DNF_STATE_ACTION_UNKNOWN) ++ if (action == DNF_STATE_ACTION_UNKNOWN || action == DNF_STATE_ACTION_REINSTALL) + action = DNF_STATE_ACTION_INSTALL; + + /* set the pkgid if not already set */ +@@ -641,7 +641,7 @@ dnf_transaction_ts_progress_cb(const void *arg, + + /* map to correct action code */ + action = dnf_package_get_action(pkg); +- if (action == DNF_STATE_ACTION_UNKNOWN) ++ if (action == DNF_STATE_ACTION_UNKNOWN || action == DNF_STATE_ACTION_REINSTALL) + action = DNF_STATE_ACTION_REMOVE; + + /* remove start */ +@@ -716,7 +716,7 @@ dnf_transaction_ts_progress_cb(const void *arg, + + /* map to correct action code */ + action = dnf_package_get_action(pkg); +- if (action == DNF_STATE_ACTION_UNKNOWN) ++ if (action == DNF_STATE_ACTION_UNKNOWN || action == DNF_STATE_ACTION_REINSTALL) + action = DNF_STATE_ACTION_REMOVE; + + dnf_state_set_package_progress( +@@ -1354,6 +1354,15 @@ dnf_transaction_commit(DnfTransaction *transaction, HyGoal goal, DnfState *state + g_ptr_array_unref(pkglist); + } + ++ /* add reinstalled packages to a helper array which is used to ++ * map removed packages auto-added by rpm to actual DnfPackage's */ ++ pkglist = dnf_goal_get_packages(goal, DNF_PACKAGE_INFO_REINSTALL, -1); ++ for (i = 0; i < pkglist->len; i++) { ++ pkg_tmp = static_cast< DnfPackage * >(g_ptr_array_index(pkglist, i)); ++ g_ptr_array_add(priv->remove_helper, g_object_ref(pkg_tmp)); ++ } ++ g_ptr_array_unref(pkglist); ++ + /* this section done */ + ret = dnf_state_done(state, error); + if (!ret) +-- +2.45.2 + diff --git a/libdnf.spec b/libdnf.spec index d47c4b3..5430a2c 100644 --- a/libdnf.spec +++ b/libdnf.spec @@ -56,11 +56,13 @@ Name: libdnf Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version} -Release: 1%{?dist} +Release: 2%{?dist} Summary: Library providing simplified C and Python API to libsolv License: LGPL-2.1-or-later URL: https://github.com/rpm-software-management/libdnf Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz +Patch1: 0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch +Patch2: 0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch BuildRequires: cmake BuildRequires: gcc @@ -304,6 +306,10 @@ popd %endif %changelog +* Tue Jun 11 2024 Petr Pisar - 0.73.1-2 +- Fix reinstalling packages which conflicts with themselves in + dnf_transaction_commit() (RHEL-32919) + * Thu Mar 28 2024 Evan Goode - 0.73.1-1 - Update to 0.73.1 (RHEL-38831) - Fix https://issues.redhat.com/browse/RHEL-27657 (RHEL-32935)