X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/204fbdcce67a08d33a5e7eb3ca9e7a9ae80918c5..3bbcf3b90405b25fc3a53dad0a6afdf341de528c:/apt-pkg/pkgcachegen.cc diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc index 11d539dff..0e6ae698d 100644 --- a/apt-pkg/pkgcachegen.cc +++ b/apt-pkg/pkgcachegen.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: pkgcachegen.cc,v 1.38 1999/05/23 22:55:54 jgg Exp $ +// $Id: pkgcachegen.cc,v 1.53.2.1 2003/12/24 23:09:17 mdz Exp $ /* ###################################################################### Package Cache Generator - Generator for the cache structure. @@ -10,9 +10,7 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/pkgcachegen.h" -#endif +#define APT_COMPATIBILITY 986 #include #include @@ -20,33 +18,72 @@ #include #include #include -#include +#include #include -#include +#include +#include +#include + +#include + +#include + +#include #include #include #include +#include /*}}}*/ +typedef vector::iterator FileIterator; +template std::vector pkgCacheGenerator::Dynamic::toReMap; // CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/ // --------------------------------------------------------------------- -/* We set the diry flag and make sure that is written to the disk */ -pkgCacheGenerator::pkgCacheGenerator(DynamicMMap &Map,OpProgress &Prog) : - Map(Map), Cache(Map), Progress(Prog) +/* We set the dirty flag and make sure that is written to the disk */ +pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) : + Map(*pMap), Cache(pMap,false), Progress(Prog), + FoundFileDeps(0) { + CurrentFile = 0; + memset(UniqHash,0,sizeof(UniqHash)); + if (_error->PendingError() == true) return; - + if (Map.Size() == 0) { - Map.RawAllocate(sizeof(pkgCache::Header)); + // Setup the map interface.. + Cache.HeaderP = (pkgCache::Header *)Map.Data(); + if (Map.RawAllocate(sizeof(pkgCache::Header)) == 0 && _error->PendingError() == true) + return; + + Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0])); + + // Starting header *Cache.HeaderP = pkgCache::Header(); + map_ptrloc const idxVerSysName = WriteStringInMap(_system->VS->Label); + Cache.HeaderP->VerSysName = idxVerSysName; + map_ptrloc const idxArchitecture = WriteStringInMap(_config->Find("APT::Architecture")); + Cache.HeaderP->Architecture = idxArchitecture; + if (unlikely(idxVerSysName == 0 || idxArchitecture == 0)) + return; + Cache.ReMap(); + } + else + { + // Map directly from the existing file + Cache.ReMap(); + Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0])); + if (Cache.VS != _system->VS) + { + _error->Error(_("Cache has an incompatible versioning system")); + return; + } } + Cache.HeaderP->Dirty = true; Map.Sync(0,sizeof(pkgCache::Header)); - Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0])); - memset(UniqHash,0,sizeof(UniqHash)); } /*}}}*/ // CacheGenerator::~pkgCacheGenerator - Destructor /*{{{*/ @@ -61,31 +98,100 @@ pkgCacheGenerator::~pkgCacheGenerator() return; Cache.HeaderP->Dirty = false; + Cache.HeaderP->CacheFileSize = Map.Size(); Map.Sync(0,sizeof(pkgCache::Header)); } /*}}}*/ +void pkgCacheGenerator::ReMap(void const * const oldMap, void const * const newMap) {/*{{{*/ + if (oldMap == newMap) + return; + + if (_config->FindB("Debug::pkgCacheGen", false)) + std::clog << "Remaping from " << oldMap << " to " << newMap << std::endl; + + Cache.ReMap(false); + + CurrentFile += (pkgCache::PackageFile*) newMap - (pkgCache::PackageFile*) oldMap; + + for (size_t i = 0; i < _count(UniqHash); ++i) + if (UniqHash[i] != 0) + UniqHash[i] += (pkgCache::StringItem*) newMap - (pkgCache::StringItem*) oldMap; + + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); + for (std::vector::const_iterator i = Dynamic::toReMap.begin(); + i != Dynamic::toReMap.end(); ++i) + (*i)->ReMap(oldMap, newMap); +} /*}}}*/ +// CacheGenerator::WriteStringInMap /*{{{*/ +map_ptrloc pkgCacheGenerator::WriteStringInMap(const char *String, + const unsigned long &Len) { + void const * const oldMap = Map.Data(); + map_ptrloc const index = Map.WriteString(String, Len); + if (index != 0) + ReMap(oldMap, Map.Data()); + return index; +} + /*}}}*/ +// CacheGenerator::WriteStringInMap /*{{{*/ +map_ptrloc pkgCacheGenerator::WriteStringInMap(const char *String) { + void const * const oldMap = Map.Data(); + map_ptrloc const index = Map.WriteString(String); + if (index != 0) + ReMap(oldMap, Map.Data()); + return index; +} + /*}}}*/ +map_ptrloc pkgCacheGenerator::AllocateInMap(const unsigned long &size) {/*{{{*/ + void const * const oldMap = Map.Data(); + map_ptrloc const index = Map.Allocate(size); + if (index != 0) + ReMap(oldMap, Map.Data()); + return index; +} + /*}}}*/ // CacheGenerator::MergeList - Merge the package list /*{{{*/ // --------------------------------------------------------------------- /* This provides the generation of the entries in the cache. Each loop goes through a single package record from the underlying parse engine. */ -bool pkgCacheGenerator::MergeList(ListParser &List) +bool pkgCacheGenerator::MergeList(ListParser &List, + pkgCache::VerIterator *OutVer) { List.Owner = this; unsigned int Counter = 0; while (List.Step() == true) { - // Get a pointer to the package structure - string PackageName = List.Package(); + string const PackageName = List.Package(); if (PackageName.empty() == true) return false; - + + string const Arch = List.Architecture(); + + // Get a pointer to the package structure pkgCache::PkgIterator Pkg; - if (NewPackage(Pkg,PackageName) == false) - return _error->Error("Error occured while processing %s (NewPackage)",PackageName.c_str()); + Dynamic DynPkg(Pkg); + if (NewPackage(Pkg, PackageName, Arch) == false) + return _error->Error(_("Error occurred while processing %s (NewPackage)"),PackageName.c_str()); Counter++; - if (Counter % 100 == 0) - Progress.Progress(List.Offset()); + if (Counter % 100 == 0 && Progress != 0) + Progress->Progress(List.Offset()); /* Get a pointer to the version structure. We know the list is sorted so we use that fact in the search. Insertion of new versions is @@ -93,92 +199,297 @@ bool pkgCacheGenerator::MergeList(ListParser &List) string Version = List.Version(); if (Version.empty() == true) { - if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false) - return _error->Error("Error occured while processing %s (UsePackage1)",PackageName.c_str()); + // we first process the package, then the descriptions + // (this has the bonus that we get MMap error when we run out + // of MMap space) + pkgCache::VerIterator Ver(Cache); + Dynamic DynVer(Ver); + if (List.UsePackage(Pkg, Ver) == false) + return _error->Error(_("Error occurred while processing %s (UsePackage1)"), + PackageName.c_str()); + + // Find the right version to write the description + MD5SumValue CurMd5 = List.Description_md5(); + Ver = Pkg.VersionList(); + + for (; Ver.end() == false; ++Ver) + { + pkgCache::DescIterator Desc = Ver.DescriptionList(); + Dynamic DynDesc(Desc); + map_ptrloc *LastDesc = &Ver->DescriptionList; + bool duplicate=false; + + // don't add a new description if we have one for the given + // md5 && language + for ( ; Desc.end() == false; Desc++) + if (MD5SumValue(Desc.md5()) == CurMd5 && + Desc.LanguageCode() == List.DescriptionLanguage()) + duplicate=true; + if(duplicate) + continue; + + for (Desc = Ver.DescriptionList(); + Desc.end() == false; + LastDesc = &Desc->NextDesc, Desc++) + { + if (MD5SumValue(Desc.md5()) == CurMd5) + { + // Add new description + void const * const oldMap = Map.Data(); + map_ptrloc const descindex = NewDescription(Desc, List.DescriptionLanguage(), CurMd5, *LastDesc); + if (oldMap != Map.Data()) + LastDesc += (map_ptrloc*) Map.Data() - (map_ptrloc*) oldMap; + *LastDesc = descindex; + Desc->ParentPkg = Pkg.Index(); + + if ((*LastDesc == 0 && _error->PendingError()) || NewFileDesc(Desc,List) == false) + return _error->Error(_("Error occurred while processing %s (NewFileDesc1)"),PackageName.c_str()); + break; + } + } + } + continue; } pkgCache::VerIterator Ver = Pkg.VersionList(); - __apt_ptrloc *Last = &Pkg->VersionList; + Dynamic DynVer(Ver); + map_ptrloc *LastVer = &Pkg->VersionList; + void const * oldMap = Map.Data(); int Res = 1; - for (; Ver.end() == false; Last = &Ver->NextVer, Ver++) + unsigned long const Hash = List.VersionHash(); + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) { - Res = pkgVersionCompare(Version.begin(),Version.end(),Ver.VerStr(), - Ver.VerStr() + strlen(Ver.VerStr())); - if (Res >= 0) + Res = Cache.VS->CmpVersion(Version,Ver.VerStr()); + // Version is higher as current version - insert here + if (Res > 0) break; + // Versionstrings are equal - is hash also equal? + if (Res == 0 && Ver->Hash == Hash) + break; + // proceed with the next till we have either the right + // or we found another version (which will be lower) } - - /* We already have a version for this item, record that we - saw it */ - unsigned long Hash = List.VersionHash(); - if (Res == 0 && Ver->Hash == Hash) + + /* We already have a version for this item, record that we saw it */ + if (Res == 0 && Ver.end() == false && Ver->Hash == Hash) { if (List.UsePackage(Pkg,Ver) == false) - return _error->Error("Error occured while processing %s (UsePackage2)",PackageName.c_str()); - + return _error->Error(_("Error occurred while processing %s (UsePackage2)"), + PackageName.c_str()); + if (NewFileVer(Ver,List) == false) - return _error->Error("Error occured while processing %s (NewFileVer1)",PackageName.c_str()); + return _error->Error(_("Error occurred while processing %s (NewFileVer1)"), + PackageName.c_str()); - continue; - } - - // Skip to the end of the same version set. - if (Res == 0) - { - for (; Ver.end() == false; Last = &Ver->NextVer, Ver++) + // Read only a single record and return + if (OutVer != 0) { - Res = pkgVersionCompare(Version.begin(),Version.end(),Ver.VerStr(), - Ver.VerStr() + strlen(Ver.VerStr())); - if (Res != 0) - break; + *OutVer = Ver; + FoundFileDeps |= List.HasFileDeps(); + return true; } + + continue; } // Add a new version - *Last = NewVersion(Ver,Version,*Last); + map_ptrloc const verindex = NewVersion(Ver,Version,*LastVer); + if (verindex == 0 && _error->PendingError()) + return _error->Error(_("Error occurred while processing %s (NewVersion%d)"), + PackageName.c_str(), 1); + + if (oldMap != Map.Data()) + LastVer += (map_ptrloc*) Map.Data() - (map_ptrloc*) oldMap; + *LastVer = verindex; Ver->ParentPkg = Pkg.Index(); Ver->Hash = Hash; + if (List.NewVersion(Ver) == false) - return _error->Error("Error occured while processing %s (NewVersion1)",PackageName.c_str()); + return _error->Error(_("Error occurred while processing %s (NewVersion%d)"), + PackageName.c_str(), 2); if (List.UsePackage(Pkg,Ver) == false) - return _error->Error("Error occured while processing %s (UsePackage3)",PackageName.c_str()); + return _error->Error(_("Error occurred while processing %s (UsePackage3)"), + PackageName.c_str()); if (NewFileVer(Ver,List) == false) - return _error->Error("Error occured while processing %s (NewVersion2)",PackageName.c_str()); + return _error->Error(_("Error occurred while processing %s (NewVersion%d)"), + PackageName.c_str(), 3); + + // Read only a single record and return + if (OutVer != 0) + { + *OutVer = Ver; + FoundFileDeps |= List.HasFileDeps(); + return true; + } + + /* Record the Description data. Description data always exist in + Packages and Translation-* files. */ + pkgCache::DescIterator Desc = Ver.DescriptionList(); + Dynamic DynDesc(Desc); + map_ptrloc *LastDesc = &Ver->DescriptionList; + + // Skip to the end of description set + for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++); + + // Add new description + oldMap = Map.Data(); + map_ptrloc const descindex = NewDescription(Desc, List.DescriptionLanguage(), List.Description_md5(), *LastDesc); + if (oldMap != Map.Data()) + LastDesc += (map_ptrloc*) Map.Data() - (map_ptrloc*) oldMap; + *LastDesc = descindex; + Desc->ParentPkg = Pkg.Index(); + + if ((*LastDesc == 0 && _error->PendingError()) || NewFileDesc(Desc,List) == false) + return _error->Error(_("Error occurred while processing %s (NewFileDesc2)"),PackageName.c_str()); } + FoundFileDeps |= List.HasFileDeps(); + + if (Cache.HeaderP->PackageCount >= (1ULL<ID)*8)-1) + return _error->Error(_("Wow, you exceeded the number of package " + "names this APT is capable of.")); + if (Cache.HeaderP->VersionCount >= (1ULL<<(sizeof(Cache.VerP->ID)*8))-1) + return _error->Error(_("Wow, you exceeded the number of versions " + "this APT is capable of.")); + if (Cache.HeaderP->DescriptionCount >= (1ULL<<(sizeof(Cache.DescP->ID)*8))-1) + return _error->Error(_("Wow, you exceeded the number of descriptions " + "this APT is capable of.")); + if (Cache.HeaderP->DependsCount >= (1ULL<<(sizeof(Cache.DepP->ID)*8))-1ULL) + return _error->Error(_("Wow, you exceeded the number of dependencies " + "this APT is capable of.")); return true; } /*}}}*/ -// CacheGenerator::NewPackage - Add a new package /*{{{*/ +// CacheGenerator::MergeFileProvides - Merge file provides /*{{{*/ // --------------------------------------------------------------------- -/* This creates a new package structure and adds it to the hash table */ -bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,string Name) +/* If we found any file depends while parsing the main list we need to + resolve them. Since it is undesired to load the entire list of files + into the cache as virtual packages we do a two stage effort. MergeList + identifies the file depends and this creates Provdies for them by + re-parsing all the indexs. */ +bool pkgCacheGenerator::MergeFileProvides(ListParser &List) { - Pkg = Cache.FindPkg(Name); - if (Pkg.end() == false) + List.Owner = this; + + unsigned int Counter = 0; + while (List.Step() == true) + { + string PackageName = List.Package(); + if (PackageName.empty() == true) + return false; + string Version = List.Version(); + if (Version.empty() == true) + continue; + + pkgCache::PkgIterator Pkg = Cache.FindPkg(PackageName); + Dynamic DynPkg(Pkg); + if (Pkg.end() == true) + return _error->Error(_("Error occurred while processing %s (FindPkg)"), + PackageName.c_str()); + Counter++; + if (Counter % 100 == 0 && Progress != 0) + Progress->Progress(List.Offset()); + + unsigned long Hash = List.VersionHash(); + pkgCache::VerIterator Ver = Pkg.VersionList(); + Dynamic DynVer(Ver); + for (; Ver.end() == false; Ver++) + { + if (Ver->Hash == Hash && Version.c_str() == Ver.VerStr()) + { + if (List.CollectFileProvides(Cache,Ver) == false) + return _error->Error(_("Error occurred while processing %s (CollectFileProvides)"),PackageName.c_str()); + break; + } + } + + if (Ver.end() == true) + _error->Warning(_("Package %s %s was not found while processing file dependencies"),PackageName.c_str(),Version.c_str()); + } + + return true; +} + /*}}}*/ +// CacheGenerator::NewGroup - Add a new group /*{{{*/ +// --------------------------------------------------------------------- +/* This creates a new group structure and adds it to the hash table */ +bool pkgCacheGenerator::NewGroup(pkgCache::GrpIterator &Grp, const string &Name) +{ + Grp = Cache.FindGrp(Name); + if (Grp.end() == false) return true; - + // Get a structure - unsigned long Package = Map.Allocate(sizeof(pkgCache::Package)); - if (Package == 0) + map_ptrloc const Group = AllocateInMap(sizeof(pkgCache::Group)); + if (unlikely(Group == 0)) return false; - - Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package); - + + Grp = pkgCache::GrpIterator(Cache, Cache.GrpP + Group); + map_ptrloc const idxName = WriteStringInMap(Name); + if (unlikely(idxName == 0)) + return false; + Grp->Name = idxName; + // Insert it into the hash table - unsigned long Hash = Cache.Hash(Name); - Pkg->NextPackage = Cache.HeaderP->HashTable[Hash]; - Cache.HeaderP->HashTable[Hash] = Package; - - // Set the name and the ID - Pkg->Name = Map.WriteString(Name); - if (Pkg->Name == 0) + unsigned long const Hash = Cache.Hash(Name); + Grp->Next = Cache.HeaderP->GrpHashTable[Hash]; + Cache.HeaderP->GrpHashTable[Hash] = Group; + + Grp->ID = Cache.HeaderP->GroupCount++; + return true; +} + /*}}}*/ +// CacheGenerator::NewPackage - Add a new package /*{{{*/ +// --------------------------------------------------------------------- +/* This creates a new package structure and adds it to the hash table */ +bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,const string &Name, + const string &Arch) { + pkgCache::GrpIterator Grp; + Dynamic DynGrp(Grp); + if (unlikely(NewGroup(Grp, Name) == false)) + return false; + + Pkg = Grp.FindPkg(Arch); + if (Pkg.end() == false) + return true; + + // Get a structure + map_ptrloc const Package = AllocateInMap(sizeof(pkgCache::Package)); + if (unlikely(Package == 0)) return false; + Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package); + + // Insert the package into our package list + if (Grp->FirstPackage == 0) // the group is new + { + // Insert it into the hash table + unsigned long const Hash = Cache.Hash(Name); + Pkg->NextPackage = Cache.HeaderP->PkgHashTable[Hash]; + Cache.HeaderP->PkgHashTable[Hash] = Package; + Grp->FirstPackage = Package; + } + else // Group the Packages together + { + // this package is the new last package + pkgCache::PkgIterator LastPkg(Cache, Cache.PkgP + Grp->LastPackage); + Pkg->NextPackage = LastPkg->NextPackage; + LastPkg->NextPackage = Package; + } + Grp->LastPackage = Package; + + // Set the name, arch and the ID + Pkg->Name = Grp->Name; + Pkg->Group = Grp.Index(); + // all is mapped to the native architecture + map_ptrloc const idxArch = (Arch == "all") ? Cache.HeaderP->Architecture : WriteUniqString(Arch.c_str()); + if (unlikely(idxArch == 0)) + return false; + Pkg->Arch = idxArch; Pkg->ID = Cache.HeaderP->PackageCount++; - + return true; } /*}}}*/ @@ -188,8 +499,11 @@ bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,string Name) bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver, ListParser &List) { + if (CurrentFile == 0) + return true; + // Get a structure - unsigned long VerFile = Map.Allocate(sizeof(pkgCache::VerFile)); + map_ptrloc const VerFile = AllocateInMap(sizeof(pkgCache::VerFile)); if (VerFile == 0) return 0; @@ -197,7 +511,7 @@ bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver, VF->File = CurrentFile - Cache.PkgFileP; // Link it to the end of the list - __apt_ptrloc *Last = &Ver->FileList; + map_ptrloc *Last = &Ver->FileList; for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; V++) Last = &V->NextFile; VF->NextFile = *Last; @@ -216,11 +530,11 @@ bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver, // --------------------------------------------------------------------- /* This puts a version structure in the linked list */ unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver, - string VerStr, + const string &VerStr, unsigned long Next) { // Get a structure - unsigned long Version = Map.Allocate(sizeof(pkgCache::Version)); + map_ptrloc const Version = AllocateInMap(sizeof(pkgCache::Version)); if (Version == 0) return 0; @@ -228,68 +542,198 @@ unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver, Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version); Ver->NextVer = Next; Ver->ID = Cache.HeaderP->VersionCount++; - Ver->VerStr = Map.WriteString(VerStr); - if (Ver->VerStr == 0) + map_ptrloc const idxVerStr = WriteStringInMap(VerStr); + if (unlikely(idxVerStr == 0)) return 0; + Ver->VerStr = idxVerStr; return Version; } /*}}}*/ -// ListParser::NewDepends - Create a dependency element /*{{{*/ +// CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc, + ListParser &List) +{ + if (CurrentFile == 0) + return true; + + // Get a structure + map_ptrloc const DescFile = AllocateInMap(sizeof(pkgCache::DescFile)); + if (DescFile == 0) + return false; + + pkgCache::DescFileIterator DF(Cache,Cache.DescFileP + DescFile); + DF->File = CurrentFile - Cache.PkgFileP; + + // Link it to the end of the list + map_ptrloc *Last = &Desc->FileList; + for (pkgCache::DescFileIterator D = Desc.FileList(); D.end() == false; D++) + Last = &D->NextFile; + + DF->NextFile = *Last; + *Last = DF.Index(); + + DF->Offset = List.Offset(); + DF->Size = List.Size(); + if (Cache.HeaderP->MaxDescFileSize < DF->Size) + Cache.HeaderP->MaxDescFileSize = DF->Size; + Cache.HeaderP->DescFileCount++; + + return true; +} + /*}}}*/ +// CacheGenerator::NewDescription - Create a new Description /*{{{*/ +// --------------------------------------------------------------------- +/* This puts a description structure in the linked list */ +map_ptrloc pkgCacheGenerator::NewDescription(pkgCache::DescIterator &Desc, + const string &Lang, + const MD5SumValue &md5sum, + map_ptrloc Next) +{ + // Get a structure + map_ptrloc const Description = AllocateInMap(sizeof(pkgCache::Description)); + if (Description == 0) + return 0; + + // Fill it in + Desc = pkgCache::DescIterator(Cache,Cache.DescP + Description); + Desc->NextDesc = Next; + Desc->ID = Cache.HeaderP->DescriptionCount++; + map_ptrloc const idxlanguage_code = WriteStringInMap(Lang); + map_ptrloc const idxmd5sum = WriteStringInMap(md5sum.Value()); + if (unlikely(idxlanguage_code == 0 || idxmd5sum == 0)) + return 0; + Desc->language_code = idxlanguage_code; + Desc->md5sum = idxmd5sum; + + return Description; +} + /*}}}*/ +// CacheGenerator::FinishCache - do various finish operations /*{{{*/ +// --------------------------------------------------------------------- +/* This prepares the Cache for delivery */ +bool pkgCacheGenerator::FinishCache(OpProgress *Progress) +{ + // FIXME: add progress reporting for this operation + // Do we have different architectures in your groups ? + vector archs = APT::Configuration::getArchitectures(); + if (archs.size() > 1) + { + // Create Conflicts in between the group + pkgCache::GrpIterator G = GetCache().GrpBegin(); + Dynamic DynG(G); + for (; G.end() != true; G++) + { + string const PkgName = G.Name(); + pkgCache::PkgIterator P = G.PackageList(); + Dynamic DynP(P); + for (; P.end() != true; P = G.NextPkg(P)) + { + pkgCache::PkgIterator allPkg; + Dynamic DynallPkg(allPkg); + pkgCache::VerIterator V = P.VersionList(); + Dynamic DynV(V); + for (; V.end() != true; V++) + { + // copy P.Arch() into a string here as a cache remap + // in NewDepends() later may alter the pointer location + string Arch = P.Arch() == NULL ? "" : P.Arch(); + map_ptrloc *OldDepLast = NULL; + /* MultiArch handling introduces a lot of implicit Dependencies: + - MultiArch: same → Co-Installable if they have the same version + - Architecture: all → Need to be Co-Installable for internal reasons + - All others conflict with all other group members */ + bool const coInstall = ((V->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same); + for (vector::const_iterator A = archs.begin(); A != archs.end(); ++A) + { + if (Arch == 0 || *A == Arch) + continue; + /* We allow only one installed arch at the time + per group, therefore each group member conflicts + with all other group members */ + pkgCache::PkgIterator D = G.FindPkg(*A); + Dynamic DynD(D); + if (D.end() == true) + continue; + if (coInstall == true) + { + // Replaces: ${self}:other ( << ${binary:Version}) + NewDepends(D, V, V.VerStr(), + pkgCache::Dep::Less, pkgCache::Dep::Replaces, + OldDepLast); + // Breaks: ${self}:other (!= ${binary:Version}) + NewDepends(D, V, V.VerStr(), + pkgCache::Dep::NotEquals, pkgCache::Dep::DpkgBreaks, + OldDepLast); + } else { + // Conflicts: ${self}:other + NewDepends(D, V, "", + pkgCache::Dep::NoOp, pkgCache::Dep::Conflicts, + OldDepLast); + } + } + } + } + } + } + return true; +} + /*}}}*/ +// CacheGenerator::NewDepends - Create a dependency element /*{{{*/ // --------------------------------------------------------------------- /* This creates a dependency element in the tree. It is linked to the version and to the package that it is pointing to. */ -bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver, - string PackageName, - string Version, - unsigned int Op, - unsigned int Type) +bool pkgCacheGenerator::NewDepends(pkgCache::PkgIterator &Pkg, + pkgCache::VerIterator &Ver, + string const &Version, + unsigned int const &Op, + unsigned int const &Type, + map_ptrloc* &OldDepLast) { - pkgCache &Cache = Owner->Cache; - + void const * const oldMap = Map.Data(); // Get a structure - unsigned long Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency)); - if (Dependency == 0) + map_ptrloc const Dependency = AllocateInMap(sizeof(pkgCache::Dependency)); + if (unlikely(Dependency == 0)) return false; // Fill it in pkgCache::DepIterator Dep(Cache,Cache.DepP + Dependency); + Dynamic DynDep(Dep); Dep->ParentVer = Ver.Index(); Dep->Type = Type; Dep->CompareOp = Op; Dep->ID = Cache.HeaderP->DependsCount++; - - // Locate the target package - pkgCache::PkgIterator Pkg; - if (Owner->NewPackage(Pkg,PackageName) == false) - return false; - + // Probe the reverse dependency list for a version string that matches if (Version.empty() == false) { -/* for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++, Hit++) +/* for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++) if (I->Version != 0 && I.TargetVer() == Version) Dep->Version = I->Version;*/ - if (Dep->Version == 0) - if ((Dep->Version = WriteString(Version)) == 0) + if (Dep->Version == 0) { + map_ptrloc const index = WriteStringInMap(Version); + if (unlikely(index == 0)) return false; + Dep->Version = index; + } } - + // Link it to the package Dep->Package = Pkg.Index(); Dep->NextRevDepends = Pkg->RevDepends; Pkg->RevDepends = Dep.Index(); - - /* Link it to the version (at the end of the list) - Caching the old end point speeds up generation substantially */ - if (OldDepVer != Ver) + + // Do we know where to link the Dependency to? + if (OldDepLast == NULL) { OldDepLast = &Ver->DependsList; for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++) OldDepLast = &D->NextDepends; - OldDepVer = Ver; - } - + } else if (oldMap != Map.Data()) + OldDepLast += (map_ptrloc*) Map.Data() - (map_ptrloc*) oldMap; + Dep->NextDepends = *OldDepLast; *OldDepLast = Dep.Index(); OldDepLast = &Dep->NextDepends; @@ -297,36 +741,77 @@ bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver, return true; } /*}}}*/ +// ListParser::NewDepends - Create the environment for a new dependency /*{{{*/ +// --------------------------------------------------------------------- +/* This creates a Group and the Package to link this dependency to if + needed and handles also the caching of the old endpoint */ +bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator &Ver, + const string &PackageName, + const string &Arch, + const string &Version, + unsigned int Op, + unsigned int Type) +{ + pkgCache::GrpIterator Grp; + Dynamic DynGrp(Grp); + if (unlikely(Owner->NewGroup(Grp, PackageName) == false)) + return false; + + // Locate the target package + pkgCache::PkgIterator Pkg = Grp.FindPkg(Arch); + Dynamic DynPkg(Pkg); + if (Pkg.end() == true) { + if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false)) + return false; + } + + // Is it a file dependency? + if (unlikely(PackageName[0] == '/')) + FoundFileDeps = true; + + /* Caching the old end point speeds up generation substantially */ + if (OldDepVer != Ver) { + OldDepLast = NULL; + OldDepVer = Ver; + } + + return Owner->NewDepends(Pkg, Ver, Version, Op, Type, OldDepLast); +} + /*}}}*/ // ListParser::NewProvides - Create a Provides element /*{{{*/ // --------------------------------------------------------------------- /* */ -bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver, - string PackageName, - string Version) +bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator &Ver, + const string &PkgName, + const string &PkgArch, + const string &Version) { pkgCache &Cache = Owner->Cache; // We do not add self referencing provides - if (Ver.ParentPkg().Name() == PackageName) + if (Ver.ParentPkg().Name() == PkgName && (PkgArch == Ver.ParentPkg().Arch() || + (PkgArch == "all" && strcmp((Cache.StrP + Cache.HeaderP->Architecture), Ver.ParentPkg().Arch()) == 0))) return true; // Get a structure - unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides)); - if (Provides == 0) + map_ptrloc const Provides = Owner->AllocateInMap(sizeof(pkgCache::Provides)); + if (unlikely(Provides == 0)) return false; Cache.HeaderP->ProvidesCount++; // Fill it in pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP); + Dynamic DynPrv(Prv); Prv->Version = Ver.Index(); Prv->NextPkgProv = Ver->ProvidesList; Ver->ProvidesList = Prv.Index(); - if (Version.empty() == false && (Prv->Version = WriteString(Version)) == 0) + if (Version.empty() == false && unlikely((Prv->ProvideVersion = WriteString(Version)) == 0)) return false; // Locate the target package pkgCache::PkgIterator Pkg; - if (Owner->NewPackage(Pkg,PackageName) == false) + Dynamic DynPkg(Pkg); + if (unlikely(Owner->NewPackage(Pkg,PkgName, PkgArch) == false)) return false; // Link it to the package @@ -340,33 +825,37 @@ bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver, // CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/ // --------------------------------------------------------------------- /* This is used to select which file is to be associated with all newly - added versions. */ -bool pkgCacheGenerator::SelectFile(string File,unsigned long Flags) + added versions. The caller is responsible for setting the IMS fields. */ +bool pkgCacheGenerator::SelectFile(const string &File,const string &Site, + const pkgIndexFile &Index, + unsigned long Flags) { - struct stat Buf; - if (stat(File.c_str(),&Buf) == -1) - return _error->Errno("stat","Couldn't stat ",File.c_str()); - // Get some space for the structure - CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile)); - if (CurrentFile == Cache.PkgFileP) + map_ptrloc const idxFile = AllocateInMap(sizeof(*CurrentFile)); + if (unlikely(idxFile == 0)) return false; - + CurrentFile = Cache.PkgFileP + idxFile; + // Fill it in - CurrentFile->FileName = Map.WriteString(File); - CurrentFile->Size = Buf.st_size; - CurrentFile->mtime = Buf.st_mtime; + map_ptrloc const idxFileName = WriteStringInMap(File); + map_ptrloc const idxSite = WriteUniqString(Site); + if (unlikely(idxFileName == 0 || idxSite == 0)) + return false; + CurrentFile->FileName = idxFileName; + CurrentFile->Site = idxSite; CurrentFile->NextFile = Cache.HeaderP->FileList; CurrentFile->Flags = Flags; CurrentFile->ID = Cache.HeaderP->PackageFileCount; + map_ptrloc const idxIndexType = WriteUniqString(Index.GetType()->Label); + if (unlikely(idxIndexType == 0)) + return false; + CurrentFile->IndexType = idxIndexType; PkgFileName = File; Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP; Cache.HeaderP->PackageFileCount++; - - if (CurrentFile->FileName == 0) - return false; - - Progress.SubProgress(Buf.st_size); + + if (Progress != 0) + Progress->SubProgress(Index.Size()); return true; } /*}}}*/ @@ -387,7 +876,7 @@ unsigned long pkgCacheGenerator::WriteUniqString(const char *S, // Search for an insertion point pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList; int Res = 1; - __apt_ptrloc *Last = &Cache.HeaderP->StringList; + map_ptrloc *Last = &Cache.HeaderP->StringList; for (; I != Cache.StringItemP; Last = &I->NextItem, I = Cache.StringItemP + I->NextItem) { @@ -404,488 +893,461 @@ unsigned long pkgCacheGenerator::WriteUniqString(const char *S, } // Get a structure - unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem)); + void const * const oldMap = Map.Data(); + map_ptrloc const Item = AllocateInMap(sizeof(pkgCache::StringItem)); if (Item == 0) return 0; + map_ptrloc const idxString = WriteStringInMap(S,Size); + if (unlikely(idxString == 0)) + return 0; + if (oldMap != Map.Data()) { + Last += (map_ptrloc*) Map.Data() - (map_ptrloc*) oldMap; + I += (pkgCache::StringItem*) Map.Data() - (pkgCache::StringItem*) oldMap; + } + *Last = Item; + // Fill in the structure pkgCache::StringItem *ItemP = Cache.StringItemP + Item; ItemP->NextItem = I - Cache.StringItemP; - *Last = Item; - ItemP->String = Map.WriteString(S,Size); - if (ItemP->String == 0) - return 0; - + ItemP->String = idxString; + Bucket = ItemP; return ItemP->String; } /*}}}*/ - -// SrcCacheCheck - Check if the source package cache is uptodate /*{{{*/ +// CheckValidity - Check that a cache is up-to-date /*{{{*/ // --------------------------------------------------------------------- -/* The source cache is checked against the source list and the files - on disk, any difference results in a false. */ -bool pkgSrcCacheCheck(pkgSourceList &List) +/* This just verifies that each file in the list of index files exists, + has matching attributes with the cache and the cache does not have + any extra files. */ +static bool CheckValidity(const string &CacheFile, + pkgSourceList &List, + FileIterator Start, + FileIterator End, + MMap **OutMap = 0) { - if (_error->PendingError() == true) - return false; - - string CacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); - string ListDir = _config->FindDir("Dir::State::lists"); - - // Count the number of missing files - int Missing = 0; - for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++) - { - // Only cache deb source types. - if (I->Type != pkgSourceList::Item::Deb) - { - Missing++; - continue; - } - - string File = ListDir + URItoFileName(I->PackagesURI()); - struct stat Buf; - if (stat(File.c_str(),&Buf) != 0) - { - _error->WarningE("stat","Couldn't stat source package list '%s' (%s)", - I->PackagesInfo().c_str(),File.c_str()); - Missing++; - } - } - - // Open the source package cache - if (FileExists(CacheFile) == false) - return false; - - FileFd CacheF(CacheFile,FileFd::ReadOnly); - if (_error->PendingError() == true) + bool const Debug = _config->FindB("Debug::pkgCacheGen", false); + // No file, certainly invalid + if (CacheFile.empty() == true || FileExists(CacheFile) == false) { - _error->Discard(); + if (Debug == true) + std::clog << "CacheFile doesn't exist" << std::endl; return false; } - - MMap Map(CacheF,MMap::Public | MMap::ReadOnly); - if (_error->PendingError() == true || Map.Size() == 0) + + if (List.GetLastModifiedTime() < GetModificationTime(CacheFile)) { - _error->Discard(); + if (Debug == true) + std::clog << "sources.list is newer than the cache" << std::endl; return false; } - + + // Map it + FileFd CacheF(CacheFile,FileFd::ReadOnly); + SPtr Map = new MMap(CacheF,0); pkgCache Cache(Map); - if (_error->PendingError() == true) + if (_error->PendingError() == true || Map->Size() == 0) { + if (Debug == true) + std::clog << "Errors are pending or Map is empty()" << std::endl; _error->Discard(); return false; } - - // They are certianly out of sync - if (Cache.Head().PackageFileCount != List.size() - Missing) - return false; - for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++) - { - // Search for a match in the source list - bool Bad = true; - for (pkgSourceList::const_iterator I = List.begin(); - I != List.end(); I++) + /* Now we check every index file, see if it is in the cache, + verify the IMS data and check that it is on the disk too.. */ + SPtrArray Visited = new bool[Cache.HeaderP->PackageFileCount]; + memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount); + for (; Start != End; Start++) + { + if (Debug == true) + std::clog << "Checking PkgFile " << (*Start)->Describe() << ": "; + if ((*Start)->HasPackages() == false) { - // Only cache deb source types. - if (I->Type != pkgSourceList::Item::Deb) - continue; - - string File = ListDir + URItoFileName(I->PackagesURI()); - if (F.FileName() == File) - { - Bad = false; - break; - } + if (Debug == true) + std::clog << "Has NO packages" << std::endl; + continue; } - - // Check if the file matches what was cached - Bad |= !F.IsOk(); - if (Bad == true) + + if ((*Start)->Exists() == false) + { +#if 0 // mvo: we no longer give a message here (Default Sources spec) + _error->WarningE("stat",_("Couldn't stat source package list %s"), + (*Start)->Describe().c_str()); +#endif + if (Debug == true) + std::clog << "file doesn't exist" << std::endl; + continue; + } + + // FindInCache is also expected to do an IMS check. + pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache); + if (File.end() == true) + { + if (Debug == true) + std::clog << "FindInCache returned end-Pointer" << std::endl; return false; + } + + Visited[File->ID] = true; + if (Debug == true) + std::clog << "with ID " << File->ID << " is valid" << std::endl; } - return true; -} - /*}}}*/ -// PkgCacheCheck - Check if the package cache is uptodate /*{{{*/ -// --------------------------------------------------------------------- -/* This does a simple check of all files used to compose the cache */ -bool pkgPkgCacheCheck(string CacheFile) -{ - if (_error->PendingError() == true) - return false; - - // Open the source package cache - if (FileExists(CacheFile) == false) - return false; - - FileFd CacheF(CacheFile,FileFd::ReadOnly); - if (_error->PendingError() == true) - { - _error->Discard(); - return false; - } - - MMap Map(CacheF,MMap::Public | MMap::ReadOnly); - if (_error->PendingError() == true || Map.Size() == 0) - { - _error->Discard(); - return false; - } + for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++) + if (Visited[I] == false) + { + if (Debug == true) + std::clog << "File with ID" << I << " wasn't visited" << std::endl; + return false; + } - pkgCache Cache(Map); if (_error->PendingError() == true) { + if (Debug == true) + { + std::clog << "Validity failed because of pending errors:" << std::endl; + _error->DumpErrors(); + } _error->Discard(); return false; } - - // Status files that must be in the cache - string Status[3]; - Status[0] = _config->FindFile("Dir::State::xstatus"); - Status[1]= _config->FindFile("Dir::State::userstatus"); - Status[2] = _config->FindFile("Dir::State::status"); - - // Cheack each file - for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++) - { - if (F.IsOk() == false) - return false; - - // See if this is one of the status files - for (int I = 0; I != 3; I++) - if (F.FileName() == Status[I]) - Status[I] = string(); - } - - // Make sure all the status files are loaded. - for (int I = 0; I != 3; I++) - { - if (Status[I].empty() == false && FileExists(Status[I]) == true) - return false; - } - - return true; -} - /*}}}*/ -// AddSourcesSize - Add the size of the status files /*{{{*/ -// --------------------------------------------------------------------- -/* This adds the size of all the status files to the size counter */ -static bool pkgAddSourcesSize(unsigned long &TotalSize) -{ - // Grab the file names - string xstatus = _config->FindFile("Dir::State::xstatus"); - string userstatus = _config->FindFile("Dir::State::userstatus"); - string status = _config->FindFile("Dir::State::status"); - - // Grab the sizes - struct stat Buf; - if (stat(xstatus.c_str(),&Buf) == 0) - TotalSize += Buf.st_size; - if (stat(userstatus.c_str(),&Buf) == 0) - TotalSize += Buf.st_size; - if (stat(status.c_str(),&Buf) != 0) - return _error->Errno("stat","Couldn't stat the status file %s",status.c_str()); - TotalSize += Buf.st_size; + if (OutMap != 0) + *OutMap = Map.UnGuard(); return true; } /*}}}*/ -// MergeStatus - Add the status files to the cache /*{{{*/ +// ComputeSize - Compute the total size of a bunch of files /*{{{*/ // --------------------------------------------------------------------- -/* This adds the status files to the map */ -static bool pkgMergeStatus(OpProgress &Progress,pkgCacheGenerator &Gen, - unsigned long &CurrentSize,unsigned long TotalSize) +/* Size is kind of an abstract notion that is only used for the progress + meter */ +static unsigned long ComputeSize(FileIterator Start,FileIterator End) { - // Grab the file names - string Status[3]; - Status[0] = _config->FindFile("Dir::State::xstatus"); - Status[1]= _config->FindFile("Dir::State::userstatus"); - Status[2] = _config->FindFile("Dir::State::status"); - - for (int I = 0; I != 3; I++) + unsigned long TotalSize = 0; + for (; Start != End; Start++) { - // Check if the file exists and it is not the primary status file. - string File = Status[I]; - if (I != 2 && FileExists(File) == false) - continue; - - FileFd Pkg(File,FileFd::ReadOnly); - debListParser Parser(Pkg); - Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),"Reading Package Lists"); - if (_error->PendingError() == true) - return _error->Error("Problem opening %s",File.c_str()); - CurrentSize += Pkg.Size(); - - Progress.SubProgress(0,"Local Package State - " + flNotDir(File)); - if (Gen.SelectFile(File,pkgCache::Flag::NotSource) == false) - return _error->Error("Problem with SelectFile %s",File.c_str()); - - if (Gen.MergeList(Parser) == false) - return _error->Error("Problem with MergeList %s",File.c_str()); - Progress.Progress(Pkg.Size()); + if ((*Start)->HasPackages() == false) + continue; + TotalSize += (*Start)->Size(); } - - return true; + return TotalSize; } /*}}}*/ -// GenerateSrcCache - Write the source package lists to the map /*{{{*/ +// BuildCache - Merge the list of index files into the cache /*{{{*/ // --------------------------------------------------------------------- -/* This puts the source package cache into the given generator. */ -bool pkgGenerateSrcCache(pkgSourceList &List,OpProgress &Progress, - pkgCacheGenerator &Gen, - unsigned long &CurrentSize,unsigned long &TotalSize) +/* */ +static bool BuildCache(pkgCacheGenerator &Gen, + OpProgress *Progress, + unsigned long &CurrentSize,unsigned long TotalSize, + FileIterator Start, FileIterator End) { - string ListDir = _config->FindDir("Dir::State::lists"); - - // Prepare the progress indicator - TotalSize = 0; - struct stat Buf; - for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++) + FileIterator I; + for (I = Start; I != End; I++) { - string File = ListDir + URItoFileName(I->PackagesURI()); - if (stat(File.c_str(),&Buf) != 0) + if ((*I)->HasPackages() == false) continue; - TotalSize += Buf.st_size; - } - - if (pkgAddSourcesSize(TotalSize) == false) - return false; - - // Generate the pkg source cache - CurrentSize = 0; - for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++) - { - // Only cache deb source types. - if (I->Type != pkgSourceList::Item::Deb) - continue; - - string File = ListDir + URItoFileName(I->PackagesURI()); - if (FileExists(File) == false) + if ((*I)->Exists() == false) continue; + + if ((*I)->FindInCache(Gen.GetCache()).end() == false) + { + _error->Warning("Duplicate sources.list entry %s", + (*I)->Describe().c_str()); + continue; + } - FileFd Pkg(File,FileFd::ReadOnly); - debListParser Parser(Pkg); - Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),"Reading Package Lists"); - if (_error->PendingError() == true) - return _error->Error("Problem opening %s",File.c_str()); - CurrentSize += Pkg.Size(); - - Progress.SubProgress(0,I->PackagesInfo()); - if (Gen.SelectFile(File) == false) - return _error->Error("Problem with SelectFile %s",File.c_str()); - - if (Gen.MergeList(Parser) == false) - return _error->Error("Problem with MergeList %s",File.c_str()); + unsigned long Size = (*I)->Size(); + if (Progress != NULL) + Progress->OverallProgress(CurrentSize,TotalSize,Size,_("Reading package lists")); + CurrentSize += Size; - // Check the release file - string RFile = ListDir + URItoFileName(I->ReleaseURI()); - if (FileExists(RFile) == true) + if ((*I)->Merge(Gen,Progress) == false) + return false; + } + + if (Gen.HasFileDeps() == true) + { + if (Progress != NULL) + Progress->Done(); + TotalSize = ComputeSize(Start, End); + CurrentSize = 0; + for (I = Start; I != End; I++) { - FileFd Rel(RFile,FileFd::ReadOnly); - if (_error->PendingError() == true) + unsigned long Size = (*I)->Size(); + if (Progress != NULL) + Progress->OverallProgress(CurrentSize,TotalSize,Size,_("Collecting File Provides")); + CurrentSize += Size; + if ((*I)->MergeFileProvides(Gen,Progress) == false) return false; - Parser.LoadReleaseInfo(Gen.GetCurFile(),Rel); } } return true; } /*}}}*/ -// MakeStatusCache - Generates a cache that includes the status files /*{{{*/ +// CacheGenerator::CreateDynamicMMap - load an mmap with configuration options /*{{{*/ +DynamicMMap* pkgCacheGenerator::CreateDynamicMMap(FileFd *CacheF, unsigned long Flags) { + unsigned long const MapStart = _config->FindI("APT::Cache-Start", 24*1024*1024); + unsigned long const MapGrow = _config->FindI("APT::Cache-Grow", 1*1024*1024); + unsigned long const MapLimit = _config->FindI("APT::Cache-Limit", 0); + Flags |= MMap::Moveable; + if (_config->FindB("APT::Cache-Fallback", false) == true) + Flags |= MMap::Fallback; + if (CacheF != NULL) + return new DynamicMMap(*CacheF, Flags, MapStart, MapGrow, MapLimit); + else + return new DynamicMMap(Flags, MapStart, MapGrow, MapLimit); +} + /*}}}*/ +// CacheGenerator::MakeStatusCache - Construct the status cache /*{{{*/ // --------------------------------------------------------------------- -/* This copies the package source cache and then merges the status and - xstatus files into it. */ -bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress) +/* This makes sure that the status cache (the cache that has all + index files from the sources list and all local ones) is ready + to be mmaped. If OutMap is not zero then a MMap object representing + the cache will be stored there. This is pretty much mandetory if you + are using AllowMem. AllowMem lets the function be run as non-root + where it builds the cache 'fast' into a memory buffer. */ +__deprecated bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress, + MMap **OutMap, bool AllowMem) + { return pkgCacheGenerator::MakeStatusCache(List, &Progress, OutMap, AllowMem); } +bool pkgCacheGenerator::MakeStatusCache(pkgSourceList &List,OpProgress *Progress, + MMap **OutMap,bool AllowMem) { - Progress.OverallProgress(0,1,1,"Reading Package Lists"); - - string CacheFile = _config->FindFile("Dir::Cache::pkgcache"); - bool SrcOk = pkgSrcCacheCheck(List); - bool PkgOk = SrcOk && pkgPkgCacheCheck(CacheFile); - - // Rebuild the source and package caches - if (SrcOk == false) - { - string SCacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); - FileFd SCacheF(SCacheFile,FileFd::WriteEmpty); - FileFd CacheF(CacheFile,FileFd::WriteEmpty); - DynamicMMap Map(CacheF,MMap::Public); - if (_error->PendingError() == true) - return false; - - pkgCacheGenerator Gen(Map,Progress); - unsigned long CurrentSize = 0; - unsigned long TotalSize = 0; - if (pkgGenerateSrcCache(List,Progress,Gen,CurrentSize,TotalSize) == false) - return false; - - // Write the src cache - Gen.GetCache().HeaderP->Dirty = false; - if (SCacheF.Write(Map.Data(),Map.Size()) == false) - return _error->Error("IO Error saving source cache"); - Gen.GetCache().HeaderP->Dirty = true; - - // Merge in the source caches - return pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize); - } - - if (PkgOk == true) + bool const Debug = _config->FindB("Debug::pkgCacheGen", false); + + vector Files; + for (vector::const_iterator i = List.begin(); + i != List.end(); + i++) { - Progress.OverallProgress(1,1,1,"Reading Package Lists"); - return true; + vector *Indexes = (*i)->GetIndexFiles(); + for (vector::const_iterator j = Indexes->begin(); + j != Indexes->end(); + j++) + Files.push_back (*j); } - // We use the source cache to generate the package cache - string SCacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); - - FileFd SCacheF(SCacheFile,FileFd::ReadOnly); - FileFd CacheF(CacheFile,FileFd::WriteEmpty); - DynamicMMap Map(CacheF,MMap::Public); - if (_error->PendingError() == true) - return false; - - // Preload the map with the source cache - if (SCacheF.Read((unsigned char *)Map.Data() + Map.RawAllocate(SCacheF.Size()), - SCacheF.Size()) == false) - return false; - - pkgCacheGenerator Gen(Map,Progress); - - // Compute the progress - unsigned long TotalSize = 0; - if (pkgAddSourcesSize(TotalSize) == false) + unsigned long const EndOfSource = Files.size(); + if (_system->AddStatusFiles(Files) == false) return false; - unsigned long CurrentSize = 0; - return pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize); -} - /*}}}*/ -// MakeStatusCacheMem - Returns a map for the status cache /*{{{*/ -// --------------------------------------------------------------------- -/* This creates a map object for the status cache. If the process has write - access to the caches then it is the same as MakeStatusCache, otherwise it - creates a memory block and puts the cache in there. */ -MMap *pkgMakeStatusCacheMem(pkgSourceList &List,OpProgress &Progress) -{ - /* If the cache file is writeable this is just a wrapper for - MakeStatusCache */ - string CacheFile = _config->FindFile("Dir::Cache::pkgcache"); - bool Writeable = (access(CacheFile.c_str(),W_OK) == 0) || - (errno == ENOENT); + // Decide if we can write to the files.. + string const CacheFile = _config->FindFile("Dir::Cache::pkgcache"); + string const SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); - if (Writeable == true) + // ensure the cache directory exists + if (CacheFile.empty() == false || SrcCacheFile.empty() == false) { - if (pkgMakeStatusCache(List,Progress) == false) - return 0; - - // Open the cache file - FileFd File(_config->FindFile("Dir::Cache::pkgcache"),FileFd::ReadOnly); - if (_error->PendingError() == true) - return 0; - - MMap *Map = new MMap(File,MMap::Public | MMap::ReadOnly); - if (_error->PendingError() == true) - { - delete Map; - return 0; - } - return Map; - } + string dir = _config->FindDir("Dir::Cache"); + size_t const len = dir.size(); + if (len > 5 && dir.find("/apt/", len - 6, 5) == len - 5) + dir = dir.substr(0, len - 5); + if (CacheFile.empty() == false) + CreateDirectory(dir, flNotFile(CacheFile)); + if (SrcCacheFile.empty() == false) + CreateDirectory(dir, flNotFile(SrcCacheFile)); + } + + // Decide if we can write to the cache + bool Writeable = false; + if (CacheFile.empty() == false) + Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0; + else + if (SrcCacheFile.empty() == false) + Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0; + if (Debug == true) + std::clog << "Do we have write-access to the cache files? " << (Writeable ? "YES" : "NO") << std::endl; - // Mostly from MakeStatusCache.. - Progress.OverallProgress(0,1,1,"Reading Package Lists"); + if (Writeable == false && AllowMem == false && CacheFile.empty() == false) + return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str()); + + if (Progress != NULL) + Progress->OverallProgress(0,1,1,_("Reading package lists")); + + // Cache is OK, Fin. + if (CheckValidity(CacheFile, List, Files.begin(),Files.end(),OutMap) == true) + { + if (Progress != NULL) + Progress->OverallProgress(1,1,1,_("Reading package lists")); + if (Debug == true) + std::clog << "pkgcache.bin is valid - no need to build anything" << std::endl; + return true; + } + else if (Debug == true) + std::clog << "pkgcache.bin is NOT valid" << std::endl; - bool SrcOk = pkgSrcCacheCheck(List); - bool PkgOk = SrcOk && pkgPkgCacheCheck(CacheFile); - - // Rebuild the source and package caches - if (SrcOk == false) + /* At this point we know we need to reconstruct the package cache, + begin. */ + SPtr CacheF; + SPtr Map; + if (Writeable == true && CacheFile.empty() == false) { - DynamicMMap *Map = new DynamicMMap(MMap::Public); + _error->PushToStack(); + unlink(CacheFile.c_str()); + CacheF = new FileFd(CacheFile,FileFd::WriteAtomic); + fchmod(CacheF->Fd(),0644); + Map = CreateDynamicMMap(CacheF, MMap::Public); if (_error->PendingError() == true) { - delete Map; - return 0; - } - - pkgCacheGenerator Gen(*Map,Progress); - unsigned long CurrentSize = 0; - unsigned long TotalSize = 0; - if (pkgGenerateSrcCache(List,Progress,Gen,CurrentSize,TotalSize) == false) - { - delete Map; - return 0; + delete CacheF.UnGuard(); + delete Map.UnGuard(); + if (Debug == true) + std::clog << "Open filebased MMap FAILED" << std::endl; + Writeable = false; + if (AllowMem == false) + { + _error->MergeWithStack(); + return false; + } + _error->RevertToStack(); } - - // Merge in the source caches - if (pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize) == false) + else if (Debug == true) { - delete Map; - return 0; + _error->MergeWithStack(); + std::clog << "Open filebased MMap" << std::endl; } - - return Map; } + if (Writeable == false || CacheFile.empty() == true) + { + // Just build it in memory.. + Map = CreateDynamicMMap(NULL); + if (Debug == true) + std::clog << "Open memory Map (not filebased)" << std::endl; + } + + // Lets try the source cache. + unsigned long CurrentSize = 0; + unsigned long TotalSize = 0; + if (CheckValidity(SrcCacheFile, List, Files.begin(), + Files.begin()+EndOfSource) == true) + { + if (Debug == true) + std::clog << "srcpkgcache.bin is valid - populate MMap with it." << std::endl; + // Preload the map with the source cache + FileFd SCacheF(SrcCacheFile,FileFd::ReadOnly); + unsigned long const alloc = Map->RawAllocate(SCacheF.Size()); + if ((alloc == 0 && _error->PendingError()) + || SCacheF.Read((unsigned char *)Map->Data() + alloc, + SCacheF.Size()) == false) + return false; - if (PkgOk == true) + TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end()); + + // Build the status cache + pkgCacheGenerator Gen(Map.Get(),Progress); + if (_error->PendingError() == true) + return false; + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin()+EndOfSource,Files.end()) == false) + return false; + + // FIXME: move me to a better place + Gen.FinishCache(Progress); + } + else { - Progress.OverallProgress(1,1,1,"Reading Package Lists"); + if (Debug == true) + std::clog << "srcpkgcache.bin is NOT valid - rebuild" << std::endl; + TotalSize = ComputeSize(Files.begin(),Files.end()); - // Open the cache file - FileFd File(_config->FindFile("Dir::Cache::pkgcache"),FileFd::ReadOnly); + // Build the source cache + pkgCacheGenerator Gen(Map.Get(),Progress); if (_error->PendingError() == true) - return 0; + return false; + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin(),Files.begin()+EndOfSource) == false) + return false; - MMap *Map = new MMap(File,MMap::Public | MMap::ReadOnly); - if (_error->PendingError() == true) + // Write it back + if (Writeable == true && SrcCacheFile.empty() == false) { - delete Map; - return 0; + FileFd SCacheF(SrcCacheFile,FileFd::WriteAtomic); + if (_error->PendingError() == true) + return false; + + fchmod(SCacheF.Fd(),0644); + + // Write out the main data + if (SCacheF.Write(Map->Data(),Map->Size()) == false) + return _error->Error(_("IO Error saving source cache")); + SCacheF.Sync(); + + // Write out the proper header + Gen.GetCache().HeaderP->Dirty = false; + if (SCacheF.Seek(0) == false || + SCacheF.Write(Map->Data(),sizeof(*Gen.GetCache().HeaderP)) == false) + return _error->Error(_("IO Error saving source cache")); + Gen.GetCache().HeaderP->Dirty = true; + SCacheF.Sync(); } - return Map; + + // Build the status cache + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin()+EndOfSource,Files.end()) == false) + return false; + + // FIXME: move me to a better place + Gen.FinishCache(Progress); } - - // We use the source cache to generate the package cache - string SCacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); - FileFd SCacheF(SCacheFile,FileFd::ReadOnly); - DynamicMMap *Map = new DynamicMMap(MMap::Public); + if (Debug == true) + std::clog << "Caches are ready for shipping" << std::endl; + if (_error->PendingError() == true) + return false; + if (OutMap != 0) { - delete Map; - return 0; - } - - // Preload the map with the source cache - if (SCacheF.Read((unsigned char *)Map->Data() + Map->RawAllocate(SCacheF.Size()), - SCacheF.Size()) == false) - { - delete Map; - return 0; + if (CacheF != 0) + { + delete Map.UnGuard(); + *OutMap = new MMap(*CacheF,0); + } + else + { + *OutMap = Map.UnGuard(); + } } - - pkgCacheGenerator Gen(*Map,Progress); - // Compute the progress - unsigned long TotalSize = 0; - if (pkgAddSourcesSize(TotalSize) == false) - { - delete Map; - return 0; - } + return true; +} + /*}}}*/ +// CacheGenerator::MakeOnlyStatusCache - Build only a status files cache/*{{{*/ +// --------------------------------------------------------------------- +/* */ +__deprecated bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap) + { return pkgCacheGenerator::MakeOnlyStatusCache(&Progress, OutMap); } +bool pkgCacheGenerator::MakeOnlyStatusCache(OpProgress *Progress,DynamicMMap **OutMap) +{ + vector Files; + unsigned long EndOfSource = Files.size(); + if (_system->AddStatusFiles(Files) == false) + return false; + SPtr Map = CreateDynamicMMap(NULL); unsigned long CurrentSize = 0; - if (pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize) == false) - { - delete Map; - return 0; - } - - return Map; + unsigned long TotalSize = 0; + + TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end()); + + // Build the status cache + if (Progress != NULL) + Progress->OverallProgress(0,1,1,_("Reading package lists")); + pkgCacheGenerator Gen(Map.Get(),Progress); + if (_error->PendingError() == true) + return false; + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin()+EndOfSource,Files.end()) == false) + return false; + + // FIXME: move me to a better place + Gen.FinishCache(Progress); + + if (_error->PendingError() == true) + return false; + *OutMap = Map.UnGuard(); + + return true; } /*}}}*/