diff options
author | Julian Andres Klode <jak@debian.org> | 2022-06-29 13:30:03 +0000 |
---|---|---|
committer | Julian Andres Klode <jak@debian.org> | 2022-06-29 13:30:03 +0000 |
commit | e4230878687c6d9bd3b13552bb2c1053b9d7d7d8 (patch) | |
tree | 6f1583b7198ce4c8f2e8a66936f53186857f2caf /apt-pkg | |
parent | edffb0ae26935d948ffeb3be229fdcf37d2a1ee1 (diff) | |
parent | 20739359fa936c0851a4c0f37667526d98c22761 (diff) |
Merge branch 'pu/phasing-better' into 'main'
Rewrite phased updates using a keep-back approach
See merge request apt-team/apt!245
Diffstat (limited to 'apt-pkg')
-rw-r--r-- | apt-pkg/policy.cc | 21 | ||||
-rw-r--r-- | apt-pkg/upgrade.cc | 114 |
2 files changed, 125 insertions, 10 deletions
diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index b30eddb37..5fcc11b66 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -288,6 +288,13 @@ void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name, // Returns true if this update is excluded by phasing. static inline bool ExcludePhased(std::string machineID, pkgCache::VerIterator const &Ver) { + if (Ver.PhasedUpdatePercentage() == 100) + return false; + + // FIXME: We have migrated to a legacy implementation until LP: #1929082 is fixed + if (not _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", @@ -312,11 +319,9 @@ static inline bool ExcludePhased(std::string machineID, pkgCache::VerIterator co } APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, bool ConsiderFiles) { - if (Ver.PhasedUpdatePercentage() != 100) - { - if (ExcludePhased(d->machineID, Ver)) - return 1; - } + auto ceiling = std::numeric_limits<signed int>::max(); + if (ExcludePhased(d->machineID, Ver)) + ceiling = 1; if (VerPins[Ver->ID].Type != pkgVersionMatch::None) { // If all sources are never pins, the never pin wins. @@ -324,10 +329,10 @@ APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, b return NEVER_PIN; for (pkgCache::VerFileIterator file = Ver.FileList(); file.end() == false; file++) if (GetPriority(file.File()) != NEVER_PIN) - return VerPins[Ver->ID].Priority; + return std::min((int)VerPins[Ver->ID].Priority, ceiling); } if (!ConsiderFiles) - return 0; + return std::min(0, ceiling); // priorities are short ints, but we want to pick a value outside the valid range here auto priority = std::numeric_limits<signed int>::min(); @@ -344,7 +349,7 @@ APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, b priority = std::max<decltype(priority)>(priority, GetPriority(file.File())); } - return priority == std::numeric_limits<decltype(priority)>::min() ? 0 : priority; + return std::min(priority == std::numeric_limits<decltype(priority)>::min() ? 0 : priority, ceiling); } APT_PURE signed short pkgPolicy::GetPriority(pkgCache::PkgFileIterator const &File) { diff --git a/apt-pkg/upgrade.cc b/apt-pkg/upgrade.cc index 06c3751e0..a7e18026b 100644 --- a/apt-pkg/upgrade.cc +++ b/apt-pkg/upgrade.cc @@ -2,19 +2,117 @@ #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); + if (Fix != nullptr) + Fix->Protect(I); + } + } +}; + // DistUpgrade - Distribution upgrade /*{{{*/ // --------------------------------------------------------------------- /* This autoinstalls every package and then force installs every @@ -115,6 +213,8 @@ static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress) } } + PhasedUpgrader().HoldBackIgnoredPhasedUpdates(Cache, &Fix); + bool const success = Fix.ResolveInternal(false); if (Progress != NULL) Progress->Done(); @@ -134,7 +234,7 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr pkgDepCache::ActionGroup group(Cache); pkgProblemResolver Fix(&Cache); - + PhasedUpgrader phasedUpgrader; // Upgrade all installed packages for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) { @@ -144,7 +244,10 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr 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); } @@ -152,6 +255,8 @@ static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Pr if (Progress != NULL) Progress->Progress(50); + phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix); + // resolve remaining issues via keep bool const success = Fix.ResolveByKeepInternal(); if (Progress != NULL) @@ -178,6 +283,7 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const 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 @@ -188,6 +294,8 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const 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); } @@ -212,6 +320,8 @@ static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const if (Progress != NULL) Progress->Progress(60); + phasedUpgrader.HoldBackIgnoredPhasedUpdates(Cache, &Fix); + // resolve remaining issues via keep bool const success = Fix.ResolveByKeepInternal(); if (Progress != NULL) |