Przeglądaj źródła

properly handle already reinstall pkgs in ordering

The bugreport itself describes the case of the ordering code detecting a
loop where none is present, but the testcase finds also cases in which
there is actually a loop and we fail to realize it. --reinstall can be
considered an interactive command through and it usually doesn't
encounter such "hard" problems (= looping essentials), so this is less
serious than it sounds at first.

Closes: 770291
David Kalnischkies 11 lat temu
rodzic
commit
4e6a7e260e
2 zmienionych plików z 134 dodań i 10 usunięć
  1. 36 10
      apt-pkg/packagemanager.cc
  2. 98 0
      test/integration/test-bug-770291-reinstall

+ 36 - 10
apt-pkg/packagemanager.cc

@@ -400,7 +400,8 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       // Check if the current version of the package is available and will satisfy this dependency
 	       if (DepPkg.CurrentVer() == Ver && List->IsNow(DepPkg) == true &&
 		   List->IsFlag(DepPkg,pkgOrderList::Removed) == false &&
-		   DepPkg.State() == PkgIterator::NeedsNothing)
+		   DepPkg.State() == PkgIterator::NeedsNothing &&
+		   (Cache[DepPkg].iFlags & pkgDepCache::ReInstall) != pkgDepCache::ReInstall)
 	       {
 		  Bad = false;
 		  break;
@@ -413,8 +414,13 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       if (PkgLoop == true)
 	       {
 		  if (Debug)
-		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure" << std::endl;
-	          Bad = false;
+		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure";
+		  if (List->IsFlag(DepPkg,pkgOrderList::UnPacked))
+		     Bad = false;
+		  else if (Debug)
+		     std::clog << ", but it isn't unpacked yet";
+		  if (Debug)
+		     std::clog << std::endl;
 	       }
 	    }
 
@@ -426,7 +432,7 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
          if (Bad == false)
          {
             if (Debug)
-               std::clog << OutputInDepth(Depth) << "Found ok dep " << D.TargetPkg() << std::endl;
+               std::clog << OutputInDepth(Depth) << "Found ok dep " << Start.TargetPkg() << std::endl;
             continue;
          }
 
@@ -444,7 +450,8 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       // Check if the current version of the package is available and will satisfy this dependency
 	       if (DepPkg.CurrentVer() == Ver && List->IsNow(DepPkg) == true &&
 		   List->IsFlag(DepPkg,pkgOrderList::Removed) == false &&
-		   DepPkg.State() == PkgIterator::NeedsNothing)
+		   DepPkg.State() == PkgIterator::NeedsNothing &&
+		   (Cache[DepPkg].iFlags & pkgDepCache::ReInstall) != pkgDepCache::ReInstall)
                   continue;
 
 	       // Check if the version that is going to be installed will satisfy the dependency
@@ -454,8 +461,13 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       if (PkgLoop == true)
 	       {
 		  if (Debug)
-		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure" << std::endl;
-	          Bad = false;
+		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure";
+		  if (List->IsFlag(DepPkg,pkgOrderList::UnPacked))
+		     Bad = false;
+		  else if (Debug)
+		     std::clog << ", but it isn't unpacked yet";
+		  if (Debug)
+		     std::clog << std::endl;
 	       }
 	       else
 	       {
@@ -722,7 +734,8 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
 
 		  // See if the current version is ok
 		  if (Pkg.CurrentVer() == Ver && List->IsNow(Pkg) == true &&
-		      Pkg.State() == PkgIterator::NeedsNothing)
+		      Pkg.State() == PkgIterator::NeedsNothing &&
+		      (Cache[Pkg].iFlags & pkgDepCache::ReInstall) != pkgDepCache::ReInstall)
 		  {
 		     Bad = false;
 		     if (Debug)
@@ -744,8 +757,11 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
 		  PkgIterator DepPkg = Ver.ParentPkg();
 
 		  // Not the install version
-		  if (Cache[DepPkg].InstallVer != *I ||
-		      (Cache[DepPkg].Keep() == true && DepPkg.State() == PkgIterator::NeedsNothing))
+		  if (Cache[DepPkg].InstallVer != *I)
+		     continue;
+
+		  if (Cache[DepPkg].Keep() == true && DepPkg.State() == PkgIterator::NeedsNothing &&
+			(Cache[DepPkg].iFlags & pkgDepCache::ReInstall) != pkgDepCache::ReInstall)
 		     continue;
 
 		  if (List->IsFlag(DepPkg,pkgOrderList::Configured))
@@ -757,6 +773,16 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
 		  // check if it needs unpack or if if configure is enough
 		  if (List->IsFlag(DepPkg,pkgOrderList::UnPacked) == false)
 		  {
+		     // two packages pre-depending on each other can't be handled sanely
+		     if (List->IsFlag(DepPkg,pkgOrderList::Loop) && PkgLoop)
+		     {
+			// this isn't an error as there is potential for something else to satisfy it
+			// (like a provides or an or-group member)
+			if (Debug)
+			   clog << OutputInDepth(Depth) << "Unpack loop detected between " << DepPkg.FullName() << " and " << Pkg.FullName() << endl;
+			continue;
+		     }
+
 		     if (Debug)
 			clog << OutputInDepth(Depth) << "Trying to SmartUnpack " << DepPkg.FullName() << endl;
 		     if (NonLoopingSmart(UNPACK_IMMEDIATE, Pkg, DepPkg, Depth, PkgLoop, &Bad, &Changed) == false)

+ 98 - 0
test/integration/test-bug-770291-reinstall

@@ -0,0 +1,98 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'i386'
+
+insertpackage 'unstable,installed' 'libc6' 'i386' '1'
+insertpackage 'unstable,installed' 'libselinux1' 'i386' '1'
+
+cp rootdir/var/lib/dpkg/status dpkg.status
+
+insertpackage 'unstable,installed' 'init' 'i386' '1' 'Depends: systemd-sysv
+Essential: yes'
+insertpackage 'unstable,installed' 'systemd-sysv' 'i386' '215-5+b1' 'Depends: systemd (= 215-5+b1)
+Pre-Depends: systemd'
+# fun fact: we need these two pre-depends to get systemd ordered before systemd-sysv as
+# many pre-depends mean: do early (as they are a pain, so get them out of the way early)
+insertpackage 'unstable,installed' 'systemd' 'i386' '215-5+b1' 'Pre-Depends: libc6, libselinux1'
+
+# depends loop
+insertpackage 'unstable,installed' 'dependsA' 'i386' '1' 'Depends: dependsB
+Essential: yes'
+insertpackage 'unstable,installed' 'dependsB' 'i386' '1' 'Depends: dependsA
+Essential: yes'
+
+# pre-depends loop
+insertpackage 'unstable,installed' 'predependsA' 'i386' '1' 'Pre-Depends: predependsB
+Essential: yes'
+insertpackage 'unstable,installed' 'predependsB' 'i386' '1' 'Pre-Depends: predependsA
+Essential: yes'
+
+# pre-depends-to-depends loop
+insertpackage 'unstable,installed' 'predependsdependsA' 'i386' '1' 'Pre-Depends: predependsdependsB
+Essential: yes'
+insertpackage 'unstable,installed' 'predependsdependsB' 'i386' '1' 'Depends: predependsdependsA
+Essential: yes'
+
+setupaptarchive
+
+testequal 'Reading package lists...
+Building dependency tree...
+0 upgraded, 0 newly installed, 2 reinstalled, 0 to remove and 0 not upgraded.
+Inst systemd [215-5+b1] (215-5+b1 unstable [i386])
+Conf systemd (215-5+b1 unstable [i386])
+Inst systemd-sysv [215-5+b1] (215-5+b1 unstable [i386])
+Conf systemd-sysv (215-5+b1 unstable [i386])' aptget install --reinstall systemd systemd-sysv -s
+
+testequal 'Reading package lists...
+Building dependency tree...
+0 upgraded, 0 newly installed, 2 reinstalled, 0 to remove and 0 not upgraded.
+Inst dependsA [1] (1 unstable [i386])
+Inst dependsB [1] (1 unstable [i386])
+Conf dependsB (1 unstable [i386])
+Conf dependsA (1 unstable [i386])' aptget install --reinstall dependsA dependsB -s
+
+# there is a chance dpkg can actually do these, BUT this depends on the maintainerscripts (not) present
+# which is very very risky to depend on (and apt doesn't know about that anyhow).
+testfailure aptget install --reinstall predependsA predependsB -s -o Debug::pkgPackageManager=1
+testequal "E: Couldn't configure predependsA:i386, probably a dependency cycle." tail -n1 rootdir/tmp/testfailure.output
+
+# FIXME: the error message is a catch all here, not like the one above
+testfailure aptget install --reinstall predependsdependsA predependsdependsB -s -o Debug::pkgPackageManager=1
+testequal "E: Could not configure 'predependsdependsB:i386'. " tail -n1 rootdir/tmp/testfailure.output
+
+
+msgmsg 'While we are at it, lets try these loops without reinstall as well'
+cp dpkg.status rootdir/var/lib/dpkg/status
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+  systemd systemd-sysv
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst systemd (215-5+b1 unstable [i386])
+Conf systemd (215-5+b1 unstable [i386])
+Inst systemd-sysv (215-5+b1 unstable [i386])
+Conf systemd-sysv (215-5+b1 unstable [i386])' aptget install systemd systemd-sysv -s
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+  dependsA dependsB
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst dependsA (1 unstable [i386]) []
+Inst dependsB (1 unstable [i386])
+Conf dependsB (1 unstable [i386])
+Conf dependsA (1 unstable [i386])' aptget install dependsA dependsB -s
+
+# there is a chance dpkg can actually do these, BUT this depends on the maintainerscripts (not) present
+# which is very very risky to depend on (and apt doesn't know about that anyhow).
+testfailure aptget install predependsA predependsB -s -o Debug::pkgPackageManager=1
+testequal "E: Couldn't configure predependsA:i386, probably a dependency cycle." tail -n1 rootdir/tmp/testfailure.output
+
+# FIXME: the error message is a catch all here, not like the one above
+testfailure aptget install predependsdependsA predependsdependsB -s -o Debug::pkgPackageManager=1
+testequal "E: Could not configure 'predependsdependsB:i386'. " tail -n1 rootdir/tmp/testfailure.output