Browse Source

fix tight loop detection and temporary removes

As outlined in #748355 apt segfaulted if it encountered a loop between a
package pre-depending on a package conflicting with the previous as it
ended up in an endless loop trying to unpack 'the other package'.

In this specific case as an essential package is involved a lot of force
needs to be applied, but can also be caused by 'normal' tight loops and
highlights a problem in how we handle breaks which we want to avoid.

The fix comes in multiple entangled changes:
1. All Smart* calls are guarded with loop detection. Some already had it,
   some had parts of it, some did it incorrect, and some didn't even try.
2. temporary removes to avoid a loop (which is done if a loop is
   detected) prevent the unpack of this looping package (we tried to unpack
   it to avoid the conflict/breaks, but due to a loop we couldn't, so we
   remove/deconfigure it instead which means we can't unpack it now)
3. handle conflicts and breaks very similar instead of duplicating most
   of the code. The only remaining difference is, as it should:
   deconfigure is enough for breaks, for conflicts we need the big hammer
tags/debian/1.0.4
David Kalnischkies 7 years ago
parent
commit
0eb4af9d3d
6 changed files with 251 additions and 191 deletions
  1. +156
    -171
      apt-pkg/packagemanager.cc
  2. +9
    -2
      apt-pkg/packagemanager.h
  3. +10
    -9
      test/integration/test-bug-618288-multiarch-same-lockstep
  4. +21
    -7
      test/integration/test-bug-673536-pre-depends-breaks-loop
  5. +4
    -2
      test/integration/test-conflicts-loop
  6. +51
    -0
      test/integration/test-essential-force-loopbreak

+ 156
- 171
apt-pkg/packagemanager.cc View File

@@ -261,7 +261,7 @@ bool pkgPackageManager::CheckRConflicts(PkgIterator Pkg,DepIterator D,
if (Cache.VS().CheckDep(Ver,D->CompareOp,D.TargetVer()) == false)
continue;

if (EarlyRemove(D.ParentPkg()) == false)
if (EarlyRemove(D.ParentPkg(), &D) == false)
return _error->Error("Reverse conflicts early remove for package '%s' failed",
Pkg.FullName().c_str());
}
@@ -313,18 +313,41 @@ bool pkgPackageManager::ConfigureAll()
return true;
}
/*}}}*/
// PM::NonLoopingSmart - helper to avoid loops while calling Smart methods /*{{{*/
// -----------------------------------------------------------------------
/* ensures that a loop of the form A depends B, B depends A (and similar)
is not leading us down into infinite recursion segfault land */
bool pkgPackageManager::NonLoopingSmart(SmartAction const action, pkgCache::PkgIterator &Pkg,
pkgCache::PkgIterator DepPkg, int const Depth, bool const PkgLoop,
bool * const Bad, bool * const Changed)
{
if (PkgLoop == false)
List->Flag(Pkg,pkgOrderList::Loop);
bool success = false;
switch(action)
{
case UNPACK_IMMEDIATE: success = SmartUnPack(DepPkg, true, Depth + 1); break;
case UNPACK: success = SmartUnPack(DepPkg, false, Depth + 1); break;
case CONFIGURE: success = SmartConfigure(DepPkg, Depth + 1); break;
}
if (PkgLoop == false)
List->RmFlag(Pkg,pkgOrderList::Loop);

if (success == false)
return false;

if (Bad != NULL)
*Bad = false;
if (Changed != NULL && List->IsFlag(DepPkg,pkgOrderList::Loop) == false)
*Changed = true;
return true;
}
/*}}}*/
// PM::SmartConfigure - Perform immediate configuration of the pkg /*{{{*/
// ---------------------------------------------------------------------
/* This function tries to put the system in a state where Pkg can be configured.
This involves checking each of Pkg's dependanies and unpacking and
configuring packages where needed.
Note on failure: This method can fail, without causing any problems.
This can happen when using Immediate-Configure-All, SmartUnPack may call
SmartConfigure, it may fail because of a complex dependency situation, but
a error will only be reported if ConfigureAll fails. This is why some of the
messages this function reports on failure (return false;) as just warnings
only shown when debuging*/
This involves checking each of Pkg's dependencies and unpacking and
configuring packages where needed. */
bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
{
// If this is true, only check and correct and dependencies without the Loop flag
@@ -339,9 +362,9 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
}

VerIterator const instVer = Cache[Pkg].InstVerIter(Cache);
/* Because of the ordered list, most dependencies should be unpacked,
however if there is a loop (A depends on B, B depends on A) this will not
/* Because of the ordered list, most dependencies should be unpacked,
however if there is a loop (A depends on B, B depends on A) this will not
be the case, so check for dependencies before configuring. */
bool Bad = false, Changed = false;
const unsigned int max_loops = _config->FindI("APT::pkgPackageManager::MaxLoopCount", 5000);
@@ -388,24 +411,15 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
if (Debug)
std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure" << std::endl;
Bad = false;
break;
}
else
{
if (Debug)
clog << OutputInDepth(Depth) << "Unpacking " << DepPkg.FullName() << " to avoid loop " << Cur << endl;
if (PkgLoop == false)
List->Flag(Pkg,pkgOrderList::Loop);
if (SmartUnPack(DepPkg, true, Depth + 1) == false)
if (NonLoopingSmart(UNPACK_IMMEDIATE, Pkg, DepPkg, Depth, PkgLoop, &Bad, &Changed) == false)
return false;
Bad = false;
if (List->IsFlag(DepPkg,pkgOrderList::Loop) == false)
Changed = true;
if (PkgLoop == false)
List->RmFlag(Pkg,pkgOrderList::Loop);
if (Bad == false)
break;
}
break;
}

if (Cur == End || Bad == false)
@@ -460,22 +474,12 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
Bad = false;
break;
}
/* Check for a loop to prevent one forming
If A depends on B and B depends on A, SmartConfigure will
just hop between them if this is not checked. Dont remove the
loop flag after finishing however as loop is already set.
This means that there is another SmartConfigure call for this
package and it will remove the loop flag */
if (PkgLoop == false)
List->Flag(Pkg,pkgOrderList::Loop);
if (SmartConfigure(DepPkg, Depth + 1) == false)
if (Debug)
std::clog << OutputInDepth(Depth) << "Configure already unpacked " << DepPkg << std::endl;
if (NonLoopingSmart(CONFIGURE, Pkg, DepPkg, Depth, PkgLoop, &Bad, &Changed) == false)
return false;
Bad = false;
if (List->IsFlag(DepPkg,pkgOrderList::Loop) == false)
Changed = true;
if (PkgLoop == false)
List->RmFlag(Pkg,pkgOrderList::Loop);
break;

}
else if (List->IsFlag(DepPkg,pkgOrderList::Configured))
{
@@ -494,16 +498,16 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
if (i++ > max_loops)
return _error->Error("Internal error: MaxLoopCount reached in SmartUnPack (2) for %s, aborting", Pkg.FullName().c_str());
} while (Changed == true);
if (Bad == true)
return _error->Error(_("Could not configure '%s'. "),Pkg.FullName().c_str());
if (PkgLoop) return true;

static std::string const conf = _config->Find("PackageManager::Configure","all");
static bool const ConfigurePkgs = (conf == "all" || conf == "smart");

if (List->IsFlag(Pkg,pkgOrderList::Configured))
if (List->IsFlag(Pkg,pkgOrderList::Configured))
return _error->Error("Internal configure error on '%s'.", Pkg.FullName().c_str());

if (ConfigurePkgs == true && Configure(Pkg) == false)
@@ -535,29 +539,37 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
// ---------------------------------------------------------------------
/* This is called to deal with conflicts arising from unpacking */
bool pkgPackageManager::EarlyRemove(PkgIterator Pkg)
{
return EarlyRemove(Pkg, NULL);
}
bool pkgPackageManager::EarlyRemove(PkgIterator Pkg, DepIterator const * const Dep)
{
if (List->IsNow(Pkg) == false)
return true;
// Already removed it
if (List->IsFlag(Pkg,pkgOrderList::Removed) == true)
return true;
// Woops, it will not be re-installed!
if (List->IsFlag(Pkg,pkgOrderList::InList) == false)
return false;

// these breaks on M-A:same packages can be dealt with. They 'loop' by design
if (Dep != NULL && (*Dep)->Type == pkgCache::Dep::DpkgBreaks && Dep->IsMultiArchImplicit() == true)
return true;

// Essential packages get special treatment
bool IsEssential = false;
if ((Pkg->Flags & pkgCache::Flag::Essential) != 0 ||
(Pkg->Flags & pkgCache::Flag::Important) != 0)
IsEssential = true;

/* Check for packages that are the dependents of essential packages and
/* Check for packages that are the dependents of essential packages and
promote them too */
if (Pkg->CurrentVer != 0)
{
for (DepIterator D = Pkg.RevDependsList(); D.end() == false &&
for (pkgCache::DepIterator D = Pkg.RevDependsList(); D.end() == false &&
IsEssential == false; ++D)
if (D->Type == pkgCache::Dep::Depends || D->Type == pkgCache::Dep::PreDepends)
if ((D.ParentPkg()->Flags & pkgCache::Flag::Essential) != 0 ||
@@ -574,11 +586,14 @@ bool pkgPackageManager::EarlyRemove(PkgIterator Pkg)
"but if you really want to do it, activate the "
"APT::Force-LoopBreak option."),Pkg.FullName().c_str());
}
// dpkg will auto-deconfigure it, no need for the big remove hammer
else if (Dep != NULL && (*Dep)->Type == pkgCache::Dep::DpkgBreaks)
return true;

bool Res = SmartRemove(Pkg);
if (Cache[Pkg].Delete() == false)
List->Flag(Pkg,pkgOrderList::Removed,pkgOrderList::States);
return Res;
}
/*}}}*/
@@ -623,13 +638,14 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c

VerIterator const instVer = Cache[Pkg].InstVerIter(Cache);

/* PreUnpack Checks: This loop checks and attempts to rectify and problems that would prevent the package being unpacked.
/* PreUnpack Checks: This loop checks and attempts to rectify any problems that would prevent the package being unpacked.
It addresses: PreDepends, Conflicts, Obsoletes and Breaks (DpkgBreaks). Any resolutions that do not require it should
avoid configuration (calling SmartUnpack with Immediate=true), this is because when unpacking some packages with
complex dependency structures, trying to configure some packages while breaking the loops can complicate things .
complex dependency structures, trying to configure some packages while breaking the loops can complicate things.
This will be either dealt with if the package is configured as a dependency of Pkg (if and when Pkg is configured),
or by the ConfigureAll call at the end of the for loop in OrderInstall. */
bool Changed = false;
bool SomethingBad = false, Changed = false;
bool couldBeTemporaryRemoved = Depth != 0 && List->IsFlag(Pkg,pkgOrderList::Removed) == false;
const unsigned int max_loops = _config->FindI("APT::pkgPackageManager::MaxLoopCount", 5000);
unsigned int i = 0;
do
@@ -677,183 +693,142 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
for (Version **I = VList; *I != 0; ++I)
{
VerIterator Ver(Cache,*I);
PkgIterator Pkg = Ver.ParentPkg();
PkgIterator DepPkg = Ver.ParentPkg();

// Not the install version
if (Cache[Pkg].InstallVer != *I ||
(Cache[Pkg].Keep() == true && Pkg.State() == PkgIterator::NeedsNothing))
if (Cache[DepPkg].InstallVer != *I ||
(Cache[DepPkg].Keep() == true && DepPkg.State() == PkgIterator::NeedsNothing))
continue;

if (List->IsFlag(Pkg,pkgOrderList::Configured))
if (List->IsFlag(DepPkg,pkgOrderList::Configured))
{
Bad = false;
break;
}

// check if it needs unpack or if if configure is enough
if (List->IsFlag(Pkg,pkgOrderList::UnPacked) == false)
if (List->IsFlag(DepPkg,pkgOrderList::UnPacked) == false)
{
if (Debug)
clog << OutputInDepth(Depth) << "Trying to SmartUnpack " << Pkg.FullName() << endl;
// SmartUnpack with the ImmediateFlag to ensure its really ready
if (SmartUnPack(Pkg, true, Depth + 1) == false)
clog << OutputInDepth(Depth) << "Trying to SmartUnpack " << DepPkg.FullName() << endl;
if (NonLoopingSmart(UNPACK_IMMEDIATE, Pkg, DepPkg, Depth, PkgLoop, &Bad, &Changed) == false)
return false;
Bad = false;
if (List->IsFlag(Pkg,pkgOrderList::Loop) == false)
Changed = true;
break;
}
else
{
if (Debug)
clog << OutputInDepth(Depth) << "Trying to SmartConfigure " << Pkg.FullName() << endl;
if (SmartConfigure(Pkg, Depth + 1) == false)
clog << OutputInDepth(Depth) << "Trying to SmartConfigure " << DepPkg.FullName() << endl;
if (NonLoopingSmart(CONFIGURE, Pkg, DepPkg, Depth, PkgLoop, &Bad, &Changed) == false)
return false;
Bad = false;
if (List->IsFlag(Pkg,pkgOrderList::Loop) == false)
Changed = true;
break;
}
break;
}
}

if (Bad == true)
{
if (Start == End)
return _error->Error("Couldn't configure pre-depend %s for %s, "
"probably a dependency cycle.",
End.TargetPkg().FullName().c_str(),Pkg.FullName().c_str());
}
else
continue;
SomethingBad = true;
}
else if (End->Type == pkgCache::Dep::Conflicts ||
End->Type == pkgCache::Dep::Obsoletes)
End->Type == pkgCache::Dep::Obsoletes ||
End->Type == pkgCache::Dep::DpkgBreaks)
{
/* Look for conflicts. Two packages that are both in the install
state cannot conflict so we don't check.. */
SPtrArray<Version *> VList = End.AllTargets();
for (Version **I = VList; *I != 0; I++)
for (Version **I = VList; *I != 0; ++I)
{
VerIterator Ver(Cache,*I);
PkgIterator ConflictPkg = Ver.ParentPkg();
VerIterator InstallVer(Cache,Cache[ConflictPkg].InstallVer);
if (ConflictPkg.CurrentVer() != Ver)
{
if (Debug)
std::clog << OutputInDepth(Depth) << "Ignore not-installed version " << Ver.VerStr() << " of " << ConflictPkg.FullName() << " for " << End << std::endl;
continue;
}

if (List->IsNow(ConflictPkg) == false)
{
if (Debug)
std::clog << OutputInDepth(Depth) << "Ignore already dealt-with version " << Ver.VerStr() << " of " << ConflictPkg.FullName() << " for " << End << std::endl;
continue;
}

// See if the current version is conflicting
if (ConflictPkg.CurrentVer() == Ver && List->IsNow(ConflictPkg))
if (List->IsFlag(ConflictPkg,pkgOrderList::Removed) == true)
{
if (Debug)
clog << OutputInDepth(Depth) << Pkg.FullName() << " conflicts with " << ConflictPkg.FullName() << endl;
/* If a loop is not present or has not yet been detected, attempt to unpack packages
to resolve this conflict. If there is a loop present, remove packages to resolve this conflict */
if (List->IsFlag(ConflictPkg,pkgOrderList::Loop) == false)
clog << OutputInDepth(Depth) << "Ignoring " << End << " as " << ConflictPkg.FullName() << "was temporarily removed" << endl;
continue;
}

if (List->IsFlag(ConflictPkg,pkgOrderList::Loop) && PkgLoop)
{
if (End->Type == pkgCache::Dep::DpkgBreaks && End.IsMultiArchImplicit() == true)
{
if (Cache[ConflictPkg].Keep() == 0 && Cache[ConflictPkg].InstallVer != 0)
{
if (Debug)
clog << OutputInDepth(Depth) << "Unpacking " << ConflictPkg.FullName() << " to prevent conflict" << endl;
List->Flag(Pkg,pkgOrderList::Loop);
if (SmartUnPack(ConflictPkg,false, Depth + 1) == false)
return false;
if (List->IsFlag(ConflictPkg,pkgOrderList::Loop) == false)
Changed = true;
// Remove loop to allow it to be used later if needed
List->RmFlag(Pkg,pkgOrderList::Loop);
}
else if (EarlyRemove(ConflictPkg) == false)
return _error->Error("Internal Error, Could not early remove %s (1)",ConflictPkg.FullName().c_str());
if (Debug)
clog << OutputInDepth(Depth) << "Because dependency is MultiArchImplicit we ignored looping on: " << ConflictPkg << endl;
continue;
}
else if (List->IsFlag(ConflictPkg,pkgOrderList::Removed) == false)
if (Debug)
{
if (Debug)
if (End->Type == pkgCache::Dep::DpkgBreaks)
clog << OutputInDepth(Depth) << "Because of breaks knot, deconfigure " << ConflictPkg.FullName() << " temporarily" << endl;
else
clog << OutputInDepth(Depth) << "Because of conflict knot, removing " << ConflictPkg.FullName() << " temporarily" << endl;
if (EarlyRemove(ConflictPkg) == false)
return _error->Error("Internal Error, Could not early remove %s (2)",ConflictPkg.FullName().c_str());
}
}
}
}
else if (End->Type == pkgCache::Dep::DpkgBreaks)
{
SPtrArray<Version *> VList = End.AllTargets();
for (Version **I = VList; *I != 0; ++I)
{
VerIterator Ver(Cache,*I);
PkgIterator BrokenPkg = Ver.ParentPkg();
if (BrokenPkg.CurrentVer() != Ver)
{
if (Debug)
std::clog << OutputInDepth(Depth) << " Ignore not-installed version " << Ver.VerStr() << " of " << Pkg.FullName() << " for " << End << std::endl;
if (EarlyRemove(ConflictPkg, &End) == false)
return _error->Error("Internal Error, Could not early remove %s (2)",ConflictPkg.FullName().c_str());
SomethingBad = true;
continue;
}

// Check if it needs to be unpacked
if (List->IsFlag(BrokenPkg,pkgOrderList::InList) && Cache[BrokenPkg].Delete() == false &&
List->IsNow(BrokenPkg))
if (Cache[ConflictPkg].Delete() == false)
{
if (List->IsFlag(BrokenPkg,pkgOrderList::Loop) && PkgLoop)
if (Debug)
{
// This dependency has already been dealt with by another SmartUnPack on Pkg
break;
clog << OutputInDepth(Depth) << "Unpacking " << ConflictPkg.FullName() << " to avoid " << End;
if (PkgLoop == true)
clog << " (Looping)";
clog << std::endl;
}
else
// we would like to avoid temporary removals and all that at best via a simple unpack
_error->PushToStack();
if (NonLoopingSmart(UNPACK, Pkg, ConflictPkg, Depth, PkgLoop, NULL, &Changed) == false)
{
// Found a break, so see if we can unpack the package to avoid it
// but do not set loop if another SmartUnPack already deals with it
// Also, avoid it if the package we would unpack pre-depends on this one
VerIterator InstallVer(Cache,Cache[BrokenPkg].InstallVer);
bool circle = false;
for (pkgCache::DepIterator D = InstallVer.DependsList(); D.end() == false; ++D)
// but if it fails ignore this failure and look for alternative ways of solving
if (Debug)
{
if (D->Type != pkgCache::Dep::PreDepends)
continue;
SPtrArray<Version *> VL = D.AllTargets();
for (Version **I = VL; *I != 0; ++I)
{
VerIterator V(Cache,*I);
PkgIterator P = V.ParentPkg();
// we are checking for installation as an easy 'protection' against or-groups and (unchosen) providers
if (P != Pkg || (P.CurrentVer() != V && Cache[P].InstallVer != V))
continue;
circle = true;
break;
}
if (circle == true)
break;
clog << OutputInDepth(Depth) << "Avoidance unpack of " << ConflictPkg.FullName() << " failed for " << End << std::endl;
_error->DumpErrors(std::clog);
}
_error->RevertToStack();
// ignorance can only happen if a) one of the offenders is already gone
if (List->IsFlag(ConflictPkg,pkgOrderList::Removed) == true)
{
if (Debug)
clog << OutputInDepth(Depth) << "But " << ConflictPkg.FullName() << " was temporarily removed in the meantime to satisfy " << End << endl;
}
if (circle == true)
else if (List->IsFlag(Pkg,pkgOrderList::Removed) == true)
{
if (Debug)
clog << OutputInDepth(Depth) << " Avoiding " << End << " avoided as " << BrokenPkg.FullName() << " has a pre-depends on " << Pkg.FullName() << std::endl;
continue;
clog << OutputInDepth(Depth) << "But " << Pkg.FullName() << " was temporarily removed in the meantime to satisfy " << End << endl;
}
// or b) we can make one go (removal or dpkg auto-deconfigure)
else
{
if (Debug)
{
clog << OutputInDepth(Depth) << " Unpacking " << BrokenPkg.FullName() << " to avoid " << End;
if (PkgLoop == true)
clog << " (Looping)";
clog << std::endl;
}
if (PkgLoop == false)
List->Flag(Pkg,pkgOrderList::Loop);
if (SmartUnPack(BrokenPkg, false, Depth + 1) == false)
return false;
if (List->IsFlag(BrokenPkg,pkgOrderList::Loop) == false)
Changed = true;
if (PkgLoop == false)
List->RmFlag(Pkg,pkgOrderList::Loop);
clog << OutputInDepth(Depth) << "So temprorary remove/deconfigure " << ConflictPkg.FullName() << " to satisfy " << End << endl;
if (EarlyRemove(ConflictPkg, &End) == false)
return _error->Error("Internal Error, Could not early remove %s (2)",ConflictPkg.FullName().c_str());
}
}
else
_error->MergeWithStack();
}
// Check if a package needs to be removed
else if (Cache[BrokenPkg].Delete() == true && List->IsFlag(BrokenPkg,pkgOrderList::Configured) == false)
else
{
if (Debug)
clog << OutputInDepth(Depth) << " Removing " << BrokenPkg.FullName() << " to avoid " << End << endl;
if (SmartRemove(BrokenPkg) == false)
return false;
clog << OutputInDepth(Depth) << "Removing " << ConflictPkg.FullName() << " now to avoid " << End << endl;
// no earlyremove() here as user has already agreed to the permanent removal
if (SmartRemove(Pkg) == false)
return _error->Error("Internal Error, Could not early remove %s (1)",ConflictPkg.FullName().c_str());
}
}
}
@@ -861,7 +836,17 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
if (i++ > max_loops)
return _error->Error("Internal error: APT::pkgPackageManager::MaxLoopCount reached in SmartConfigure for %s, aborting", Pkg.FullName().c_str());
} while (Changed == true);

if (SomethingBad == true)
return _error->Error("Couldn't configure %s, probably a dependency cycle.", Pkg.FullName().c_str());

if (couldBeTemporaryRemoved == true && List->IsFlag(Pkg,pkgOrderList::Removed) == true)
{
if (Debug)
std::clog << OutputInDepth(Depth) << "Prevent unpack as " << Pkg << " is currently temporarily removed" << std::endl;
return true;
}

// Check for reverse conflicts.
if (CheckRConflicts(Pkg,Pkg.RevDependsList(),
instVer.VerStr()) == false)
@@ -922,7 +907,7 @@ bool pkgPackageManager::SmartUnPack(PkgIterator Pkg, bool const Immediate, int c
if (Immediate == true) {
// Perform immedate configuration of the package.
if (SmartConfigure(Pkg, Depth + 1) == false)
_error->Warning(_("Could not perform immediate configuration on '%s'. "
_error->Error(_("Could not perform immediate configuration on '%s'. "
"Please see man 5 apt.conf under APT::Immediate-Configure for details. (%d)"),Pkg.FullName().c_str(),2);
}


+ 9
- 2
apt-pkg/packagemanager.h View File

@@ -84,8 +84,9 @@ class pkgPackageManager : protected pkgCache::Namespace
bool SmartUnPack(PkgIterator Pkg) APT_MUSTCHECK;
bool SmartUnPack(PkgIterator Pkg, bool const Immediate, int const Depth) APT_MUSTCHECK;
bool SmartRemove(PkgIterator Pkg) APT_MUSTCHECK;
bool EarlyRemove(PkgIterator Pkg) APT_MUSTCHECK;
bool EarlyRemove(PkgIterator Pkg, DepIterator const * const Dep) APT_MUSTCHECK;
APT_DEPRECATED bool EarlyRemove(PkgIterator Pkg) APT_MUSTCHECK;

// The Actual installation implementation
virtual bool Install(PkgIterator /*Pkg*/,std::string /*File*/) {return false;};
virtual bool Configure(PkgIterator /*Pkg*/) {return false;};
@@ -139,6 +140,12 @@ class pkgPackageManager : protected pkgCache::Namespace

pkgPackageManager(pkgDepCache *Cache);
virtual ~pkgPackageManager();

private:
enum APT_HIDDEN SmartAction { UNPACK_IMMEDIATE, UNPACK, CONFIGURE };
APT_HIDDEN bool NonLoopingSmart(SmartAction const action, pkgCache::PkgIterator &Pkg,
pkgCache::PkgIterator DepPkg, int const Depth, bool const PkgLoop,
bool * const Bad, bool * const Changed) APT_MUSTCHECK;
};

#endif

+ 10
- 9
test/integration/test-bug-618288-multiarch-same-lockstep View File

@@ -16,22 +16,23 @@ buildsimplenativepackage 'apt' 'i386' '2' 'unstable' 'Depends: libsame (= 2)' ''
buildsimplenativepackage 'apt2' 'amd64' '2' 'unstable' 'Depends: libsame (= 2)' '' 'required'

setupaptarchive
aptget dist-upgrade -s >output.apt 2>&1
testsuccess aptget dist-upgrade -s -o Debug::pkgPackageManager=1

# order in switch libsame:{amd64,i386} are unpacked is irrelevant, as both are installed - but we need to do it together
LS_U_AMD="$(grep -o -n '^Inst libsame ' output.apt | cut -d: -f1)"
LS_U_INT="$(grep -o -n '^Inst libsame:i386 ' output.apt | cut -d: -f1)"
LS_C_AMD="$(grep -o -n '^Conf libsame ' output.apt | cut -d: -f1)"
LS_C_INT="$(grep -o -n '^Conf libsame:i386 ' output.apt | cut -d: -f1)"
OUTPUT=rootdir/tmp/testsuccess.output
LS_U_AMD="$(grep -o -n '^Inst libsame ' $OUTPUT | cut -d: -f1)"
LS_U_INT="$(grep -o -n '^Inst libsame:i386 ' $OUTPUT | cut -d: -f1)"
LS_C_AMD="$(grep -o -n '^Conf libsame ' $OUTPUT | cut -d: -f1)"
LS_C_INT="$(grep -o -n '^Conf libsame:i386 ' $OUTPUT | cut -d: -f1)"

msgtest 'Test if libsame:amd64 unpack before configure'
msgtest 'Test if' 'libsame:amd64 unpack before configure'
test "$LS_U_AMD" -lt "$LS_C_AMD" && msgpass || msgfail

msgtest 'Test if libsame:i386 unpack before configure'
msgtest 'Test if' 'libsame:i386 unpack before configure'
test "$LS_U_INT" -lt "$LS_C_INT" && msgpass || msgfail

msgtest 'Test if libsame:amd64 unpack is before libsame:i386 configure'
msgtest 'Test if' 'libsame:amd64 unpack is before libsame:i386 configure'
test "$LS_U_AMD" -lt "$LS_C_INT" && msgpass || msgfail

msgtest 'Test if libsame:i386 unpack is before libsame:amd64 configure'
msgtest 'Test if' 'libsame:i386 unpack is before libsame:amd64 configure'
test "$LS_U_INT" -lt "$LS_C_AMD" && msgpass || msgfail

+ 21
- 7
test/integration/test-bug-673536-pre-depends-breaks-loop View File

@@ -6,18 +6,32 @@ TESTDIR=$(readlink -f $(dirname $0))
setupenvironment
configarchitecture 'native'

buildsimplenativepackage 'basic' 'native' '1' 'stable'
buildsimplenativepackage 'advanced' 'native' '1' 'stable'
buildsimplenativepackage 'advanced' 'native' '2' 'unstable' 'Pre-Depends: basic'
buildsimplenativepackage 'basic' 'native' '2' 'unstable' 'Pre-Depends: common'
buildsimplenativepackage 'common' 'native' '2' 'unstable' 'Breaks: basic (<= 1)'

buildsimplenativepackage 'common' 'native' '2~conflict' 'unstable-conflict' 'Conflicts: advanced (<= 1)'
buildsimplenativepackage 'common' 'native' '2~break' 'unstable-break' 'Conflicts: advanced (<= 1)'

setupaptarchive

# we check with 'real' packages here as the simulation reports a 'Conf broken'
# which is technical correct for the simulation, but testing errormsg is ugly

testsuccess aptget install basic=1 -y
testdpkginstalled basic
testdpkgnotinstalled common
cp -a rootdir/var/lib/dpkg/status dpkg.status.backup

testloopbreak() {
cp -a dpkg.status.backup rootdir/var/lib/dpkg/status
rm -f rootdir/var/lib/apt/extended_states


testsuccess aptget install advanced=1 -y -t "$1" -o Debug::pkgPackageManager=1
testdpkginstalled advanced
testdpkgnotinstalled basic common

testsuccess aptget dist-upgrade -y -t "$1" -o Debug::pkgPackageManager=1
testdpkginstalled advanced basic common
}

testsuccess aptget dist-upgrade -y
testdpkginstalled basic common
testloopbreak 'unstable-break'
testloopbreak 'unstable-conflict'

+ 4
- 2
test/integration/test-conflicts-loop View File

@@ -20,11 +20,13 @@ Building dependency tree...
The following packages will be upgraded:
openjdk-6-jre openjdk-6-jre-headless openjdk-6-jre-lib
3 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Remv openjdk-6-jre [6b16-1.8-0ubuntu1]
Remv openjdk-6-jre-headless [6b16-1.8-0ubuntu1]
Remv openjdk-6-jre-lib [6b16-1.8-0ubuntu1]
Inst openjdk-6-jre-headless [6b16-1.8-0ubuntu1] (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Inst openjdk-6-jre [6b16-1.8-0ubuntu1] (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Inst openjdk-6-jre-lib [6b16-1.8-0ubuntu1] (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Conf openjdk-6-jre-lib (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Conf openjdk-6-jre (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Inst openjdk-6-jre-headless [6b16-1.8-0ubuntu1] (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])
Conf openjdk-6-jre-headless (6b20-1.9.8-0ubuntu1~10.04.1 unstable [i386])' aptget dist-upgrade -s -o APT::Immediate-Configure-All=true

testsuccess aptget dist-upgrade -s -o Debug::pkgPackageManager=1

+ 51
- 0
test/integration/test-essential-force-loopbreak View File

@@ -0,0 +1,51 @@
#!/bin/sh
set -e

TESTDIR=$(readlink -f $(dirname $0))
. $TESTDIR/framework

setupenvironment
configarchitecture 'amd64'

insertinstalledpackage 'sysvinit' 'amd64' '1' 'Essential: yes'

buildsimplenativepackage 'sysvinit' 'amd64' '2' 'sid' 'Pre-Depends: sysvinit-core | systemd-sysv
Essential: yes'
buildsimplenativepackage 'sysvinit-core' 'amd64' '2' 'sid'

buildsimplenativepackage 'systemd-sysv' 'amd64' '2~conflict' 'sid-conflict' 'Conflicts: sysvinit (<< 2)
Breaks: sysvinit-core'

buildsimplenativepackage 'systemd-sysv' 'amd64' '2~break' 'sid-break' 'Breaks: sysvinit (<< 2), sysvinit-core'

setupaptarchive

cp -a rootdir/var/lib/dpkg/status dpkg.status.backup

testforcebreak() {
cp -a dpkg.status.backup rootdir/var/lib/dpkg/status
rm -f rootdir/var/lib/apt/extended_states
testequal 'Reading package lists...
Building dependency tree...
The following extra packages will be installed:
sysvinit
The following NEW packages will be installed:
systemd-sysv
The following packages will be upgraded:
sysvinit
1 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
E: This installation run will require temporarily removing the essential package sysvinit:amd64 due to a Conflicts/Pre-Depends loop. This is often bad, but if you really want to do it, activate the APT::Force-LoopBreak option.
E: Internal Error, Could not early remove sysvinit:amd64 (2)' aptget install systemd-sysv -t "$1" -s
# ensure that really nothing happens
testfailure aptget install systemd-sysv -y -t "$1" -o Debug::pkgPackageManager=1
testdpkginstalled 'sysvinit'
testdpkgnotinstalled 'systemd-sysv'

# with enough force however …
cp -a dpkg.status.backup rootdir/var/lib/dpkg/status
testsuccess aptget install systemd-sysv -y -t "$1" -o Debug::pkgPackageManager=1 -o APT::Force-LoopBreak=1
testdpkginstalled 'sysvinit' 'systemd-sysv'
}

testforcebreak 'sid-conflict'
testforcebreak 'sid-break'

Loading…
Cancel
Save