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