#include<config.h>
#include <apt-pkg/depcache.h>
-#include <apt-pkg/version.h>
#include <apt-pkg/versionmatch.h>
#include <apt-pkg/error.h>
#include <apt-pkg/sptr.h>
-#include <apt-pkg/algorithms.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/aptconfiguration.h>
-#include <apt-pkg/pkgsystem.h>
#include <apt-pkg/tagfile.h>
#include <apt-pkg/progress.h>
#include <apt-pkg/cacheset.h>
-
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/cacheiterators.h>
+#include <apt-pkg/macros.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <list>
+#include <string>
+#include <utility>
+#include <vector>
#include <algorithm>
#include <iostream>
-#include <sstream>
#include <set>
#include <sys/stat.h>
}
/*}}}*/
pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) : /*{{{*/
- cache(cache), released(false)
+ d(NULL), cache(cache), released(false)
{
++cache.group_level;
}
// ---------------------------------------------------------------------
/* */
pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) :
- group_level(0), Cache(pCache), PkgState(0), DepState(0)
+ group_level(0), Cache(pCache), PkgState(0), DepState(0),
+ iUsrSize(0), iDownloadSize(0), iInstCount(0), iDelCount(0), iKeepCount(0),
+ iBrokenCount(0), iPolicyBrokenCount(0), iBadCount(0), d(NULL)
{
DebugMarker = _config->FindB("Debug::pkgDepCache::Marker", false);
DebugAutoInstall = _config->FindB("Debug::pkgDepCache::AutoInstall", false);
return true;
}
/*}}}*/
-bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) /*{{{*/
+bool pkgDepCache::writeStateFile(OpProgress * /*prog*/, bool InstalledOnly) /*{{{*/
{
bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false);
FileFd StateFile;
string const state = _config->FindFile("Dir::State::extended_states");
+ if (CreateAPTDirectoryIfNeeded(_config->FindDir("Dir::State"), flNotFile(state)) == false)
+ return false;
// if it does not exist, create a empty one
- if(!RealFileExists(state))
+ if(!RealFileExists(state))
{
StateFile.Open(state, FileFd::WriteAtomic);
StateFile.Close();
return _error->Error(_("Failed to open StateFile %s"),
state.c_str());
- FILE *OutFile;
- string const outfile = state + ".tmp";
- if((OutFile = fopen(outfile.c_str(),"w")) == NULL)
- return _error->Error(_("Failed to write temporary StateFile %s"),
- outfile.c_str());
+ FileFd OutFile(state, FileFd::ReadWrite | FileFd::Atomic);
+ if (OutFile.IsOpen() == false || OutFile.Failed() == true)
+ return _error->Error(_("Failed to write temporary StateFile %s"), state.c_str());
// first merge with the existing sections
pkgTagFile tagfile(&StateFile);
pkgTagSection section;
std::set<string> pkgs_seen;
- const char *nullreorderlist[] = {0};
while(tagfile.Step(section)) {
string const pkgname = section.FindS("Package");
string pkgarch = section.FindS("Architecture");
// Silently ignore unknown packages and packages with no actual
// version.
pkgCache::PkgIterator pkg = Cache->FindPkg(pkgname, pkgarch);
- if(pkg.end() || pkg.VersionList().end())
+ if(pkg.end() || pkg.VersionList().end())
continue;
StateCache const &P = PkgState[pkg->ID];
bool newAuto = (P.Flags & Flag::Auto);
if(_config->FindB("Debug::pkgAutoRemove",false))
std::clog << "Update existing AutoInstall info: "
<< pkg.FullName() << std::endl;
- TFRewriteData rewrite[3];
- rewrite[0].Tag = "Architecture";
- rewrite[0].Rewrite = pkg.Arch();
- rewrite[0].NewTag = 0;
- rewrite[1].Tag = "Auto-Installed";
- rewrite[1].Rewrite = newAuto ? "1" : "0";
- rewrite[1].NewTag = 0;
- rewrite[2].Tag = 0;
- TFRewrite(OutFile, section, nullreorderlist, rewrite);
- fprintf(OutFile,"\n");
+
+ std::vector<pkgTagSection::Tag> rewrite;
+ rewrite.push_back(pkgTagSection::Tag::Rewrite("Architecture", pkg.Arch()));
+ rewrite.push_back(pkgTagSection::Tag::Rewrite("Auto-Installed", newAuto ? "1" : "0"));
+ section.Write(OutFile, NULL, rewrite);
+ if (OutFile.Write("\n", 1) == false)
+ return false;
pkgs_seen.insert(pkg.FullName());
}
-
+
// then write the ones we have not seen yet
- std::ostringstream ostr;
for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); ++pkg) {
StateCache const &P = PkgState[pkg->ID];
if(P.Flags & Flag::Auto) {
continue;
if(debug_autoremove)
std::clog << "Writing new AutoInstall: " << pkg.FullName() << std::endl;
- ostr.str(string(""));
- ostr << "Package: " << pkg.Name()
- << "\nArchitecture: " << pkgarch
- << "\nAuto-Installed: 1\n\n";
- fprintf(OutFile,"%s",ostr.str().c_str());
+ std::string stanza = "Package: ";
+ stanza.append(pkg.Name())
+ .append("\nArchitecture: ").append(pkgarch)
+ .append("\nAuto-Installed: 1\n\n");
+ if (OutFile.Write(stanza.c_str(), stanza.length()) == false)
+ return false;
}
}
- fclose(OutFile);
-
- // move the outfile over the real file and set permissions
- rename(outfile.c_str(), state.c_str());
+ if (OutFile.Close() == false)
+ return false;
chmod(state.c_str(), 0644);
-
return true;
}
/*}}}*/
{
iUsrSize = 0;
iDownloadSize = 0;
- iDelCount = 0;
iInstCount = 0;
+ iDelCount = 0;
iKeepCount = 0;
iBrokenCount = 0;
+ iPolicyBrokenCount = 0;
iBadCount = 0;
// Perform the depends pass
// - this makes sense as default when all Garbage dependencies
// are automatically marked for removal (as aptitude does).
// setting a package for keep then makes it no longer autoinstalled
- // for all other use-case this action is rather suprising
+ // for all other use-case this action is rather surprising
if(FromUser && !P.Marked)
P.Flags &= ~Flag::Auto;
#endif
dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge,
unsigned long Depth, bool FromUser)
+{
+ return IsDeleteOkProtectInstallRequests(Pkg, rPurge, Depth, FromUser);
+}
+bool pkgDepCache::IsDeleteOkProtectInstallRequests(PkgIterator const &Pkg,
+ bool const /*rPurge*/, unsigned long const Depth, bool const FromUser)
{
if (FromUser == false && Pkg->CurrentVer == 0)
{
and prevents mode changes for packages on hold for example.
If you want to check Mode specific stuff you can use the virtual public
Is<Mode>Ok methods instead */
-char const* PrintMode(char const mode)
+static char const* PrintMode(char const mode)
{
switch (mode)
{
case pkgDepCache::ModeInstall: return "Install";
case pkgDepCache::ModeKeep: return "Keep";
case pkgDepCache::ModeDelete: return "Delete";
+ case pkgDepCache::ModeGarbage: return "Garbage";
default: return "UNKNOWN";
}
}
/* */
struct CompareProviders {
pkgCache::PkgIterator const Pkg;
- CompareProviders(pkgCache::DepIterator const &Dep) : Pkg(Dep.TargetPkg()) {};
+ explicit CompareProviders(pkgCache::DepIterator const &Dep) : Pkg(Dep.TargetPkg()) {};
//bool operator() (APT::VersionList::iterator const &AV, APT::VersionList::iterator const &BV)
bool operator() (pkgCache::VerIterator const &AV, pkgCache::VerIterator const &BV)
{
else if ((B->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important)
return true;
}
- // higher priority seems like a good idea
- if (AV->Priority != BV->Priority)
- return AV->Priority > BV->Priority;
// prefer native architecture
if (strcmp(A.Arch(), B.Arch()) != 0)
{
else if (*a == B.Arch())
return true;
}
+ // higher priority seems like a good idea
+ if (AV->Priority != BV->Priority)
+ return AV->Priority > BV->Priority;
// unable to decide…
return A->ID < B->ID;
}
continue;
/* Check if this dep should be consider for install. If it is a user
- defined important dep and we are installed a new package then
+ defined important dep and we are installed a new package then
it will be installed. Otherwise we only check for important
- deps that have changed from the installed version
- */
+ deps that have changed from the installed version */
if (IsImportantDep(Start) == false)
continue;
- /* If we are in an or group locate the first or that can
- succeed. We have already cached this.. */
+ /* If we are in an or group locate the first or that can
+ succeed. We have already cached this… */
for (; Ors > 1 && (DepState[Start->ID] & DepCVer) != DepCVer; --Ors)
++Start;
+
+ /* unsatisfiable dependency: IsInstallOkDependenciesSatisfiableByCandidates
+ would have prevented us to get here if not overridden, so just skip
+ over the problem here as the frontend will know what it is doing */
if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer && Start.IsNegative() == false)
- {
- if(DebugAutoInstall == true)
- std::clog << OutputInDepth(Depth) << Start << " can't be satisfied!" << std::endl;
- if (Start.IsCritical() == false)
- continue;
- // if the dependency was critical, we can't install it, so remove it again
- MarkDelete(Pkg,false,Depth + 1, false);
- return false;
- }
+ continue;
/* Check if any ImportantDep() (but not Critical) were added
* since we installed the package. Also check for deps that
}
}
- /* This bit is for processing the possibilty of an install/upgrade
+ /* This bit is for processing the possibility of an install/upgrade
fixing the problem for "positive" dependencies */
if (Start.IsNegative() == false && (DepState[Start->ID] & DepCVer) == DepCVer)
{
verlist.insert(Cand);
}
CompareProviders comp(Start);
- APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp);
- if (InstVer != verlist.end())
- {
+ do {
+ APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp);
+
+ if (InstVer == verlist.end())
+ break;
+
pkgCache::PkgIterator InstPkg = InstVer.ParentPkg();
if(DebugAutoInstall == true)
std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name()
<< " as " << Start.DepType() << " of " << Pkg.Name()
<< std::endl;
- MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps);
+ if (MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps) == false)
+ {
+ verlist.erase(InstVer);
+ continue;
+ }
// now check if we should consider it a automatic dependency or not
- if(InstPkg->CurrentVer == 0 && Pkg->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section()))
+ if(InstPkg->CurrentVer == 0 && InstVer->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", InstVer.Section()))
{
if(DebugAutoInstall == true)
std::clog << OutputInDepth(Depth) << "Setting NOT as auto-installed (direct "
<< Start.DepType() << " of pkg in APT::Never-MarkAuto-Sections)" << std::endl;
MarkAuto(InstPkg, false);
}
- }
+ break;
+ } while(true);
continue;
}
/* Negative dependencies have no or-group
if (PkgState[Pkg->ID].InstallVer == 0)
continue;
+ /* Ignore negative dependencies that we are not going to
+ get installed */
+ if (PkgState[Pkg->ID].InstallVer != *I)
+ continue;
+
if ((Start->Version != 0 || TrgPkg != Pkg) &&
PkgState[Pkg->ID].CandidateVer != PkgState[Pkg->ID].InstallVer &&
PkgState[Pkg->ID].CandidateVer != *I &&
MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps) == true)
continue;
- else if ((Start->Type == pkgCache::Dep::Conflicts || Start->Type == pkgCache::Dep::DpkgBreaks) &&
- MarkDelete(Pkg,false,Depth + 1, false) == false)
- break;
+ else if (Start->Type == pkgCache::Dep::Conflicts ||
+ Start->Type == pkgCache::Dep::DpkgBreaks)
+ {
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth)
+ << " Removing: " << Pkg.Name()
+ << std::endl;
+ if (MarkDelete(Pkg,false,Depth + 1, false) == false)
+ break;
+ }
}
continue;
}
/*}}}*/
// DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/
// ---------------------------------------------------------------------
-/* The default implementation does nothing.
+/* The default implementation checks if the installation of an M-A:same
+ package would lead us into a version-screw and if so forbids it.
dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsInstallOk(PkgIterator const &Pkg,bool AutoInst,
unsigned long Depth, bool FromUser)
{
+ return IsInstallOkMultiArchSameVersionSynced(Pkg,AutoInst, Depth, FromUser) &&
+ IsInstallOkDependenciesSatisfiableByCandidates(Pkg,AutoInst, Depth, FromUser);
+}
+bool pkgDepCache::IsInstallOkMultiArchSameVersionSynced(PkgIterator const &Pkg,
+ bool const /*AutoInst*/, unsigned long const Depth, bool const FromUser)
+{
+ if (FromUser == true) // as always: user is always right
+ return true;
+
+ // if we have checked before and it was okay, it will still be okay
+ if (PkgState[Pkg->ID].Mode == ModeInstall &&
+ PkgState[Pkg->ID].InstallVer == PkgState[Pkg->ID].CandidateVer)
+ return true;
+
+ // ignore packages with none-M-A:same candidates
+ VerIterator const CandVer = PkgState[Pkg->ID].CandidateVerIter(*this);
+ if (unlikely(CandVer.end() == true) || CandVer == Pkg.CurrentVer() ||
+ (CandVer->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same)
+ return true;
+
+ GrpIterator const Grp = Pkg.Group();
+ for (PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
+ {
+ // not installed or self-check: fine by definition
+ if (P->CurrentVer == 0 || P == Pkg)
+ continue;
+
+ // not having a candidate or being in sync
+ // (simple string-compare as stuff like '1' == '0:1-0' can't happen here)
+ VerIterator CV = PkgState[P->ID].CandidateVerIter(*this);
+ if (CV.end() == true || strcmp(Pkg.CandVersion(), CV.VerStr()) == 0)
+ continue;
+
+ // packages losing M-A:same can be out-of-sync
+ if ((CV->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same)
+ continue;
+
+ // not downloadable means the package is obsolete, so allow out-of-sync
+ if (CV.Downloadable() == false)
+ continue;
+
+ PkgState[Pkg->ID].iFlags |= AutoKept;
+ if (unlikely(DebugMarker == true))
+ std::clog << OutputInDepth(Depth) << "Ignore MarkInstall of " << Pkg
+ << " as it is not in sync with its M-A:same sibling " << P
+ << " (" << Pkg.CandVersion() << " != " << CV.VerStr() << ")" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+bool pkgDepCache::IsInstallOkDependenciesSatisfiableByCandidates(PkgIterator const &Pkg,
+ bool const AutoInst, unsigned long const Depth, bool const /*FromUser*/)
+{
+ if (AutoInst == false)
+ return true;
+
+ VerIterator const CandVer = PkgState[Pkg->ID].CandidateVerIter(*this);
+ if (unlikely(CandVer.end() == true) || CandVer == Pkg.CurrentVer())
+ return true;
+
+ for (DepIterator Dep = CandVer.DependsList(); Dep.end() != true;)
+ {
+ // Grok or groups
+ DepIterator Start = Dep;
+ bool Result = true;
+ unsigned Ors = 0;
+ for (bool LastOR = true; Dep.end() == false && LastOR == true; ++Dep, ++Ors)
+ {
+ LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or;
+
+ if ((DepState[Dep->ID] & DepInstall) == DepInstall)
+ Result = false;
+ }
+
+ if (Start.IsCritical() == false || Start.IsNegative() == true || Result == false)
+ continue;
+
+ /* If we are in an or group locate the first or that can succeed.
+ We have already cached this… */
+ for (; Ors > 1 && (DepState[Start->ID] & DepCVer) != DepCVer; --Ors)
+ ++Start;
+
+ if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer)
+ {
+ if (DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << Start << " can't be satisfied!" << std::endl;
+
+ // the dependency is critical, but can't be installed, so discard the candidate
+ // as the problemresolver will trip over it otherwise trying to install it (#735967)
+ if (Pkg->CurrentVer != 0 && (PkgState[Pkg->ID].iFlags & Protected) != Protected)
+ SetCandidateVersion(Pkg.CurrentVer());
+ return false;
+ }
+ }
+
return true;
}
/*}}}*/
if (itsFine == false)
{
// change the candidate
- Changed.push_back(make_pair(oldCand, TargetVer));
+ Changed.push_back(make_pair(V, TargetVer));
if (SetCandidateRelease(V, TargetRel, Changed) == false)
{
if (stillOr == false)
{
/* Not source/not automatic versions cannot be a candidate version
unless they are already installed */
- VerIterator Last(*(pkgCache *)this,0);
+ VerIterator Last;
for (VerIterator I = Pkg.VersionList(); I.end() == false; ++I)
{
for (VerFileIterator J = I.FileList(); J.end() == false; ++J)
{
- if ((J.File()->Flags & Flag::NotSource) != 0)
+ if (J.File().Flagged(Flag::NotSource))
continue;
/* Stash the highest version of a not-automatic source, we use it
if there is nothing better */
- if ((J.File()->Flags & Flag::NotAutomatic) != 0 ||
- (J.File()->Flags & Flag::ButAutomaticUpgrades) != 0)
+ if (J.File().Flagged(Flag::NotAutomatic) ||
+ J.File().Flagged(Flag::ButAutomaticUpgrades))
{
if (Last.end() == true)
Last = I;
}
/*}}}*/
// Policy::GetPriority - Get the priority of the package pin /*{{{*/
-signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgIterator const &Pkg)
-{ return 0; };
-signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgFileIterator const &File)
-{ return 0; };
+APT_CONST signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgIterator const &/*Pkg*/)
+{ return 0; }
+APT_CONST signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgFileIterator const &/*File*/)
+{ return 0; }
/*}}}*/
pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() /*{{{*/
{
follow_recommends = MarkFollowsRecommends();
follow_suggests = MarkFollowsSuggests();
-
-
// do the mark part, this is the core bit of the algorithm
for(PkgIterator p = PkgBegin(); !p.end(); ++p)
{
// be nice even then a required package violates the policy (#583517)
// and do the full mark process also for required packages
(p.CurrentVer().end() != true &&
- p.CurrentVer()->Priority == pkgCache::State::Required))
+ p.CurrentVer()->Priority == pkgCache::State::Required) ||
+ // packages which can't be changed (like holds) can't be garbage
+ (IsModeChangeOk(ModeGarbage, p, 0, false) == false))
{
// the package is installed (and set to keep)
if(PkgState[p->ID].Keep() && !p.CurrentVer().end())
return true;
}
/*}}}*/
+// DepCache::MarkAndSweep /*{{{*/
+bool pkgDepCache::MarkAndSweep(InRootSetFunc &rootFunc)
+{
+ return MarkRequired(rootFunc) && Sweep();
+}
+bool pkgDepCache::MarkAndSweep()
+{
+ std::auto_ptr<InRootSetFunc> f(GetRootSetFunc());
+ if(f.get() != NULL)
+ return MarkAndSweep(*f.get());
+ else
+ return false;
+}
+ /*}}}*/