X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/c8a4ce6cbed57ae108dc955d4a850f9b129a0693..9fe917c7ea24a626c97fcd9cda39743440a4cbfc:/apt-pkg/policy.cc diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index 6da6ed606..3dd6ddac4 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -22,10 +22,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -44,15 +44,19 @@ using namespace std; // --------------------------------------------------------------------- /* Set the defaults for operation. The default mode with no loaded policy file matches the V0 policy engine. */ -pkgPolicy::pkgPolicy(pkgCache *Owner) : Pins(0), PFPriority(0), Cache(Owner) +pkgPolicy::pkgPolicy(pkgCache *Owner) : Pins(nullptr), VerPins(nullptr), + PFPriority(nullptr), Cache(Owner), d(NULL) { if (Owner == 0) return; PFPriority = new signed short[Owner->Head().PackageFileCount]; Pins = new Pin[Owner->Head().PackageCount]; + VerPins = new Pin[Owner->Head().VersionCount]; for (unsigned long I = 0; I != Owner->Head().PackageCount; I++) Pins[I].Type = pkgVersionMatch::None; + for (unsigned long I = 0; I != Owner->Head().VersionCount; I++) + VerPins[I].Type = pkgVersionMatch::None; // The config file has a master override. string DefRel = _config->Find("APT::Default-Release"); @@ -95,8 +99,8 @@ bool pkgPolicy::InitDefaults() } // Apply the defaults.. - SPtrArray Fixed = new bool[Cache->HeaderP->PackageFileCount]; - memset(Fixed,0,sizeof(*Fixed)*Cache->HeaderP->PackageFileCount); + std::unique_ptr Fixed(new bool[Cache->HeaderP->PackageFileCount]); + memset(Fixed.get(),0,sizeof(Fixed[0])*Cache->HeaderP->PackageFileCount); StatusOverride = false; for (vector::const_iterator I = Defaults.begin(); I != Defaults.end(); ++I) { @@ -128,91 +132,27 @@ bool pkgPolicy::InitDefaults() best package is. */ pkgCache::VerIterator pkgPolicy::GetCandidateVer(pkgCache::PkgIterator const &Pkg) { - // Look for a package pin and evaluate it. - signed Max = GetPriority(Pkg); - pkgCache::VerIterator Pref = GetMatch(Pkg); - - // Alternatives in case we can not find our package pin (Bug#512318). - signed MaxAlt = 0; - pkgCache::VerIterator PrefAlt; - - // no package = no candidate version - if (Pkg.end() == true) - return Pref; - - // packages with a pin lower than 0 have no newer candidate than the current version - if (Max < 0) - return Pkg.CurrentVer(); - - /* Falling through to the default version.. Setting Max to zero - effectively excludes everything <= 0 which are the non-automatic - priorities.. The status file is given a prio of 100 which will exclude - not-automatic sources, except in a single shot not-installed mode. - - The user pin is subject to the same priority rules as default - selections. Thus there are two ways to create a pin - a pin that - tracks the default when the default is taken away, and a permanent - pin that stays at that setting. - */ - bool PrefSeen = false; - for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver) - { - /* Lets see if this version is the installed version */ - bool instVer = (Pkg.CurrentVer() == Ver); + pkgCache::VerIterator cand; + pkgCache::VerIterator cur = Pkg.CurrentVer(); + int candPriority = -1; + pkgVersioningSystem *vs = Cache->VS; - if (Pref == Ver) - PrefSeen = true; + for (pkgCache::VerIterator ver = Pkg.VersionList(); ver.end() == false; ++ver) { + int priority = GetPriority(ver, true); - for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF) - { - /* If this is the status file, and the current version is not the - version in the status file (ie it is not installed, or somesuch) - then it is not a candidate for installation, ever. This weeds - out bogus entries that may be due to config-file states, or - other. */ - if (VF.File().Flagged(pkgCache::Flag::NotSource) && instVer == false) - continue; - - signed Prio = PFPriority[VF.File()->ID]; - if (Prio > Max) - { - Pref = Ver; - Max = Prio; - PrefSeen = true; - } - if (Prio > MaxAlt) - { - PrefAlt = Ver; - MaxAlt = Prio; - } - } + if (priority == 0 || priority <= candPriority) + continue; - if (instVer == true && Max < 1000) - { - /* Not having seen the Pref yet means we have a specific pin below 1000 - on a version below the current installed one, so ignore the specific pin - as this would be a downgrade otherwise */ - if (PrefSeen == false || Pref.end() == true) - { - Pref = Ver; - PrefSeen = true; - } - /* Elevate our current selection (or the status file itself) so that only - a downgrade can override it from now on */ - Max = 999; + // TODO: Maybe optimize to not compare versions + if (!cur.end() && priority < 1000 + && (vs->CmpVersion(ver.VerStr(), cur.VerStr()) < 0)) + continue; - // Fast path optimize. - if (StatusOverride == false) - break; - } + candPriority = priority; + cand = ver; } - // If we do not find our candidate, use the one with the highest pin. - // This means that if there is a version available with pin > 0; there - // will always be a candidate (Closes: #512318) - if (!Pref.IsGood() && MaxAlt > 0) - Pref = PrefAlt; - return Pref; + return cand; } /*}}}*/ // Policy::CreatePin - Create an entry in the pin table.. /*{{{*/ @@ -240,14 +180,13 @@ void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name, Name.erase(found); } - // Allow pinning by wildcards - // TODO: Maybe we should always prefer specific pins over non- - // specific ones. - if (Name[0] == '/' || Name.find_first_of("*[?") != string::npos) + // Allow pinning by wildcards - beware of package names looking like wildcards! + // TODO: Maybe we should always prefer specific pins over non-specific ones. + if ((Name[0] == '/' && Name[Name.length() - 1] == '/') || Name.find_first_of("*[?") != string::npos) { pkgVersionMatch match(Data, Type); for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() != true; ++G) - if (match.ExpressionMatches(Name, G.Name())) + if (Name != G.Name() && match.ExpressionMatches(Name, G.Name())) { if (Arch.empty() == false) CreatePin(Type, string(G.Name()).append(":").append(Arch), Data, Priority); @@ -281,6 +220,17 @@ void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name, P->Priority = Priority; P->Data = Data; matched = true; + + // Find matching version(s) and copy the pin into it + pkgVersionMatch Match(P->Data,P->Type); + for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() != true; ++Ver) + { + if (Match.VersionMatches(Ver)) { + Pin *VP = VerPins + Ver->ID; + if (VP->Type == pkgVersionMatch::None) + *VP = *P; + } + } } } @@ -318,26 +268,35 @@ APT_PURE signed short pkgPolicy::GetPriority(pkgCache::PkgIterator const &Pkg) return Pins[Pkg->ID].Priority; return 0; } +APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, bool ConsiderFiles) +{ + if (VerPins[Ver->ID].Type != pkgVersionMatch::None) + return VerPins[Ver->ID].Priority; + if (!ConsiderFiles) + return 0; + + // priorities are short ints, but we want to pick a value outside the valid range here + auto priority = std::numeric_limits::min(); + for (pkgCache::VerFileIterator file = Ver.FileList(); file.end() == false; file++) + { + /* If this is the status file, and the current version is not the + version in the status file (ie it is not installed, or somesuch) + then it is not a candidate for installation, ever. This weeds + out bogus entries that may be due to config-file states, or + other. */ + if (file.File().Flagged(pkgCache::Flag::NotSource) && Ver.ParentPkg().CurrentVer() != Ver) + priority = std::max(priority, static_cast(-1)); + else + priority = std::max(priority, static_cast(GetPriority(file.File()))); + } + + return priority == std::numeric_limits::min() ? 0 : priority; +} APT_PURE signed short pkgPolicy::GetPriority(pkgCache::PkgFileIterator const &File) { return PFPriority[File->ID]; } /*}}}*/ -// PreferenceSection class - Overriding the default TrimRecord method /*{{{*/ -// --------------------------------------------------------------------- -/* The preference file is a user generated file so the parser should - therefore be a bit more friendly by allowing comments and new lines - all over the place rather than forcing a special format */ -class PreferenceSection : public pkgTagSection -{ - void TrimRecord(bool /*BeforeRecord*/, const char* &End) - { - for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++) - if (Stop[0] == '#') - Stop = (const char*) memchr(Stop,'\n',End-Stop); - } -}; - /*}}}*/ // ReadPinDir - Load the pin files from this dir into a Policy /*{{{*/ // --------------------------------------------------------------------- /* This will load each pin file in the given dir into a Policy. If the @@ -347,15 +306,21 @@ class PreferenceSection : public pkgTagSection bool ReadPinDir(pkgPolicy &Plcy,string Dir) { if (Dir.empty() == true) - Dir = _config->FindDir("Dir::Etc::PreferencesParts"); + Dir = _config->FindDir("Dir::Etc::PreferencesParts", "/dev/null"); if (DirectoryExists(Dir) == false) { - _error->WarningE("DirectoryExists",_("Unable to read %s"),Dir.c_str()); + if (APT::String::Endswith(Dir, "/dev/null") == false) + _error->WarningE("DirectoryExists",_("Unable to read %s"),Dir.c_str()); return true; } + _error->PushToStack(); vector const List = GetListOfFilesInDir(Dir, "pref", true, true); + bool const PendingErrors = _error->PendingError(); + _error->MergeWithStack(); + if (PendingErrors) + return false; // Read the files for (vector::const_iterator I = List.begin(); I != List.end(); ++I) @@ -379,11 +344,11 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) return true; FileFd Fd(File,FileFd::ReadOnly); - pkgTagFile TF(&Fd); - if (_error->PendingError() == true) + pkgTagFile TF(&Fd, pkgTagFile::SUPPORT_COMMENTS); + if (Fd.IsOpen() == false || Fd.Failed()) return false; - - PreferenceSection Tags; + + pkgTagSection Tags; while (TF.Step(Tags) == true) { // can happen when there are only comments in a record @@ -419,11 +384,21 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) } for (; Word != End && isspace(*Word) != 0; Word++); - short int priority = Tags.FindI("Pin-Priority", 0); + _error->PushToStack(); + int const priority = Tags.FindI("Pin-Priority", 0); + bool const newError = _error->PendingError(); + _error->MergeWithStack(); + if (priority < std::numeric_limits::min() || + priority > std::numeric_limits::max() || + newError) { + return _error->Error(_("%s: Value %s is outside the range of valid pin priorities (%d to %d)"), + File.c_str(), Tags.FindS("Pin-Priority").c_str(), + std::numeric_limits::min(), + std::numeric_limits::max()); + } if (priority == 0) { - _error->Warning(_("No priority (or zero) specified for pin")); - continue; + return _error->Error(_("No priority (or zero) specified for pin")); } istringstream s(Name); @@ -440,4 +415,4 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) } /*}}}*/ -pkgPolicy::~pkgPolicy() {delete [] PFPriority; delete [] Pins;} +pkgPolicy::~pkgPolicy() {delete [] PFPriority; delete [] Pins; delete [] VerPins; }