From b820fd59c4fe6e3581901eee648e88209be56137 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 3 Jul 2016 14:39:16 +0200 Subject: save and restore selection states before/after calling dpkg dpkg decides certain things on its own based on selections and especially if we want to call --pending on purge/remove actions, we need to ensure a clean slate or otherwise we surprise the user by removing packages we weren't allowed to remove by the user in this run (the selection might be an overarching plan for the not-yet "future"). Ideally dpkg would have some kind of temporal selection interface for this case, but it hasn't, so we make it temporal with the risk of loosing state if we don't manage to restore them. --- apt-pkg/deb/dpkgpm.cc | 42 +++++++++++++++++++++++++++----- apt-pkg/statechanges.cc | 3 ++- test/integration/test-apt-get-autoremove | 10 +++++++- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index c82a09b09..38285d14b 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,14 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg) return Ver; } /*}}}*/ +static pkgCache::VerIterator FindToBeRemovedVersion(pkgCache::PkgIterator const &Pkg)/*{{{*/ +{ + auto const PV = Pkg.CurrentVer(); + if (PV.end() == false) + return PV; + return FindNowVersion(Pkg); +} + /*}}}*/ // DPkgPM::pkgDPkgPM - Constructor /*{{{*/ // --------------------------------------------------------------------- @@ -1342,6 +1351,28 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) List.erase(std::next(List.begin(), notconfidx), List.end()); } + APT::StateChanges currentStates; + if (_config->FindB("dpkg::selection::current::saveandrestore", true)) + { + for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + if (Pkg->CurrentVer == 0) + continue; + else if (Pkg->SelectedState == pkgCache::State::Purge) + currentStates.Purge(FindToBeRemovedVersion(Pkg)); + else if (Pkg->SelectedState == pkgCache::State::DeInstall) + currentStates.Remove(FindToBeRemovedVersion(Pkg)); + if (currentStates.empty() == false) + { + APT::StateChanges cleanStates; + for (auto && P: currentStates.Remove()) + cleanStates.Install(P); + for (auto && P: currentStates.Purge()) + cleanStates.Install(P); + if (cleanStates.Save(false) == false) + return _error->Error("Couldn't clean the currently selected dpkg states"); + } + } + d->stdin_is_dev_null = false; // create log @@ -1505,12 +1536,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { pkgCache::VerIterator PkgVer; std::string name = I->Pkg.Name(); - if (Op == Item::Remove || Op == Item::Purge) - { - PkgVer = I->Pkg.CurrentVer(); - if(PkgVer.end() == true) - PkgVer = FindNowVersion(I->Pkg); - } + if (Op == Item::Remove || Op == Item::Purge) + PkgVer = FindToBeRemovedVersion(I->Pkg); else PkgVer = Cache[I->Pkg].InstVerIter(Cache); if (strcmp(I->Pkg.Arch(), "none") == 0) @@ -1714,6 +1741,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) StopPtyMagic(); CloseLog(); + if (currentStates.Save(false) == false) + _error->Error("Couldn't restore dpkg selection states which were present before this interaction!"); + if (pkgPackageManager::SigINTStop) _error->Warning(_("Operation was interrupted before it could finish")); diff --git a/apt-pkg/statechanges.cc b/apt-pkg/statechanges.cc index ed8f9f524..35af45538 100644 --- a/apt-pkg/statechanges.cc +++ b/apt-pkg/statechanges.cc @@ -25,7 +25,8 @@ public: #define APT_GETTERSETTER(Name, Container) \ void StateChanges::Name(pkgCache::VerIterator const &Ver) \ { \ - Container.push_back(Ver); \ + if (Ver.end() == false) \ + Container.push_back(Ver); \ }\ APT::VersionVector& StateChanges::Name() \ { \ diff --git a/test/integration/test-apt-get-autoremove b/test/integration/test-apt-get-autoremove index 17dba9aec..cfee748af 100755 --- a/test/integration/test-apt-get-autoremove +++ b/test/integration/test-apt-get-autoremove @@ -18,6 +18,8 @@ testmarkedauto 'po-debconf' testsuccess aptget remove debhelper -y testdpkgnotinstalled 'debhelper' testdpkginstalled 'po-debconf' 'unrelated' +echo 'unrelated purge' | dpkg --set-selections +testdpkgstatus 'pi' '1' 'unrelated' AUTOREMOVE='apt autoremove' if [ -n "$SUDO_USER" ]; then @@ -49,14 +51,17 @@ testdpkginstalled 'po-debconf' echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkginstalled 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkginstalled "po-debconf" +testdpkgstatus 'pi' '1' 'unrelated' rm rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkgnotinstalled 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' testmarkedauto sed rootdir/var/log/apt/history.log -e '/^Commandline: / d' \ @@ -71,7 +76,8 @@ Remove: debhelper:i386 (8.0.0) Remove: po-debconf:i386 (1.0.16)' testsuccess aptget install debhelper -y -testdpkginstalled 'unrelated' 'debhelper' 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' +testdpkginstalled 'debhelper' 'po-debconf' testsuccess aptmark auto debhelper testmarkedauto 'debhelper' 'po-debconf' @@ -105,9 +111,11 @@ Reading state information... testsuccess aptget autoremove debhelper -y --allow-change-held-packages testdpkgnotinstalled 'po-debconf' 'debhelper' +testdpkgstatus 'pi' '1' 'unrelated' testmarkedauto testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1 testmarkedauto 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider' insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider -- cgit v1.2.3