]> git.saurik.com Git - apt.git/blame - apt-pkg/pkgcachegen.cc
* apt-pkg/depcache.cc:
[apt.git] / apt-pkg / pkgcachegen.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7db98ffc 3// $Id: pkgcachegen.cc,v 1.53.2.1 2003/12/24 23:09:17 mdz Exp $
578bfd0a
AL
4/* ######################################################################
5
6 Package Cache Generator - Generator for the cache structure.
7
8 This builds the cache structure from the abstract package list parser.
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
6c139d6e 13#ifdef __GNUG__
094a497d 14#pragma implementation "apt-pkg/pkgcachegen.h"
6c139d6e
AL
15#endif
16
b2e465d6
AL
17#define APT_COMPATIBILITY 986
18
094a497d
AL
19#include <apt-pkg/pkgcachegen.h>
20#include <apt-pkg/error.h>
21#include <apt-pkg/version.h>
b35d2f5f
AL
22#include <apt-pkg/progress.h>
23#include <apt-pkg/sourcelist.h>
24#include <apt-pkg/configuration.h>
cdcc6d34 25#include <apt-pkg/strutl.h>
b2e465d6
AL
26#include <apt-pkg/sptr.h>
27#include <apt-pkg/pkgsystem.h>
578bfd0a 28
afb1e2e3
MV
29#include <apt-pkg/tagfile.h>
30
b2e465d6 31#include <apti18n.h>
e7b470ee
AL
32
33#include <vector>
34
578bfd0a
AL
35#include <sys/stat.h>
36#include <unistd.h>
803fafcb 37#include <errno.h>
7ef72446 38#include <stdio.h>
1ae93c94 39#include <system.h>
578bfd0a 40 /*}}}*/
e7b470ee 41typedef vector<pkgIndexFile *>::iterator FileIterator;
578bfd0a
AL
42
43// CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/
44// ---------------------------------------------------------------------
45/* We set the diry flag and make sure that is written to the disk */
b2e465d6 46pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) :
45415543
AL
47 Map(*pMap), Cache(pMap,false), Progress(Prog),
48 FoundFileDeps(0)
578bfd0a 49{
ddc1d8d0 50 CurrentFile = 0;
b2e465d6 51 memset(UniqHash,0,sizeof(UniqHash));
ddc1d8d0 52
578bfd0a
AL
53 if (_error->PendingError() == true)
54 return;
b2e465d6 55
578bfd0a
AL
56 if (Map.Size() == 0)
57 {
b2e465d6
AL
58 // Setup the map interface..
59 Cache.HeaderP = (pkgCache::Header *)Map.Data();
578bfd0a 60 Map.RawAllocate(sizeof(pkgCache::Header));
b2e465d6
AL
61 Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
62
63 // Starting header
578bfd0a 64 *Cache.HeaderP = pkgCache::Header();
b2e465d6
AL
65 Cache.HeaderP->VerSysName = Map.WriteString(_system->VS->Label);
66 Cache.HeaderP->Architecture = Map.WriteString(_config->Find("APT::Architecture"));
67 Cache.ReMap();
578bfd0a 68 }
b2e465d6
AL
69 else
70 {
71 // Map directly from the existing file
72 Cache.ReMap();
73 Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
74 if (Cache.VS != _system->VS)
75 {
76 _error->Error(_("Cache has an incompatible versioning system"));
77 return;
78 }
79 }
80
578bfd0a
AL
81 Cache.HeaderP->Dirty = true;
82 Map.Sync(0,sizeof(pkgCache::Header));
578bfd0a
AL
83}
84 /*}}}*/
85// CacheGenerator::~pkgCacheGenerator - Destructor /*{{{*/
86// ---------------------------------------------------------------------
87/* We sync the data then unset the dirty flag in two steps so as to
88 advoid a problem during a crash */
89pkgCacheGenerator::~pkgCacheGenerator()
90{
91 if (_error->PendingError() == true)
92 return;
93 if (Map.Sync() == false)
94 return;
95
96 Cache.HeaderP->Dirty = false;
97 Map.Sync(0,sizeof(pkgCache::Header));
98}
99 /*}}}*/
100// CacheGenerator::MergeList - Merge the package list /*{{{*/
101// ---------------------------------------------------------------------
102/* This provides the generation of the entries in the cache. Each loop
103 goes through a single package record from the underlying parse engine. */
ddc1d8d0
AL
104bool pkgCacheGenerator::MergeList(ListParser &List,
105 pkgCache::VerIterator *OutVer)
578bfd0a
AL
106{
107 List.Owner = this;
0149949b 108
f9eec0e7 109 unsigned int Counter = 0;
0149949b 110 while (List.Step() == true)
578bfd0a
AL
111 {
112 // Get a pointer to the package structure
9ddf7030 113 string PackageName = List.Package();
65a1e968
AL
114 if (PackageName.empty() == true)
115 return false;
116
9ddf7030 117 pkgCache::PkgIterator Pkg;
8efa2a3b 118 if (NewPackage(Pkg,PackageName) == false)
080bf1be 119 return _error->Error(_("Error occurred while processing %s (NewPackage)"),PackageName.c_str());
a246f2dc 120 Counter++;
ddc1d8d0
AL
121 if (Counter % 100 == 0 && Progress != 0)
122 Progress->Progress(List.Offset());
8ce4327b 123
578bfd0a
AL
124 /* Get a pointer to the version structure. We know the list is sorted
125 so we use that fact in the search. Insertion of new versions is
126 done with correct sorting */
127 string Version = List.Version();
f55a958f
AL
128 if (Version.empty() == true)
129 {
130 if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false)
080bf1be 131 return _error->Error(_("Error occurred while processing %s (UsePackage1)"),
7a3c2ab0 132 PackageName.c_str());
f55a958f
AL
133 continue;
134 }
135
578bfd0a 136 pkgCache::VerIterator Ver = Pkg.VersionList();
349cd3b8 137 map_ptrloc *Last = &Pkg->VersionList;
2246928b 138 int Res = 1;
f55a958f 139 for (; Ver.end() == false; Last = &Ver->NextVer, Ver++)
578bfd0a 140 {
c24972cb 141 Res = Cache.VS->CmpVersion(Version,Ver.VerStr());
578bfd0a
AL
142 if (Res >= 0)
143 break;
144 }
145
146 /* We already have a version for this item, record that we
147 saw it */
204fbdcc
AL
148 unsigned long Hash = List.VersionHash();
149 if (Res == 0 && Ver->Hash == Hash)
578bfd0a 150 {
f55a958f 151 if (List.UsePackage(Pkg,Ver) == false)
080bf1be 152 return _error->Error(_("Error occurred while processing %s (UsePackage2)"),
7a3c2ab0 153 PackageName.c_str());
f78439bf 154
578bfd0a 155 if (NewFileVer(Ver,List) == false)
080bf1be 156 return _error->Error(_("Error occurred while processing %s (NewFileVer1)"),
7a3c2ab0 157 PackageName.c_str());
578bfd0a 158
ddc1d8d0
AL
159 // Read only a single record and return
160 if (OutVer != 0)
161 {
162 *OutVer = Ver;
45415543 163 FoundFileDeps |= List.HasFileDeps();
ddc1d8d0
AL
164 return true;
165 }
166
578bfd0a
AL
167 continue;
168 }
169
204fbdcc
AL
170 // Skip to the end of the same version set.
171 if (Res == 0)
172 {
173 for (; Ver.end() == false; Last = &Ver->NextVer, Ver++)
174 {
c24972cb 175 Res = Cache.VS->CmpVersion(Version,Ver.VerStr());
204fbdcc
AL
176 if (Res != 0)
177 break;
178 }
179 }
180
578bfd0a 181 // Add a new version
f55a958f
AL
182 *Last = NewVersion(Ver,Version,*Last);
183 Ver->ParentPkg = Pkg.Index();
204fbdcc 184 Ver->Hash = Hash;
578bfd0a 185 if (List.NewVersion(Ver) == false)
080bf1be 186 return _error->Error(_("Error occurred while processing %s (NewVersion1)"),
7a3c2ab0 187 PackageName.c_str());
0149949b 188
f55a958f 189 if (List.UsePackage(Pkg,Ver) == false)
080bf1be 190 return _error->Error(_("Error occurred while processing %s (UsePackage3)"),
7a3c2ab0 191 PackageName.c_str());
f55a958f 192
578bfd0a 193 if (NewFileVer(Ver,List) == false)
080bf1be 194 return _error->Error(_("Error occurred while processing %s (NewVersion2)"),
7a3c2ab0 195 PackageName.c_str());
ddc1d8d0
AL
196
197 // Read only a single record and return
198 if (OutVer != 0)
199 {
200 *OutVer = Ver;
45415543 201 FoundFileDeps |= List.HasFileDeps();
ddc1d8d0
AL
202 return true;
203 }
578bfd0a 204 }
0149949b 205
45415543
AL
206 FoundFileDeps |= List.HasFileDeps();
207
6a3da7a6
AL
208 if (Cache.HeaderP->PackageCount >= (1ULL<<sizeof(Cache.PkgP->ID)*8)-1)
209 return _error->Error(_("Wow, you exceeded the number of package "
210 "names this APT is capable of."));
211 if (Cache.HeaderP->VersionCount >= (1ULL<<(sizeof(Cache.VerP->ID)*8))-1)
212 return _error->Error(_("Wow, you exceeded the number of versions "
213 "this APT is capable of."));
214 if (Cache.HeaderP->DependsCount >= (1ULL<<(sizeof(Cache.DepP->ID)*8))-1ULL)
215 return _error->Error(_("Wow, you exceeded the number of dependencies "
216 "this APT is capable of."));
578bfd0a
AL
217 return true;
218}
219 /*}}}*/
45415543
AL
220// CacheGenerator::MergeFileProvides - Merge file provides /*{{{*/
221// ---------------------------------------------------------------------
222/* If we found any file depends while parsing the main list we need to
223 resolve them. Since it is undesired to load the entire list of files
224 into the cache as virtual packages we do a two stage effort. MergeList
225 identifies the file depends and this creates Provdies for them by
226 re-parsing all the indexs. */
227bool pkgCacheGenerator::MergeFileProvides(ListParser &List)
228{
229 List.Owner = this;
230
231 unsigned int Counter = 0;
232 while (List.Step() == true)
233 {
234 string PackageName = List.Package();
235 if (PackageName.empty() == true)
236 return false;
237 string Version = List.Version();
238 if (Version.empty() == true)
239 continue;
240
241 pkgCache::PkgIterator Pkg = Cache.FindPkg(PackageName);
242 if (Pkg.end() == true)
080bf1be 243 return _error->Error(_("Error occurred while processing %s (FindPkg)"),
45415543
AL
244 PackageName.c_str());
245 Counter++;
246 if (Counter % 100 == 0 && Progress != 0)
247 Progress->Progress(List.Offset());
248
249 unsigned long Hash = List.VersionHash();
250 pkgCache::VerIterator Ver = Pkg.VersionList();
251 for (; Ver.end() == false; Ver++)
252 {
253 if (Ver->Hash == Hash && Version.c_str() == Ver.VerStr())
254 {
255 if (List.CollectFileProvides(Cache,Ver) == false)
080bf1be 256 return _error->Error(_("Error occurred while processing %s (CollectFileProvides)"),PackageName.c_str());
45415543
AL
257 break;
258 }
259 }
260
261 if (Ver.end() == true)
262 _error->Warning(_("Package %s %s was not found while processing file dependencies"),PackageName.c_str(),Version.c_str());
263 }
264
265 return true;
266}
267 /*}}}*/
578bfd0a
AL
268// CacheGenerator::NewPackage - Add a new package /*{{{*/
269// ---------------------------------------------------------------------
270/* This creates a new package structure and adds it to the hash table */
171c75f1 271bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,const string &Name)
578bfd0a 272{
8efa2a3b
AL
273 Pkg = Cache.FindPkg(Name);
274 if (Pkg.end() == false)
275 return true;
276
578bfd0a
AL
277 // Get a structure
278 unsigned long Package = Map.Allocate(sizeof(pkgCache::Package));
279 if (Package == 0)
280 return false;
281
f55a958f 282 Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package);
578bfd0a
AL
283
284 // Insert it into the hash table
f55a958f 285 unsigned long Hash = Cache.Hash(Name);
578bfd0a
AL
286 Pkg->NextPackage = Cache.HeaderP->HashTable[Hash];
287 Cache.HeaderP->HashTable[Hash] = Package;
288
289 // Set the name and the ID
290 Pkg->Name = Map.WriteString(Name);
291 if (Pkg->Name == 0)
292 return false;
293 Pkg->ID = Cache.HeaderP->PackageCount++;
294
295 return true;
296}
297 /*}}}*/
298// CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
299// ---------------------------------------------------------------------
300/* */
f55a958f 301bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
578bfd0a
AL
302 ListParser &List)
303{
ddc1d8d0
AL
304 if (CurrentFile == 0)
305 return true;
306
dcb79bae
AL
307 // Get a structure
308 unsigned long VerFile = Map.Allocate(sizeof(pkgCache::VerFile));
309 if (VerFile == 0)
310 return 0;
311
312 pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
313 VF->File = CurrentFile - Cache.PkgFileP;
03e39e59
AL
314
315 // Link it to the end of the list
349cd3b8 316 map_ptrloc *Last = &Ver->FileList;
03e39e59
AL
317 for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; V++)
318 Last = &V->NextFile;
319 VF->NextFile = *Last;
320 *Last = VF.Index();
321
dcb79bae
AL
322 VF->Offset = List.Offset();
323 VF->Size = List.Size();
ad00ae81
AL
324 if (Cache.HeaderP->MaxVerFileSize < VF->Size)
325 Cache.HeaderP->MaxVerFileSize = VF->Size;
a7e66b17
AL
326 Cache.HeaderP->VerFileCount++;
327
f55a958f 328 return true;
578bfd0a
AL
329}
330 /*}}}*/
331// CacheGenerator::NewVersion - Create a new Version /*{{{*/
332// ---------------------------------------------------------------------
f55a958f 333/* This puts a version structure in the linked list */
578bfd0a 334unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
171c75f1 335 const string &VerStr,
578bfd0a
AL
336 unsigned long Next)
337{
f55a958f
AL
338 // Get a structure
339 unsigned long Version = Map.Allocate(sizeof(pkgCache::Version));
340 if (Version == 0)
0149949b 341 return 0;
f55a958f
AL
342
343 // Fill it in
344 Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
f55a958f
AL
345 Ver->NextVer = Next;
346 Ver->ID = Cache.HeaderP->VersionCount++;
347 Ver->VerStr = Map.WriteString(VerStr);
348 if (Ver->VerStr == 0)
0149949b 349 return 0;
f55a958f 350
0149949b 351 return Version;
578bfd0a
AL
352}
353 /*}}}*/
dcb79bae
AL
354// ListParser::NewDepends - Create a dependency element /*{{{*/
355// ---------------------------------------------------------------------
356/* This creates a dependency element in the tree. It is linked to the
357 version and to the package that it is pointing to. */
358bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
171c75f1
MV
359 const string &PackageName,
360 const string &Version,
dcb79bae
AL
361 unsigned int Op,
362 unsigned int Type)
363{
364 pkgCache &Cache = Owner->Cache;
365
366 // Get a structure
367 unsigned long Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency));
368 if (Dependency == 0)
369 return false;
370
371 // Fill it in
372 pkgCache::DepIterator Dep(Cache,Cache.DepP + Dependency);
373 Dep->ParentVer = Ver.Index();
374 Dep->Type = Type;
375 Dep->CompareOp = Op;
376 Dep->ID = Cache.HeaderP->DependsCount++;
377
378 // Locate the target package
8efa2a3b
AL
379 pkgCache::PkgIterator Pkg;
380 if (Owner->NewPackage(Pkg,PackageName) == false)
381 return false;
dcb79bae
AL
382
383 // Probe the reverse dependency list for a version string that matches
384 if (Version.empty() == false)
385 {
b2e465d6 386/* for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++)
dcb79bae 387 if (I->Version != 0 && I.TargetVer() == Version)
f9eec0e7 388 Dep->Version = I->Version;*/
dcb79bae
AL
389 if (Dep->Version == 0)
390 if ((Dep->Version = WriteString(Version)) == 0)
391 return false;
392 }
c1a22377 393
dcb79bae
AL
394 // Link it to the package
395 Dep->Package = Pkg.Index();
396 Dep->NextRevDepends = Pkg->RevDepends;
397 Pkg->RevDepends = Dep.Index();
398
c1a22377
AL
399 /* Link it to the version (at the end of the list)
400 Caching the old end point speeds up generation substantially */
f9eec0e7 401 if (OldDepVer != Ver)
c1a22377 402 {
f9eec0e7 403 OldDepLast = &Ver->DependsList;
c1a22377 404 for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
f9eec0e7
AL
405 OldDepLast = &D->NextDepends;
406 OldDepVer = Ver;
c1a22377 407 }
45415543
AL
408
409 // Is it a file dependency?
410 if (PackageName[0] == '/')
411 FoundFileDeps = true;
dcb79bae 412
f9eec0e7
AL
413 Dep->NextDepends = *OldDepLast;
414 *OldDepLast = Dep.Index();
415 OldDepLast = &Dep->NextDepends;
c1a22377 416
dcb79bae
AL
417 return true;
418}
419 /*}}}*/
420// ListParser::NewProvides - Create a Provides element /*{{{*/
421// ---------------------------------------------------------------------
422/* */
423bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver,
171c75f1
MV
424 const string &PackageName,
425 const string &Version)
dcb79bae
AL
426{
427 pkgCache &Cache = Owner->Cache;
8efa2a3b
AL
428
429 // We do not add self referencing provides
430 if (Ver.ParentPkg().Name() == PackageName)
431 return true;
dcb79bae
AL
432
433 // Get a structure
434 unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides));
435 if (Provides == 0)
436 return false;
a7e66b17 437 Cache.HeaderP->ProvidesCount++;
dcb79bae
AL
438
439 // Fill it in
440 pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
441 Prv->Version = Ver.Index();
442 Prv->NextPkgProv = Ver->ProvidesList;
443 Ver->ProvidesList = Prv.Index();
b2e465d6 444 if (Version.empty() == false && (Prv->ProvideVersion = WriteString(Version)) == 0)
dcb79bae
AL
445 return false;
446
447 // Locate the target package
8efa2a3b
AL
448 pkgCache::PkgIterator Pkg;
449 if (Owner->NewPackage(Pkg,PackageName) == false)
450 return false;
dcb79bae
AL
451
452 // Link it to the package
453 Prv->ParentPkg = Pkg.Index();
454 Prv->NextProvides = Pkg->ProvidesList;
455 Pkg->ProvidesList = Prv.Index();
456
457 return true;
458}
459 /*}}}*/
578bfd0a
AL
460// CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/
461// ---------------------------------------------------------------------
462/* This is used to select which file is to be associated with all newly
b2e465d6 463 added versions. The caller is responsible for setting the IMS fields. */
171c75f1 464bool pkgCacheGenerator::SelectFile(const string &File,const string &Site,
b2e465d6
AL
465 const pkgIndexFile &Index,
466 unsigned long Flags)
578bfd0a 467{
578bfd0a
AL
468 // Get some space for the structure
469 CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile));
470 if (CurrentFile == Cache.PkgFileP)
471 return false;
472
473 // Fill it in
474 CurrentFile->FileName = Map.WriteString(File);
b2e465d6 475 CurrentFile->Site = WriteUniqString(Site);
578bfd0a
AL
476 CurrentFile->NextFile = Cache.HeaderP->FileList;
477 CurrentFile->Flags = Flags;
e1b74f61 478 CurrentFile->ID = Cache.HeaderP->PackageFileCount;
b2e465d6 479 CurrentFile->IndexType = WriteUniqString(Index.GetType()->Label);
578bfd0a 480 PkgFileName = File;
ad00ae81 481 Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
b35d2f5f 482 Cache.HeaderP->PackageFileCount++;
b2e465d6 483
578bfd0a
AL
484 if (CurrentFile->FileName == 0)
485 return false;
404ec98e 486
ddc1d8d0 487 if (Progress != 0)
b2e465d6 488 Progress->SubProgress(Index.Size());
8efa2a3b 489 return true;
578bfd0a
AL
490}
491 /*}}}*/
f55a958f
AL
492// CacheGenerator::WriteUniqueString - Insert a unique string /*{{{*/
493// ---------------------------------------------------------------------
494/* This is used to create handles to strings. Given the same text it
495 always returns the same number */
496unsigned long pkgCacheGenerator::WriteUniqString(const char *S,
497 unsigned int Size)
498{
f9eec0e7
AL
499 /* We use a very small transient hash table here, this speeds up generation
500 by a fair amount on slower machines */
501 pkgCache::StringItem *&Bucket = UniqHash[(S[0]*5 + S[1]) % _count(UniqHash)];
502 if (Bucket != 0 &&
503 stringcmp(S,S+Size,Cache.StrP + Bucket->String) == 0)
504 return Bucket->String;
505
f55a958f
AL
506 // Search for an insertion point
507 pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList;
508 int Res = 1;
349cd3b8 509 map_ptrloc *Last = &Cache.HeaderP->StringList;
f55a958f
AL
510 for (; I != Cache.StringItemP; Last = &I->NextItem,
511 I = Cache.StringItemP + I->NextItem)
512 {
9c14e3d6 513 Res = stringcmp(S,S+Size,Cache.StrP + I->String);
f55a958f
AL
514 if (Res >= 0)
515 break;
516 }
517
518 // Match
519 if (Res == 0)
f9eec0e7
AL
520 {
521 Bucket = I;
0149949b 522 return I->String;
f9eec0e7 523 }
f55a958f
AL
524
525 // Get a structure
526 unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem));
527 if (Item == 0)
0149949b
AL
528 return 0;
529
f55a958f
AL
530 // Fill in the structure
531 pkgCache::StringItem *ItemP = Cache.StringItemP + Item;
532 ItemP->NextItem = I - Cache.StringItemP;
533 *Last = Item;
534 ItemP->String = Map.WriteString(S,Size);
535 if (ItemP->String == 0)
0149949b 536 return 0;
f55a958f 537
f9eec0e7 538 Bucket = ItemP;
0149949b 539 return ItemP->String;
f55a958f
AL
540}
541 /*}}}*/
b35d2f5f 542
b2e465d6 543// CheckValidity - Check that a cache is up-to-date /*{{{*/
b35d2f5f 544// ---------------------------------------------------------------------
b2e465d6
AL
545/* This just verifies that each file in the list of index files exists,
546 has matching attributes with the cache and the cache does not have
547 any extra files. */
171c75f1 548static bool CheckValidity(const string &CacheFile, FileIterator Start,
e7b470ee 549 FileIterator End,MMap **OutMap = 0)
b35d2f5f 550{
b2e465d6
AL
551 // No file, certainly invalid
552 if (CacheFile.empty() == true || FileExists(CacheFile) == false)
b35d2f5f
AL
553 return false;
554
b2e465d6 555 // Map it
b35d2f5f 556 FileFd CacheF(CacheFile,FileFd::ReadOnly);
b2e465d6 557 SPtr<MMap> Map = new MMap(CacheF,MMap::Public | MMap::ReadOnly);
b35d2f5f 558 pkgCache Cache(Map);
b2e465d6 559 if (_error->PendingError() == true || Map->Size() == 0)
b35d2f5f
AL
560 {
561 _error->Discard();
562 return false;
563 }
b35d2f5f 564
b2e465d6
AL
565 /* Now we check every index file, see if it is in the cache,
566 verify the IMS data and check that it is on the disk too.. */
567 SPtrArray<bool> Visited = new bool[Cache.HeaderP->PackageFileCount];
568 memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount);
569 for (; Start != End; Start++)
a77ad7c3 570 {
b2e465d6
AL
571 if ((*Start)->HasPackages() == false)
572 continue;
a77ad7c3 573
b2e465d6 574 if ((*Start)->Exists() == false)
b35d2f5f 575 {
b2e465d6
AL
576 _error->WarningE("stat",_("Couldn't stat source package list %s"),
577 (*Start)->Describe().c_str());
578 continue;
b35d2f5f 579 }
b2e465d6
AL
580
581 // FindInCache is also expected to do an IMS check.
582 pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache);
583 if (File.end() == true)
b35d2f5f 584 return false;
b2e465d6
AL
585
586 Visited[File->ID] = true;
b35d2f5f
AL
587 }
588
b2e465d6
AL
589 for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
590 if (Visited[I] == false)
591 return false;
b35d2f5f 592
b35d2f5f
AL
593 if (_error->PendingError() == true)
594 {
595 _error->Discard();
596 return false;
597 }
b35d2f5f 598
b2e465d6
AL
599 if (OutMap != 0)
600 *OutMap = Map.UnGuard();
b35d2f5f
AL
601 return true;
602}
603 /*}}}*/
b2e465d6 604// ComputeSize - Compute the total size of a bunch of files /*{{{*/
b35d2f5f 605// ---------------------------------------------------------------------
b2e465d6
AL
606/* Size is kind of an abstract notion that is only used for the progress
607 meter */
e7b470ee 608static unsigned long ComputeSize(FileIterator Start,FileIterator End)
b35d2f5f 609{
b2e465d6
AL
610 unsigned long TotalSize = 0;
611 for (; Start != End; Start++)
b35d2f5f 612 {
b2e465d6
AL
613 if ((*Start)->HasPackages() == false)
614 continue;
615 TotalSize += (*Start)->Size();
b35d2f5f 616 }
b2e465d6 617 return TotalSize;
2d11135a
AL
618}
619 /*}}}*/
b2e465d6 620// BuildCache - Merge the list of index files into the cache /*{{{*/
2d11135a 621// ---------------------------------------------------------------------
b2e465d6
AL
622/* */
623static bool BuildCache(pkgCacheGenerator &Gen,
624 OpProgress &Progress,
625 unsigned long &CurrentSize,unsigned long TotalSize,
e7b470ee 626 FileIterator Start, FileIterator End)
2d11135a 627{
45415543
AL
628 FileIterator I;
629 for (I = Start; I != End; I++)
2d11135a 630 {
45415543 631 if ((*I)->HasPackages() == false)
2d11135a
AL
632 continue;
633
45415543 634 if ((*I)->Exists() == false)
2d11135a 635 continue;
b2e465d6 636
45415543 637 if ((*I)->FindInCache(Gen.GetCache()).end() == false)
a77ad7c3
AL
638 {
639 _error->Warning("Duplicate sources.list entry %s",
45415543 640 (*I)->Describe().c_str());
a77ad7c3
AL
641 continue;
642 }
643
45415543 644 unsigned long Size = (*I)->Size();
db0db9fe 645 Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading package lists"));
b2e465d6 646 CurrentSize += Size;
2d11135a 647
45415543 648 if ((*I)->Merge(Gen,Progress) == false)
b2e465d6
AL
649 return false;
650 }
45415543
AL
651
652 if (Gen.HasFileDeps() == true)
653 {
654 Progress.Done();
655 TotalSize = ComputeSize(Start, End);
656 CurrentSize = 0;
657 for (I = Start; I != End; I++)
658 {
659 unsigned long Size = (*I)->Size();
660 Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Collecting File Provides"));
661 CurrentSize += Size;
662 if ((*I)->MergeFileProvides(Gen,Progress) == false)
663 return false;
664 }
665 }
2d11135a 666
b35d2f5f
AL
667 return true;
668}
669 /*}}}*/
b2e465d6 670// MakeStatusCache - Construct the status cache /*{{{*/
b35d2f5f 671// ---------------------------------------------------------------------
b2e465d6
AL
672/* This makes sure that the status cache (the cache that has all
673 index files from the sources list and all local ones) is ready
674 to be mmaped. If OutMap is not zero then a MMap object representing
675 the cache will be stored there. This is pretty much mandetory if you
676 are using AllowMem. AllowMem lets the function be run as non-root
677 where it builds the cache 'fast' into a memory buffer. */
678bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
679 MMap **OutMap,bool AllowMem)
b35d2f5f 680{
ac13a427 681 unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*1024*1024);
67db871e 682
7db98ffc
MZ
683 vector<pkgIndexFile *> Files;
684 for (vector<metaIndex *>::const_iterator i = List.begin();
685 i != List.end();
686 i++)
687 {
688 vector <pkgIndexFile *> *Indexes = (*i)->GetIndexFiles();
689 for (vector<pkgIndexFile *>::const_iterator j = Indexes->begin();
690 j != Indexes->end();
691 j++)
692 Files.push_back (*j);
693 }
694
b2e465d6
AL
695 unsigned long EndOfSource = Files.size();
696 if (_system->AddStatusFiles(Files) == false)
697 return false;
8ce4327b 698
b2e465d6 699 // Decide if we can write to the files..
3b5421b4 700 string CacheFile = _config->FindFile("Dir::Cache::pkgcache");
b2e465d6
AL
701 string SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
702
703 // Decide if we can write to the cache
704 bool Writeable = false;
705 if (CacheFile.empty() == false)
706 Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0;
707 else
708 if (SrcCacheFile.empty() == false)
709 Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
710
711 if (Writeable == false && AllowMem == false && CacheFile.empty() == false)
712 return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str());
713
db0db9fe 714 Progress.OverallProgress(0,1,1,_("Reading package lists"));
b2e465d6
AL
715
716 // Cache is OK, Fin.
717 if (CheckValidity(CacheFile,Files.begin(),Files.end(),OutMap) == true)
718 {
db0db9fe 719 Progress.OverallProgress(1,1,1,_("Reading package lists"));
b2e465d6
AL
720 return true;
721 }
722
723 /* At this point we know we need to reconstruct the package cache,
724 begin. */
725 SPtr<FileFd> CacheF;
726 SPtr<DynamicMMap> Map;
727 if (Writeable == true && CacheFile.empty() == false)
728 {
729 unlink(CacheFile.c_str());
730 CacheF = new FileFd(CacheFile,FileFd::WriteEmpty);
7a3c2ab0 731 fchmod(CacheF->Fd(),0644);
b2e465d6 732 Map = new DynamicMMap(*CacheF,MMap::Public,MapSize);
b35d2f5f
AL
733 if (_error->PendingError() == true)
734 return false;
b35d2f5f 735 }
b2e465d6 736 else
8ce4327b 737 {
b2e465d6
AL
738 // Just build it in memory..
739 Map = new DynamicMMap(MMap::Public,MapSize);
8ce4327b 740 }
b35d2f5f 741
b2e465d6 742 // Lets try the source cache.
b35d2f5f 743 unsigned long CurrentSize = 0;
b2e465d6
AL
744 unsigned long TotalSize = 0;
745 if (CheckValidity(SrcCacheFile,Files.begin(),
746 Files.begin()+EndOfSource) == true)
2d11135a 747 {
b2e465d6
AL
748 // Preload the map with the source cache
749 FileFd SCacheF(SrcCacheFile,FileFd::ReadOnly);
750 if (SCacheF.Read((unsigned char *)Map->Data() + Map->RawAllocate(SCacheF.Size()),
751 SCacheF.Size()) == false)
752 return false;
753
754 TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
2d11135a 755
b2e465d6
AL
756 // Build the status cache
757 pkgCacheGenerator Gen(Map.Get(),&Progress);
2d11135a 758 if (_error->PendingError() == true)
b2e465d6
AL
759 return false;
760 if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
761 Files.begin()+EndOfSource,Files.end()) == false)
762 return false;
763 }
764 else
2d11135a 765 {
b2e465d6 766 TotalSize = ComputeSize(Files.begin(),Files.end());
2d11135a 767
b2e465d6
AL
768 // Build the source cache
769 pkgCacheGenerator Gen(Map.Get(),&Progress);
770 if (_error->PendingError() == true)
771 return false;
772 if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
773 Files.begin(),Files.begin()+EndOfSource) == false)
774 return false;
2d11135a 775
b2e465d6
AL
776 // Write it back
777 if (Writeable == true && SrcCacheFile.empty() == false)
2d11135a 778 {
b2e465d6
AL
779 FileFd SCacheF(SrcCacheFile,FileFd::WriteEmpty);
780 if (_error->PendingError() == true)
781 return false;
7a3c2ab0
AL
782
783 fchmod(SCacheF.Fd(),0644);
784
b2e465d6
AL
785 // Write out the main data
786 if (SCacheF.Write(Map->Data(),Map->Size()) == false)
787 return _error->Error(_("IO Error saving source cache"));
788 SCacheF.Sync();
789
790 // Write out the proper header
791 Gen.GetCache().HeaderP->Dirty = false;
792 if (SCacheF.Seek(0) == false ||
793 SCacheF.Write(Map->Data(),sizeof(*Gen.GetCache().HeaderP)) == false)
794 return _error->Error(_("IO Error saving source cache"));
b2e465d6 795 Gen.GetCache().HeaderP->Dirty = true;
7a3c2ab0 796 SCacheF.Sync();
2d11135a
AL
797 }
798
b2e465d6
AL
799 // Build the status cache
800 if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
801 Files.begin()+EndOfSource,Files.end()) == false)
802 return false;
2d11135a
AL
803 }
804
b2e465d6
AL
805 if (_error->PendingError() == true)
806 return false;
807 if (OutMap != 0)
2d11135a 808 {
b2e465d6 809 if (CacheF != 0)
2d11135a 810 {
b2e465d6
AL
811 delete Map.UnGuard();
812 *OutMap = new MMap(*CacheF,MMap::Public | MMap::ReadOnly);
2d11135a 813 }
b2e465d6
AL
814 else
815 {
816 *OutMap = Map.UnGuard();
817 }
2d11135a
AL
818 }
819
b2e465d6
AL
820 return true;
821}
822 /*}}}*/
823// MakeOnlyStatusCache - Build a cache with just the status files /*{{{*/
824// ---------------------------------------------------------------------
825/* */
826bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap)
827{
ac13a427 828 unsigned long MapSize = _config->FindI("APT::Cache-Limit",8*1024*1024);
b2e465d6
AL
829 vector<pkgIndexFile *> Files;
830 unsigned long EndOfSource = Files.size();
831 if (_system->AddStatusFiles(Files) == false)
832 return false;
833
834 SPtr<DynamicMMap> Map;
835 Map = new DynamicMMap(MMap::Public,MapSize);
836 unsigned long CurrentSize = 0;
837 unsigned long TotalSize = 0;
838
839 TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
840
841 // Build the status cache
db0db9fe 842 Progress.OverallProgress(0,1,1,_("Reading package lists"));
b2e465d6 843 pkgCacheGenerator Gen(Map.Get(),&Progress);
2d11135a 844 if (_error->PendingError() == true)
b2e465d6
AL
845 return false;
846 if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
847 Files.begin()+EndOfSource,Files.end()) == false)
848 return false;
2d11135a 849
b2e465d6
AL
850 if (_error->PendingError() == true)
851 return false;
852 *OutMap = Map.UnGuard();
2d11135a 853
b2e465d6 854 return true;
2d11135a
AL
855}
856 /*}}}*/