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