Ver código fonte

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.
David Kalnischkies 10 anos atrás
pai
commit
b820fd59c4

+ 36 - 6
apt-pkg/deb/dpkgpm.cc

@@ -19,6 +19,7 @@
 #include <apt-pkg/install-progress.h>
 #include <apt-pkg/install-progress.h>
 #include <apt-pkg/packagemanager.h>
 #include <apt-pkg/packagemanager.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/statechanges.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgcache.h>
@@ -208,6 +209,14 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
    return Ver;
    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					/*{{{*/
 // DPkgPM::pkgDPkgPM - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
@@ -1342,6 +1351,28 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	 List.erase(std::next(List.begin(), notconfidx), List.end());
 	 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;
    d->stdin_is_dev_null = false;
 
 
    // create log
    // create log
@@ -1505,12 +1536,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	    {
 	    {
 	       pkgCache::VerIterator PkgVer;
 	       pkgCache::VerIterator PkgVer;
 	       std::string name = I->Pkg.Name();
 	       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
 	       else
 		  PkgVer = Cache[I->Pkg].InstVerIter(Cache);
 		  PkgVer = Cache[I->Pkg].InstVerIter(Cache);
 	       if (strcmp(I->Pkg.Arch(), "none") == 0)
 	       if (strcmp(I->Pkg.Arch(), "none") == 0)
@@ -1714,6 +1741,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    StopPtyMagic();
    StopPtyMagic();
    CloseLog();
    CloseLog();
 
 
+   if (currentStates.Save(false) == false)
+      _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
+
    if (pkgPackageManager::SigINTStop)
    if (pkgPackageManager::SigINTStop)
        _error->Warning(_("Operation was interrupted before it could finish"));
        _error->Warning(_("Operation was interrupted before it could finish"));
 
 

+ 2 - 1
apt-pkg/statechanges.cc

@@ -25,7 +25,8 @@ public:
 #define APT_GETTERSETTER(Name, Container) \
 #define APT_GETTERSETTER(Name, Container) \
 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
 { \
 { \
-   Container.push_back(Ver); \
+   if (Ver.end() == false) \
+      Container.push_back(Ver); \
 }\
 }\
 APT::VersionVector& StateChanges::Name() \
 APT::VersionVector& StateChanges::Name() \
 { \
 { \

+ 9 - 1
test/integration/test-apt-get-autoremove

@@ -18,6 +18,8 @@ testmarkedauto 'po-debconf'
 testsuccess aptget remove debhelper -y
 testsuccess aptget remove debhelper -y
 testdpkgnotinstalled 'debhelper'
 testdpkgnotinstalled 'debhelper'
 testdpkginstalled 'po-debconf' 'unrelated'
 testdpkginstalled 'po-debconf' 'unrelated'
+echo 'unrelated purge' | dpkg --set-selections
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 AUTOREMOVE='apt autoremove'
 AUTOREMOVE='apt autoremove'
 if [ -n "$SUDO_USER" ]; then
 if [ -n "$SUDO_USER" ]; then
@@ -49,14 +51,17 @@ testdpkginstalled 'po-debconf'
 echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkginstalled 'po-debconf'
 testdpkginstalled 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkginstalled "po-debconf"
 testdpkginstalled "po-debconf"
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 rm rootdir/etc/apt/apt.conf.d/00autoremove
 rm rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkgnotinstalled 'po-debconf'
 testdpkgnotinstalled 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 testmarkedauto
 testmarkedauto
 
 
 sed rootdir/var/log/apt/history.log -e '/^Commandline: / d' \
 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)'
 Remove: po-debconf:i386 (1.0.16)'
 
 
 testsuccess aptget install debhelper -y
 testsuccess aptget install debhelper -y
-testdpkginstalled 'unrelated' 'debhelper' 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
+testdpkginstalled 'debhelper' 'po-debconf'
 testsuccess aptmark auto debhelper
 testsuccess aptmark auto debhelper
 
 
 testmarkedauto 'debhelper' 'po-debconf'
 testmarkedauto 'debhelper' 'po-debconf'
@@ -105,9 +111,11 @@ Reading state information...
 
 
 testsuccess aptget autoremove debhelper -y --allow-change-held-packages
 testsuccess aptget autoremove debhelper -y --allow-change-held-packages
 testdpkgnotinstalled 'po-debconf' 'debhelper'
 testdpkgnotinstalled 'po-debconf' 'debhelper'
+testdpkgstatus 'pi' '1' 'unrelated'
 testmarkedauto
 testmarkedauto
 testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1
 testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1
 testmarkedauto 'po-debconf'
 testmarkedauto 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider'
 insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider'
 insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider
 insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider