X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/8efa2a3ba4ae833415223a888e4561e57e4bf132..a7307a8727c85b0166339a54fb14ba3812f7a608:/apt-pkg/pkgcachegen.cc diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc index 452caa74a..3eeb18cae 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.10 1998/07/16 06:08:38 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,35 +10,74 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/pkgcachegen.h" -#endif +#define APT_COMPATIBILITY 986 #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include #include #include +#include +#include /*}}}*/ +typedef vector::iterator FileIterator; // CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/ // --------------------------------------------------------------------- /* We set the diry flag and make sure that is written to the disk */ -pkgCacheGenerator::pkgCacheGenerator(DynamicMMap &Map) : Map(Map), Cache(Map) +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(); + Cache.HeaderP->VerSysName = Map.WriteString(_system->VS->Label); + Cache.HeaderP->Architecture = Map.WriteString(_config->Find("APT::Architecture")); + 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])); } /*}}}*/ // CacheGenerator::~pkgCacheGenerator - Destructor /*{{{*/ @@ -60,64 +99,225 @@ pkgCacheGenerator::~pkgCacheGenerator() // --------------------------------------------------------------------- /* 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(); - pkgCache::PkgIterator Pkg; - if (NewPackage(Pkg,PackageName) == false) + if (PackageName.empty() == true) return false; + pkgCache::PkgIterator Pkg; + if (NewPackage(Pkg,PackageName) == false) + return _error->Error(_("Error occurred while processing %s (NewPackage)"),PackageName.c_str()); + Counter++; + 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 done with correct sorting */ string Version = List.Version(); if (Version.empty() == true) { + // we first process the package, then the descriptions + // (this has the bonus that we get MMap error when we run out + // of MMap space) if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false) - return 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(); + pkgCache::VerIterator Ver = Pkg.VersionList(); + map_ptrloc *LastVer = &Pkg->VersionList; + + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) + { + pkgCache::DescIterator Desc = Ver.DescriptionList(); + 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 + *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), CurMd5, *LastDesc); + 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(); - unsigned long *Last = &Pkg->VersionList; + map_ptrloc *LastVer = &Pkg->VersionList; int Res = 1; - for (; Ver.end() == false; Last = &Ver->NextVer, Ver++) + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) { - Res = pkgVersionCompare(Version.begin(),Version.end(),Ver.VerStr(), - Ver.VerStr() + strlen(Ver.VerStr())); + Res = Cache.VS->CmpVersion(Version,Ver.VerStr()); if (Res >= 0) break; } /* We already have a version for this item, record that we saw it */ - if (Res == 0) + unsigned long Hash = List.VersionHash(); + if (Res == 0 && Ver->Hash == Hash) { if (List.UsePackage(Pkg,Ver) == false) - return false; - + return _error->Error(_("Error occurred while processing %s (UsePackage2)"), + PackageName.c_str()); + if (NewFileVer(Ver,List) == false) - return false; + return _error->Error(_("Error occurred while processing %s (NewFileVer1)"), + PackageName.c_str()); + + // Read only a single record and return + if (OutVer != 0) + { + *OutVer = Ver; + FoundFileDeps |= List.HasFileDeps(); + return true; + } continue; } + // Skip to the end of the same version set. + if (Res == 0) + { + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) + { + Res = Cache.VS->CmpVersion(Version,Ver.VerStr()); + if (Res != 0) + break; + } + } + // Add a new version - *Last = NewVersion(Ver,Version,*Last); + *LastVer = NewVersion(Ver,Version,*LastVer); Ver->ParentPkg = Pkg.Index(); - if (List.NewVersion(Ver) == false) - return false; + Ver->Hash = Hash; + + if ((*LastVer == 0 && _error->PendingError()) || List.NewVersion(Ver) == false) + return _error->Error(_("Error occurred while processing %s (NewVersion1)"), + PackageName.c_str()); if (List.UsePackage(Pkg,Ver) == false) - return false; + return _error->Error(_("Error occurred while processing %s (UsePackage3)"), + PackageName.c_str()); if (NewFileVer(Ver,List) == false) + return _error->Error(_("Error occurred while processing %s (NewVersion2)"), + PackageName.c_str()); + + // 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(); + map_ptrloc *LastDesc = &Ver->DescriptionList; + + // Skip to the end of description set + for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++); + + // Add new description + *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), List.Description_md5(), *LastDesc); + 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::MergeFileProvides - Merge file provides /*{{{*/ +// --------------------------------------------------------------------- +/* 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) +{ + 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); + 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(); + 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; @@ -126,12 +326,12 @@ bool pkgCacheGenerator::MergeList(ListParser &List) // 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,string Name) +bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,const string &Name) { Pkg = Cache.FindPkg(Name); if (Pkg.end() == false) return true; - + // Get a structure unsigned long Package = Map.Allocate(sizeof(pkgCache::Package)); if (Package == 0) @@ -159,6 +359,9 @@ 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)); if (VerFile == 0) @@ -166,11 +369,20 @@ bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver, pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile); VF->File = CurrentFile - Cache.PkgFileP; - VF->NextFile = Ver->FileList; - Ver->FileList = VF.Index(); + + // Link it to the end of the list + map_ptrloc *Last = &Ver->FileList; + for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; V++) + Last = &V->NextFile; + VF->NextFile = *Last; + *Last = VF.Index(); + VF->Offset = List.Offset(); VF->Size = List.Size(); - + if (Cache.HeaderP->MaxVerFileSize < VF->Size) + Cache.HeaderP->MaxVerFileSize = VF->Size; + Cache.HeaderP->VerFileCount++; + return true; } /*}}}*/ @@ -178,7 +390,7 @@ 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 @@ -197,13 +409,72 @@ unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver, return Version; } /*}}}*/ +// CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc, + ListParser &List) +{ + if (CurrentFile == 0) + return true; + + // Get a structure + unsigned long DescFile = Map.Allocate(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 Description = Map.Allocate(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++; + Desc->language_code = Map.WriteString(Lang); + Desc->md5sum = Map.WriteString(md5sum.Value()); + if (Desc->language_code == 0 || Desc->md5sum == 0) + return 0; + + return Description; +} + /*}}}*/ // ListParser::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, + const string &PackageName, + const string &Version, unsigned int Op, unsigned int Type) { @@ -229,26 +500,37 @@ bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver, // 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++) +/* for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++) if (I->Version != 0 && I.TargetVer() == Version) - Dep->Version = I->Version; + Dep->Version = I->Version;*/ if (Dep->Version == 0) if ((Dep->Version = WriteString(Version)) == 0) return false; } - + // 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) - unsigned long *Last = &Ver->DependsList; - for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++) - Last = &D->NextDepends; - Dep->NextDepends = *Last; - *Last = 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) + { + OldDepLast = &Ver->DependsList; + for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++) + OldDepLast = &D->NextDepends; + OldDepVer = Ver; + } + + // Is it a file dependency? + if (PackageName[0] == '/') + FoundFileDeps = true; + Dep->NextDepends = *OldDepLast; + *OldDepLast = Dep.Index(); + OldDepLast = &Dep->NextDepends; + return true; } /*}}}*/ @@ -256,8 +538,8 @@ bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver, // --------------------------------------------------------------------- /* */ bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver, - string PackageName, - string Version) + const string &PackageName, + const string &Version) { pkgCache &Cache = Owner->Cache; @@ -269,13 +551,14 @@ bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver, unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides)); if (Provides == 0) return false; + Cache.HeaderP->ProvidesCount++; // Fill it in pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP); 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 && (Prv->ProvideVersion = WriteString(Version)) == 0) return false; // Locate the target package @@ -294,13 +577,11 @@ 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) @@ -308,14 +589,20 @@ bool pkgCacheGenerator::SelectFile(string File,unsigned long Flags) // Fill it in CurrentFile->FileName = Map.WriteString(File); - CurrentFile->Size = Buf.st_size; - CurrentFile->mtime = Buf.st_mtime; + CurrentFile->Site = WriteUniqString(Site); CurrentFile->NextFile = Cache.HeaderP->FileList; CurrentFile->Flags = Flags; + CurrentFile->ID = Cache.HeaderP->PackageFileCount; + CurrentFile->IndexType = WriteUniqString(Index.GetType()->Label); PkgFileName = File; + Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP; + Cache.HeaderP->PackageFileCount++; if (CurrentFile->FileName == 0) return false; + + if (Progress != 0) + Progress->SubProgress(Index.Size()); return true; } /*}}}*/ @@ -326,10 +613,17 @@ bool pkgCacheGenerator::SelectFile(string File,unsigned long Flags) unsigned long pkgCacheGenerator::WriteUniqString(const char *S, unsigned int Size) { + /* We use a very small transient hash table here, this speeds up generation + by a fair amount on slower machines */ + pkgCache::StringItem *&Bucket = UniqHash[(S[0]*5 + S[1]) % _count(UniqHash)]; + if (Bucket != 0 && + stringcmp(S,S+Size,Cache.StrP + Bucket->String) == 0) + return Bucket->String; + // Search for an insertion point pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList; int Res = 1; - unsigned long *Last = &Cache.HeaderP->StringList; + map_ptrloc *Last = &Cache.HeaderP->StringList; for (; I != Cache.StringItemP; Last = &I->NextItem, I = Cache.StringItemP + I->NextItem) { @@ -340,7 +634,10 @@ unsigned long pkgCacheGenerator::WriteUniqString(const char *S, // Match if (Res == 0) + { + Bucket = I; return I->String; + } // Get a structure unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem)); @@ -355,6 +652,371 @@ unsigned long pkgCacheGenerator::WriteUniqString(const char *S, if (ItemP->String == 0) return 0; + Bucket = ItemP; return ItemP->String; } /*}}}*/ +// CheckValidity - Check that a cache is up-to-date /*{{{*/ +// --------------------------------------------------------------------- +/* 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, FileIterator Start, + FileIterator End,MMap **OutMap = 0) +{ + bool const Debug = _config->FindB("Debug::pkgCacheGen", false); + // No file, certainly invalid + if (CacheFile.empty() == true || FileExists(CacheFile) == false) + { + if (Debug == true) + std::clog << "CacheFile doesn't exist" << std::endl; + return false; + } + + // Map it + FileFd CacheF(CacheFile,FileFd::ReadOnly); + SPtr Map = new MMap(CacheF,0); + pkgCache Cache(Map); + 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; + } + + /* 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) + { + if (Debug == true) + std::clog << "Has NO packages" << std::endl; + continue; + } + + 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; + } + + 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; + } + + if (_error->PendingError() == true) + { + if (Debug == true) + { + std::clog << "Validity failed because of pending errors:" << std::endl; + _error->DumpErrors(); + } + _error->Discard(); + return false; + } + + if (OutMap != 0) + *OutMap = Map.UnGuard(); + return true; +} + /*}}}*/ +// ComputeSize - Compute the total size of a bunch of files /*{{{*/ +// --------------------------------------------------------------------- +/* Size is kind of an abstract notion that is only used for the progress + meter */ +static unsigned long ComputeSize(FileIterator Start,FileIterator End) +{ + unsigned long TotalSize = 0; + for (; Start != End; Start++) + { + if ((*Start)->HasPackages() == false) + continue; + TotalSize += (*Start)->Size(); + } + return TotalSize; +} + /*}}}*/ +// BuildCache - Merge the list of index files into the cache /*{{{*/ +// --------------------------------------------------------------------- +/* */ +static bool BuildCache(pkgCacheGenerator &Gen, + OpProgress &Progress, + unsigned long &CurrentSize,unsigned long TotalSize, + FileIterator Start, FileIterator End) +{ + FileIterator I; + for (I = Start; I != End; I++) + { + if ((*I)->HasPackages() == false) + continue; + + if ((*I)->Exists() == false) + continue; + + if ((*I)->FindInCache(Gen.GetCache()).end() == false) + { + _error->Warning("Duplicate sources.list entry %s", + (*I)->Describe().c_str()); + continue; + } + + unsigned long Size = (*I)->Size(); + Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading package lists")); + CurrentSize += Size; + + if ((*I)->Merge(Gen,Progress) == false) + return false; + } + + if (Gen.HasFileDeps() == true) + { + Progress.Done(); + TotalSize = ComputeSize(Start, End); + CurrentSize = 0; + for (I = Start; I != End; I++) + { + unsigned long Size = (*I)->Size(); + Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Collecting File Provides")); + CurrentSize += Size; + if ((*I)->MergeFileProvides(Gen,Progress) == false) + return false; + } + } + + return true; +} + /*}}}*/ +// MakeStatusCache - Construct the status cache /*{{{*/ +// --------------------------------------------------------------------- +/* 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. */ +bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress, + MMap **OutMap,bool AllowMem) +{ + bool const Debug = _config->FindB("Debug::pkgCacheGen", false); + unsigned long const MapSize = _config->FindI("APT::Cache-Limit",24*1024*1024); + + vector Files; + for (vector::const_iterator i = List.begin(); + i != List.end(); + i++) + { + vector *Indexes = (*i)->GetIndexFiles(); + for (vector::const_iterator j = Indexes->begin(); + j != Indexes->end(); + j++) + Files.push_back (*j); + } + + unsigned long const EndOfSource = Files.size(); + if (_system->AddStatusFiles(Files) == false) + return false; + + // Decide if we can write to the files.. + string const CacheFile = _config->FindFile("Dir::Cache::pkgcache"); + string const SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache"); + + // 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; + + if (Writeable == false && AllowMem == false && CacheFile.empty() == false) + return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str()); + + Progress.OverallProgress(0,1,1,_("Reading package lists")); + + // Cache is OK, Fin. + if (CheckValidity(CacheFile,Files.begin(),Files.end(),OutMap) == true) + { + 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; + + /* At this point we know we need to reconstruct the package cache, + begin. */ + SPtr CacheF; + SPtr Map; + if (Writeable == true && CacheFile.empty() == false) + { + unlink(CacheFile.c_str()); + CacheF = new FileFd(CacheFile,FileFd::WriteEmpty); + fchmod(CacheF->Fd(),0644); + Map = new DynamicMMap(*CacheF,MMap::Public,MapSize); + if (_error->PendingError() == true) + return false; + if (Debug == true) + std::clog << "Open filebased MMap" << std::endl; + } + else + { + // Just build it in memory.. + Map = new DynamicMMap(0,MapSize); + 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,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; + + 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; + } + else + { + if (Debug == true) + std::clog << "srcpkgcache.bin is NOT valid - rebuild" << std::endl; + TotalSize = ComputeSize(Files.begin(),Files.end()); + + // Build the source cache + pkgCacheGenerator Gen(Map.Get(),&Progress); + if (_error->PendingError() == true) + return false; + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin(),Files.begin()+EndOfSource) == false) + return false; + + // Write it back + if (Writeable == true && SrcCacheFile.empty() == false) + { + FileFd SCacheF(SrcCacheFile,FileFd::WriteEmpty); + 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(); + } + + // Build the status cache + if (BuildCache(Gen,Progress,CurrentSize,TotalSize, + Files.begin()+EndOfSource,Files.end()) == false) + return false; + } + if (Debug == true) + std::clog << "Caches are ready for shipping" << std::endl; + + if (_error->PendingError() == true) + return false; + if (OutMap != 0) + { + if (CacheF != 0) + { + delete Map.UnGuard(); + *OutMap = new MMap(*CacheF,0); + } + else + { + *OutMap = Map.UnGuard(); + } + } + + return true; +} + /*}}}*/ +// MakeOnlyStatusCache - Build a cache with just the status files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap) +{ + unsigned long MapSize = _config->FindI("APT::Cache-Limit",20*1024*1024); + vector Files; + unsigned long EndOfSource = Files.size(); + if (_system->AddStatusFiles(Files) == false) + return false; + + SPtr Map = new DynamicMMap(0,MapSize); + unsigned long CurrentSize = 0; + unsigned long TotalSize = 0; + + TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end()); + + // Build the status cache + 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; + + if (_error->PendingError() == true) + return false; + *OutMap = Map.UnGuard(); + + return true; +} + /*}}}*/