From c32ce1071807176eb31d884d4185b20d944a28b9 Mon Sep 17 00:00:00 2001 From: Aleš Matěj Date: Mon, 25 Sep 2023 08:24:40 +0200 Subject: [PATCH] Avoid reinstalling installonly packages marked for ERASE Without this patch reinstalling installonly pkg marked for ERASE might be a valid smallest solution to our job. For example when user wants to install through a provide we select all packages that provide it and put them inside a `job install oneof ...` if one of the providers is also marked for ERASE due to installonly limit libsolv might decide to reinstall it. To make sure it doesn't happen mark the available package also as ERASE. https://github.com/openSUSE/libsolv/issues/540 https://issues.redhat.com/browse/RHEL-1253 (https://bugzilla.redhat.com/show_bug.cgi?id=2163474) --- libdnf/goal/Goal.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp index b771030..6939d8a 100644 --- a/libdnf/goal/Goal.cpp +++ b/libdnf/goal/Goal.cpp @@ -639,6 +639,12 @@ erase_flags2libsolv(int flags) return ret; } +static bool +NameSolvableComparator(const Solvable * first, const Solvable * second) +{ + return first->name < second->name; +} + Goal::Goal(const Goal & goal_src) : pImpl(new Impl(*goal_src.pImpl)) {} Goal::Impl::Impl(const Goal::Impl & goal_src) @@ -1324,10 +1330,24 @@ Goal::Impl::limitInstallonlyPackages(Solver *solv, Queue *job) for (int i = 0; i < onlies->count; ++i) { Id p, pp; IdQueue q, installing; + std::vector available_unused_providers; + // Add all providers of installonly provides that are marked for install + // to `q` IdQueue those that are not marked for install and are not already + // installed are added to available_unused_providers. FOR_PKG_PROVIDES(p, pp, onlies->elements[i]) - if (solver_get_decisionlevel(solv, p) > 0) + // According to libsolv-bindings the decision level is positive for installs + // and negative for conflicts (conflicts with another package or dependency + // conflicts = dependencies cannot be met). + if (solver_get_decisionlevel(solv, p) > 0) { q.pushBack(p); + } else { + Solvable *s = pool_id2solvable(pool, p); + if (s->repo != pool->installed) { + available_unused_providers.push_back(s); + } + } + if (q.size() <= (int) dnf_sack_get_installonly_limit(sack)) { continue; } @@ -1345,17 +1365,28 @@ Goal::Impl::limitInstallonlyPackages(Solver *solv, Queue *job) struct InstallonliesSortCallback s_cb = {pool, dnf_sack_running_kernel(sack)}; solv_sort(q.data(), q.size(), sizeof(q[0]), sort_packages, &s_cb); + std::sort(available_unused_providers.begin(), available_unused_providers.end(), NameSolvableComparator); IdQueue same_names; while (q.size() > 0) { same_name_subqueue(pool, q.getQueue(), same_names.getQueue()); if (same_names.size() <= (int) dnf_sack_get_installonly_limit(sack)) continue; reresolve = 1; for (int j = 0; j < same_names.size(); ++j) { Id id = same_names[j]; Id action = SOLVER_ERASE; - if (j < (int) dnf_sack_get_installonly_limit(sack)) + if (j < (int) dnf_sack_get_installonly_limit(sack)) { action = SOLVER_INSTALL; + } else { + // We want to avoid reinstalling packages marked for ERASE, therefore + // if some unused provider is also available we need to mark it ERASE as well. + Solvable *s = pool_id2solvable(pool, id); + auto low = std::lower_bound(available_unused_providers.begin(), available_unused_providers.end(), s, NameSolvableComparator); + while (low != available_unused_providers.end() && (*low)->name == s->name) { + queue_push2(job, SOLVER_ERASE | SOLVER_SOLVABLE, pool_solvable2id(pool, *low)); + ++low; + } + } queue_push2(job, action | SOLVER_SOLVABLE, id); } } -- libgit2 1.6.4