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