]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/depcache.cc
make all d-pointer * const pointers
[apt.git] / apt-pkg / depcache.cc
index 5ca0c2ea5cb957f2ef98bb263198604767f0a572..d01c142239b112cd569640d0fd0a6dd95631d459 100644 (file)
 #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>
@@ -58,7 +63,7 @@ ConfigValueInSubTree(const char* SubTree, const char *needle)
 }
                                                                        /*}}}*/
 pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) :            /*{{{*/
-  cache(cache), released(false)
+  d(NULL), cache(cache), released(false)
 {
   ++cache.group_level;
 }
@@ -90,7 +95,9 @@ pkgDepCache::ActionGroup::~ActionGroup()
 // ---------------------------------------------------------------------
 /* */
 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);
@@ -222,7 +229,7 @@ bool pkgDepCache::readStateFile(OpProgress *Prog)                   /*{{{*/
    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);
    
@@ -231,9 +238,11 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly)     /*{{{*/
 
    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();
@@ -244,17 +253,14 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly)    /*{{{*/
       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");
@@ -263,7 +269,7 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly)      /*{{{*/
         // 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);
@@ -284,21 +290,17 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly)    /*{{{*/
         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) {
@@ -317,19 +319,17 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly)    /*{{{*/
            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;
 }
                                                                        /*}}}*/
@@ -657,10 +657,11 @@ void pkgDepCache::Update(OpProgress *Prog)
 {   
    iUsrSize = 0;
    iDownloadSize = 0;
-   iDelCount = 0;
    iInstCount = 0;
+   iDelCount = 0;
    iKeepCount = 0;
    iBrokenCount = 0;
+   iPolicyBrokenCount = 0;
    iBadCount = 0;
 
    // Perform the depends pass
@@ -789,7 +790,7 @@ bool pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser,
       // - 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
@@ -864,6 +865,11 @@ bool pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge,
    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)
    {
@@ -884,13 +890,14 @@ bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge,
    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";
         }
 }
@@ -942,7 +949,7 @@ bool pkgDepCache::IsModeChangeOk(ModeList const mode, PkgIterator const &Pkg,
 /* */
 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)
    {
@@ -1002,9 +1009,6 @@ struct CompareProviders {
         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)
       {
@@ -1019,6 +1023,9 @@ struct CompareProviders {
            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;
    }
@@ -1110,27 +1117,22 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
         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
@@ -1183,7 +1185,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
         }
       }
 
-      /* 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)
       {
@@ -1200,25 +1202,33 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
            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
@@ -1238,14 +1248,26 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
            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;
       }
@@ -1256,11 +1278,108 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
                                                                        /*}}}*/
 // 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;
 }
                                                                        /*}}}*/
@@ -1461,7 +1580,7 @@ bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer,
         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)
@@ -1559,7 +1678,7 @@ pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator const &Pk
 {
    /* 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)
    {
@@ -1568,13 +1687,13 @@ pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator const &Pk
       
       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;
@@ -1614,10 +1733,10 @@ bool pkgDepCache::Policy::IsImportantDep(DepIterator const &Dep)
 }
                                                                        /*}}}*/
 // 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()              /*{{{*/
 {
@@ -1666,8 +1785,6 @@ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
    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)
    {
@@ -1678,7 +1795,9 @@ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
          // 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())
@@ -1841,3 +1960,17 @@ bool pkgDepCache::Sweep()                                                /*{{{*/
    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;
+}
+                                                                       /*}}}*/