summaryrefslogtreecommitdiffstats
path: root/apt-pkg/upgrade.cc
diff options
context:
space:
mode:
Diffstat (limited to 'apt-pkg/upgrade.cc')
-rw-r--r--apt-pkg/upgrade.cc393
1 files changed, 393 insertions, 0 deletions
diff --git a/apt-pkg/upgrade.cc b/apt-pkg/upgrade.cc
new file mode 100644
index 0000000..e3e98e5
--- /dev/null
+++ b/apt-pkg/upgrade.cc
@@ -0,0 +1,393 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/edsp.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/upgrade.h>
+
+#include <random>
+#include <string>
+
+#include <apti18n.h>
+ /*}}}*/
+
+struct PhasedUpgrader
+{
+ std::string machineID;
+ bool isChroot;
+
+ PhasedUpgrader()
+ {
+ machineID = APT::Configuration::getMachineID();
+ }
+
+ // See if this version is a security update. This also checks, for installed packages,
+ // if any of the previous versions is a security update
+ bool IsSecurityUpdate(pkgCache::VerIterator const &Ver)
+ {
+ auto Pkg = Ver.ParentPkg();
+ auto Installed = Pkg.CurrentVer();
+
+ auto OtherVer = Pkg.VersionList();
+
+ // Advance to first version < our version
+ while (OtherVer->ID != Ver->ID)
+ ++OtherVer;
+ ++OtherVer;
+
+ // Iterate over all versions < our version
+ for (; !OtherVer.end() && (Installed.end() || OtherVer->ID != Installed->ID); OtherVer++)
+ {
+ for (auto PF = OtherVer.FileList(); !PF.end(); PF++)
+ if (PF.File() && PF.File().Archive() != nullptr && APT::String::Endswith(PF.File().Archive(), "-security"))
+ return true;
+ }
+ return false;
+ }
+
+ // Check if this version is a phased update that should be ignored
+ bool IsIgnoredPhasedUpdate(pkgCache::VerIterator const &Ver)
+ {
+ if (_config->FindB("APT::Get::Phase-Policy", false))
+ return false;
+
+ // The order and fallbacks for the always/never checks come from update-manager and exist
+ // to preserve compatibility.
+ if (_config->FindB("APT::Get::Always-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Always-Include-Phased-Updates", false)))
+ return false;
+
+ if (_config->FindB("APT::Get::Never-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Never-Include-Phased-Updates", false)))
+ return true;
+
+ if (machineID.empty() // no machine-id
+ || getenv("SOURCE_DATE_EPOCH") != nullptr // reproducible build - always include
+ || APT::Configuration::isChroot())
+ return false;
+
+ std::string seedStr = std::string(Ver.SourcePkgName()) + "-" + Ver.SourceVerStr() + "-" + machineID;
+ std::seed_seq seed(seedStr.begin(), seedStr.end());
+ std::minstd_rand rand(seed);
+ std::uniform_int_distribution<unsigned int> dist(0, 100);
+
+ return dist(rand) > Ver.PhasedUpdatePercentage();
+ }
+
+ bool ShouldKeep(pkgDepCache &Cache, pkgCache::PkgIterator Pkg)
+ {
+ if (Pkg->CurrentVer == 0)
+ return false;
+ if (Cache[Pkg].InstallVer == 0)
+ return false;
+ if (Cache[Pkg].InstVerIter(Cache).PhasedUpdatePercentage() == 100)
+ return false;
+ if (IsSecurityUpdate(Cache[Pkg].InstVerIter(Cache)))
+ return false;
+ if (!IsIgnoredPhasedUpdate(Cache[Pkg].InstVerIter(Cache)))
+ return false;
+
+ return true;
+ }
+
+ // Hold back upgrades to phased versions of already installed packages, unless
+ // they are security updates
+ void HoldBackIgnoredPhasedUpdates(pkgDepCache &Cache, pkgProblemResolver *Fix)
+ {
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ if (not ShouldKeep(Cache, I))
+ continue;
+
+ Cache.MarkKeep(I, false, false);
+ Cache.MarkProtected(I);
+ if (Fix != nullptr)
+ Fix->Protect(I);
+ }
+ }
+};
+
+// DistUpgrade - Distribution upgrade /*{{{*/
+// ---------------------------------------------------------------------
+/* This autoinstalls every package and then force installs every
+ pre-existing package. This creates the initial set of conditions which
+ most likely contain problems because too many things were installed.
+
+ The problem resolver is used to resolve the problems.
+ */
+static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
+{
+ std::string const solver = _config->Find("APT::Solver", "internal");
+ auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, EDSP::Request::UPGRADE_ALL, Progress);
+ if (solver != "internal")
+ return ret;
+
+ if (Progress != NULL)
+ Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
+
+ pkgDepCache::ActionGroup group(Cache);
+
+ PhasedUpgrader().HoldBackIgnoredPhasedUpdates(Cache, nullptr);
+
+ /* Upgrade all installed packages first without autoinst to help the resolver
+ in versioned or-groups to upgrade the old solver instead of installing
+ a new one (if the old solver is not the first one [anymore]) */
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if (I->CurrentVer != 0)
+ Cache.MarkInstall(I, false, 0, false);
+
+ if (Progress != NULL)
+ Progress->Progress(10);
+
+ /* Auto upgrade all installed packages, this provides the basis
+ for the installation */
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if (I->CurrentVer != 0)
+ Cache.MarkInstall(I, true, 0, false);
+
+ if (Progress != NULL)
+ Progress->Progress(50);
+
+ /* Now, install each essential package which is not installed
+ (and not provided by another package in the same name group) */
+ std::string essential = _config->Find("pkgCacheGen::Essential", "all");
+ if (essential == "all")
+ {
+ for (pkgCache::GrpIterator G = Cache.GrpBegin(); G.end() == false; ++G)
+ {
+ bool isEssential = false;
+ bool instEssential = false;
+ for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P))
+ {
+ if ((P->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential)
+ continue;
+ isEssential = true;
+ if (Cache[P].Install() == true)
+ {
+ instEssential = true;
+ break;
+ }
+ }
+ if (isEssential == false || instEssential == true)
+ continue;
+ pkgCache::PkgIterator P = G.FindPreferredPkg();
+ Cache.MarkInstall(P, true, 0, false);
+ }
+ }
+ else if (essential != "none")
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
+ Cache.MarkInstall(I, true, 0, false);
+
+ if (Progress != NULL)
+ Progress->Progress(55);
+
+ /* We do it again over all previously installed packages to force
+ conflict resolution on them all. */
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if (I->CurrentVer != 0)
+ Cache.MarkInstall(I, false, 0, false);
+
+ if (Progress != NULL)
+ Progress->Progress(65);
+
+ pkgProblemResolver Fix(&Cache);
+
+ if (Progress != NULL)
+ Progress->Progress(95);
+
+ // Hold back held packages.
+ if (_config->FindB("APT::Ignore-Hold",false) == false)
+ {
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ if (I->SelectedState == pkgCache::State::Hold)
+ {
+ Fix.Protect(I);
+ Cache.MarkKeep(I, false, false);
+ }
+ }
+ }
+
+ PhasedUpgrader().HoldBackIgnoredPhasedUpdates(Cache, &Fix);
+
+ bool const success = Fix.ResolveInternal(false);
+ if (Progress != NULL)
+ Progress->Done();
+ return success;
+} /*}}}*/
+// AllUpgradeNoNewPackages - Upgrade but no removals or new pkgs /*{{{*/
+static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Progress)
+{
+ std::string const solver = _config->Find("APT::Solver", "internal");
+ constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_NEW_INSTALL | EDSP::Request::FORBID_REMOVE;
+ auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress);
+ if (solver != "internal")
+ return ret;
+
+ if (Progress != NULL)
+ Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
+
+ pkgDepCache::ActionGroup group(Cache);
+ pkgProblemResolver Fix(&Cache);
+ PhasedUpgrader phasedUpgrader;
+ // Upgrade all installed packages
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ if (Cache[I].Install() == true)
+ Fix.Protect(I);
+
+ if (_config->FindB("APT::Ignore-Hold",false) == false)
+ if (I->SelectedState == pkgCache::State::Hold)
+ continue;
+
+ if (phasedUpgrader.ShouldKeep(Cache, I))
+ continue;
+
+ if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
+ Cache.MarkInstall(I, false, 0, false);
+ }
+
+ if (Progress != NULL)
+ Progress->Progress(50);
+
+ phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix);
+
+ // resolve remaining issues via keep
+ bool const success = Fix.ResolveByKeepInternal();
+ if (Progress != NULL)
+ Progress->Done();
+ return success;
+}
+ /*}}}*/
+// AllUpgradeWithNewInstalls - Upgrade + install new packages as needed /*{{{*/
+// ---------------------------------------------------------------------
+/* Right now the system must be consistent before this can be called.
+ * Upgrade as much as possible without deleting anything (useful for
+ * stable systems)
+ */
+static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const Progress)
+{
+ std::string const solver = _config->Find("APT::Solver", "internal");
+ constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_REMOVE;
+ auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress);
+ if (solver != "internal")
+ return ret;
+
+ if (Progress != NULL)
+ Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
+
+ pkgDepCache::ActionGroup group(Cache);
+ pkgProblemResolver Fix(&Cache);
+ PhasedUpgrader phasedUpgrader;
+
+ // provide the initial set of stuff we want to upgrade by marking
+ // all upgradable packages for upgrade
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
+ {
+ if (_config->FindB("APT::Ignore-Hold",false) == false)
+ if (I->SelectedState == pkgCache::State::Hold)
+ continue;
+ if (phasedUpgrader.ShouldKeep(Cache, I))
+ continue;
+
+ Cache.MarkInstall(I, false, 0, false);
+ }
+ }
+
+ if (Progress != NULL)
+ Progress->Progress(10);
+
+ // then let auto-install loose
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if (Cache[I].Install())
+ Cache.MarkInstall(I, true, 0, false);
+
+ if (Progress != NULL)
+ Progress->Progress(50);
+
+ // ... but it may remove stuff, we need to clean up afterwards again
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ if (Cache[I].Delete() == true)
+ Cache.MarkKeep(I, false, false);
+
+ if (Progress != NULL)
+ Progress->Progress(60);
+
+ phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix);
+
+ // resolve remaining issues via keep
+ bool const success = Fix.ResolveByKeepInternal();
+ if (Progress != NULL)
+ Progress->Done();
+ return success;
+}
+ /*}}}*/
+// MinimizeUpgrade - Minimizes the set of packages to be upgraded /*{{{*/
+// ---------------------------------------------------------------------
+/* This simply goes over the entire set of packages and tries to keep
+ each package marked for upgrade. If a conflict is generated then
+ the package is restored. */
+bool pkgMinimizeUpgrade(pkgDepCache &Cache)
+{
+ pkgDepCache::ActionGroup group(Cache);
+
+ if (Cache.BrokenCount() != 0)
+ return false;
+
+ // We loop for 10 tries to get the minimal set size.
+ bool Change = false;
+ unsigned int Count = 0;
+ do
+ {
+ Change = false;
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
+ {
+ // Not interesting
+ if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true)
+ continue;
+
+ // Keep it and see if that is OK
+ Cache.MarkKeep(I, false, false);
+ if (Cache.BrokenCount() != 0)
+ Cache.MarkInstall(I, false, 0, false);
+ else
+ {
+ // If keep didn't actually do anything then there was no change..
+ if (Cache[I].Upgrade() == false)
+ Change = true;
+ }
+ }
+ ++Count;
+ }
+ while (Change == true && Count < 10);
+
+ if (Cache.BrokenCount() != 0)
+ return _error->Error("Internal Error in pkgMinimizeUpgrade");
+
+ return true;
+}
+ /*}}}*/
+// APT::Upgrade::Upgrade - Upgrade using a specific strategy /*{{{*/
+bool APT::Upgrade::Upgrade(pkgDepCache &Cache, int mode, OpProgress * const Progress)
+{
+ if (mode == ALLOW_EVERYTHING)
+ return pkgDistUpgrade(Cache, Progress);
+ else if ((mode & ~FORBID_REMOVE_PACKAGES) == 0)
+ return pkgAllUpgradeWithNewPackages(Cache, Progress);
+ else if ((mode & ~(FORBID_REMOVE_PACKAGES|FORBID_INSTALL_NEW_PACKAGES)) == 0)
+ return pkgAllUpgradeNoNewPackages(Cache, Progress);
+ else
+ _error->Error("pkgAllUpgrade called with unsupported mode %i", mode);
+ return false;
+}
+ /*}}}*/