]>
git.saurik.com Git - apt.git/blob - apt-pkg/pkgcachegen.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: pkgcachegen.cc,v 1.46 2001/02/20 07:03:17 jgg Exp $
4 /* ######################################################################
6 Package Cache Generator - Generator for the cache structure.
8 This builds the cache structure from the abstract package list parser.
10 ##################################################################### */
12 // Include Files /*{{{*/
14 #pragma implementation "apt-pkg/pkgcachegen.h"
17 #define APT_COMPATIBILITY 986
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>
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
)
45 memset(UniqHash
,0,sizeof(UniqHash
));
47 if (_error
->PendingError() == true)
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]));
58 *Cache
.HeaderP
= pkgCache::Header();
59 Cache
.HeaderP
->VerSysName
= Map
.WriteString(_system
->VS
->Label
);
60 Cache
.HeaderP
->Architecture
= Map
.WriteString(_config
->Find("APT::Architecture"));
65 // Map directly from the existing file
67 Map
.UsePools(*Cache
.HeaderP
->Pools
,sizeof(Cache
.HeaderP
->Pools
)/sizeof(Cache
.HeaderP
->Pools
[0]));
68 if (Cache
.VS
!= _system
->VS
)
70 _error
->Error(_("Cache has an incompatible versioning system"));
75 Cache
.HeaderP
->Dirty
= true;
76 Map
.Sync(0,sizeof(pkgCache::Header
));
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()
85 if (_error
->PendingError() == true)
87 if (Map
.Sync() == false)
90 Cache
.HeaderP
->Dirty
= false;
91 Map
.Sync(0,sizeof(pkgCache::Header
));
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
)
103 unsigned int Counter
= 0;
104 while (List
.Step() == true)
106 // Get a pointer to the package structure
107 string PackageName
= List
.Package();
108 if (PackageName
.empty() == true)
111 pkgCache::PkgIterator Pkg
;
112 if (NewPackage(Pkg
,PackageName
) == false)
113 return _error
->Error(_("Error occured while processing %s (NewPackage)"),PackageName
.c_str());
115 if (Counter
% 100 == 0 && Progress
!= 0)
116 Progress
->Progress(List
.Offset());
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)
124 if (List
.UsePackage(Pkg
,pkgCache::VerIterator(Cache
)) == false)
125 return _error
->Error(_("Error occured while processing %s (UsePackage1)"),PackageName
.c_str());
129 pkgCache::VerIterator Ver
= Pkg
.VersionList();
130 map_ptrloc
*Last
= &Pkg
->VersionList
;
132 for (; Ver
.end() == false; Last
= &Ver
->NextVer
, Ver
++)
134 Res
= Cache
.VS
->DoCmpVersion(Version
.begin(),Version
.end(),Ver
.VerStr(),
135 Ver
.VerStr() + strlen(Ver
.VerStr()));
140 /* We already have a version for this item, record that we
142 unsigned long Hash
= List
.VersionHash();
143 if (Res
== 0 && Ver
->Hash
== Hash
)
145 if (List
.UsePackage(Pkg
,Ver
) == false)
146 return _error
->Error(_("Error occured while processing %s (UsePackage2)"),PackageName
.c_str());
148 if (NewFileVer(Ver
,List
) == false)
149 return _error
->Error(_("Error occured while processing %s (NewFileVer1)"),PackageName
.c_str());
151 // Read only a single record and return
161 // Skip to the end of the same version set.
164 for (; Ver
.end() == false; Last
= &Ver
->NextVer
, Ver
++)
166 Res
= Cache
.VS
->DoCmpVersion(Version
.begin(),Version
.end(),Ver
.VerStr(),
167 Ver
.VerStr() + strlen(Ver
.VerStr()));
174 *Last
= NewVersion(Ver
,Version
,*Last
);
175 Ver
->ParentPkg
= Pkg
.Index();
177 if (List
.NewVersion(Ver
) == false)
178 return _error
->Error(_("Error occured while processing %s (NewVersion1)"),PackageName
.c_str());
180 if (List
.UsePackage(Pkg
,Ver
) == false)
181 return _error
->Error(_("Error occured while processing %s (UsePackage3)"),PackageName
.c_str());
183 if (NewFileVer(Ver
,List
) == false)
184 return _error
->Error(_("Error occured while processing %s (NewVersion2)"),PackageName
.c_str());
186 // Read only a single record and return
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
)
202 Pkg
= Cache
.FindPkg(Name
);
203 if (Pkg
.end() == false)
207 unsigned long Package
= Map
.Allocate(sizeof(pkgCache::Package
));
211 Pkg
= pkgCache::PkgIterator(Cache
,Cache
.PkgP
+ Package
);
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
;
218 // Set the name and the ID
219 Pkg
->Name
= Map
.WriteString(Name
);
222 Pkg
->ID
= Cache
.HeaderP
->PackageCount
++;
227 // CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
228 // ---------------------------------------------------------------------
230 bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator
&Ver
,
233 if (CurrentFile
== 0)
237 unsigned long VerFile
= Map
.Allocate(sizeof(pkgCache::VerFile
));
241 pkgCache::VerFileIterator
VF(Cache
,Cache
.VerFileP
+ VerFile
);
242 VF
->File
= CurrentFile
- Cache
.PkgFileP
;
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
++)
248 VF
->NextFile
= *Last
;
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
++;
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
,
268 unsigned long Version
= Map
.Allocate(sizeof(pkgCache::Version
));
273 Ver
= pkgCache::VerIterator(Cache
,Cache
.VerP
+ Version
);
275 Ver
->ID
= Cache
.HeaderP
->VersionCount
++;
276 Ver
->VerStr
= Map
.WriteString(VerStr
);
277 if (Ver
->VerStr
== 0)
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
,
293 pkgCache
&Cache
= Owner
->Cache
;
296 unsigned long Dependency
= Owner
->Map
.Allocate(sizeof(pkgCache::Dependency
));
301 pkgCache::DepIterator
Dep(Cache
,Cache
.DepP
+ Dependency
);
302 Dep
->ParentVer
= Ver
.Index();
305 Dep
->ID
= Cache
.HeaderP
->DependsCount
++;
307 // Locate the target package
308 pkgCache::PkgIterator Pkg
;
309 if (Owner
->NewPackage(Pkg
,PackageName
) == false)
312 // Probe the reverse dependency list for a version string that matches
313 if (Version
.empty() == false)
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)
323 // Link it to the package
324 Dep
->Package
= Pkg
.Index();
325 Dep
->NextRevDepends
= Pkg
->RevDepends
;
326 Pkg
->RevDepends
= Dep
.Index();
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
)
332 OldDepLast
= &Ver
->DependsList
;
333 for (pkgCache::DepIterator D
= Ver
.DependsList(); D
.end() == false; D
++)
334 OldDepLast
= &D
->NextDepends
;
338 Dep
->NextDepends
= *OldDepLast
;
339 *OldDepLast
= Dep
.Index();
340 OldDepLast
= &Dep
->NextDepends
;
345 // ListParser::NewProvides - Create a Provides element /*{{{*/
346 // ---------------------------------------------------------------------
348 bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver
,
352 pkgCache
&Cache
= Owner
->Cache
;
354 // We do not add self referencing provides
355 if (Ver
.ParentPkg().Name() == PackageName
)
359 unsigned long Provides
= Owner
->Map
.Allocate(sizeof(pkgCache::Provides
));
362 Cache
.HeaderP
->ProvidesCount
++;
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)
372 // Locate the target package
373 pkgCache::PkgIterator Pkg
;
374 if (Owner
->NewPackage(Pkg
,PackageName
) == false)
377 // Link it to the package
378 Prv
->ParentPkg
= Pkg
.Index();
379 Prv
->NextProvides
= Pkg
->ProvidesList
;
380 Pkg
->ProvidesList
= Prv
.Index();
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
,
393 // Get some space for the structure
394 CurrentFile
= Cache
.PkgFileP
+ Map
.Allocate(sizeof(*CurrentFile
));
395 if (CurrentFile
== Cache
.PkgFileP
)
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
);
406 Cache
.HeaderP
->FileList
= CurrentFile
- Cache
.PkgFileP
;
407 Cache
.HeaderP
->PackageFileCount
++;
409 if (CurrentFile
->FileName
== 0)
413 Progress
->SubProgress(Index
.Size());
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
,
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
)];
428 stringcmp(S
,S
+Size
,Cache
.StrP
+ Bucket
->String
) == 0)
429 return Bucket
->String
;
431 // Search for an insertion point
432 pkgCache::StringItem
*I
= Cache
.StringItemP
+ Cache
.HeaderP
->StringList
;
434 map_ptrloc
*Last
= &Cache
.HeaderP
->StringList
;
435 for (; I
!= Cache
.StringItemP
; Last
= &I
->NextItem
,
436 I
= Cache
.StringItemP
+ I
->NextItem
)
438 Res
= stringcmp(S
,S
+Size
,Cache
.StrP
+ I
->String
);
451 unsigned long Item
= Map
.Allocate(sizeof(pkgCache::StringItem
));
455 // Fill in the structure
456 pkgCache::StringItem
*ItemP
= Cache
.StringItemP
+ Item
;
457 ItemP
->NextItem
= I
- Cache
.StringItemP
;
459 ItemP
->String
= Map
.WriteString(S
,Size
);
460 if (ItemP
->String
== 0)
464 return ItemP
->String
;
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
473 static bool CheckValidity(string CacheFile
,pkgIndexFile
**Start
,
474 pkgIndexFile
**End
,MMap
**OutMap
= 0)
476 // No file, certainly invalid
477 if (CacheFile
.empty() == true || FileExists(CacheFile
) == false)
481 FileFd
CacheF(CacheFile
,FileFd::ReadOnly
);
482 SPtr
<MMap
> Map
= new MMap(CacheF
,MMap::Public
| MMap::ReadOnly
);
484 if (_error
->PendingError() == true || Map
->Size() == 0)
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
++)
496 if ((*Start
)->HasPackages() == false)
499 if ((*Start
)->Exists() == false)
501 _error
->WarningE("stat",_("Couldn't stat source package list %s"),
502 (*Start
)->Describe().c_str());
506 // FindInCache is also expected to do an IMS check.
507 pkgCache::PkgFileIterator File
= (*Start
)->FindInCache(Cache
);
508 if (File
.end() == true)
511 Visited
[File
->ID
] = true;
514 for (unsigned I
= 0; I
!= Cache
.HeaderP
->PackageFileCount
; I
++)
515 if (Visited
[I
] == false)
518 if (_error
->PendingError() == true)
525 *OutMap
= Map
.UnGuard();
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
533 static unsigned long ComputeSize(pkgIndexFile
**Start
,pkgIndexFile
**End
)
535 unsigned long TotalSize
= 0;
536 for (; Start
!= End
; Start
++)
538 if ((*Start
)->HasPackages() == false)
540 TotalSize
+= (*Start
)->Size();
545 // BuildCache - Merge the list of index files into the cache /*{{{*/
546 // ---------------------------------------------------------------------
548 static bool BuildCache(pkgCacheGenerator
&Gen
,
549 OpProgress
&Progress
,
550 unsigned long &CurrentSize
,unsigned long TotalSize
,
551 pkgIndexFile
**Start
,pkgIndexFile
**End
)
553 for (; Start
!= End
; Start
++)
555 if ((*Start
)->HasPackages() == false)
558 if ((*Start
)->Exists() == false)
561 unsigned long Size
= (*Start
)->Size();
562 Progress
.OverallProgress(CurrentSize
,TotalSize
,Size
,_("Reading Package Lists"));
565 if ((*Start
)->Merge(Gen
,Progress
) == false)
572 // MakeStatusCache - Construct the status cache /*{{{*/
573 // ---------------------------------------------------------------------
574 /* This makes sure that the status cache (the cache that has all
575 index files from the sources list and all local ones) is ready
576 to be mmaped. If OutMap is not zero then a MMap object representing
577 the cache will be stored there. This is pretty much mandetory if you
578 are using AllowMem. AllowMem lets the function be run as non-root
579 where it builds the cache 'fast' into a memory buffer. */
580 bool pkgMakeStatusCache(pkgSourceList
&List
,OpProgress
&Progress
,
581 MMap
**OutMap
,bool AllowMem
)
583 unsigned long MapSize
= _config
->FindI("APT::Cache-Limit",4*1024*1024);
585 vector
<pkgIndexFile
*> Files(List
.begin(),List
.end());
586 unsigned long EndOfSource
= Files
.size();
587 if (_system
->AddStatusFiles(Files
) == false)
590 // Decide if we can write to the files..
591 string CacheFile
= _config
->FindFile("Dir::Cache::pkgcache");
592 string SrcCacheFile
= _config
->FindFile("Dir::Cache::srcpkgcache");
594 // Decide if we can write to the cache
595 bool Writeable
= false;
596 if (CacheFile
.empty() == false)
597 Writeable
= access(flNotFile(CacheFile
).c_str(),W_OK
) == 0;
599 if (SrcCacheFile
.empty() == false)
600 Writeable
= access(flNotFile(SrcCacheFile
).c_str(),W_OK
) == 0;
602 if (Writeable
== false && AllowMem
== false && CacheFile
.empty() == false)
603 return _error
->Error(_("Unable to write to %s"),flNotFile(CacheFile
).c_str());
605 Progress
.OverallProgress(0,1,1,_("Reading Package Lists"));
608 if (CheckValidity(CacheFile
,Files
.begin(),Files
.end(),OutMap
) == true)
610 Progress
.OverallProgress(1,1,1,_("Reading Package Lists"));
614 /* At this point we know we need to reconstruct the package cache,
617 SPtr
<DynamicMMap
> Map
;
618 if (Writeable
== true && CacheFile
.empty() == false)
620 unlink(CacheFile
.c_str());
621 CacheF
= new FileFd(CacheFile
,FileFd::WriteEmpty
);
622 Map
= new DynamicMMap(*CacheF
,MMap::Public
,MapSize
);
623 if (_error
->PendingError() == true)
628 // Just build it in memory..
629 Map
= new DynamicMMap(MMap::Public
,MapSize
);
632 // Lets try the source cache.
633 unsigned long CurrentSize
= 0;
634 unsigned long TotalSize
= 0;
635 if (CheckValidity(SrcCacheFile
,Files
.begin(),
636 Files
.begin()+EndOfSource
) == true)
638 // Preload the map with the source cache
639 FileFd
SCacheF(SrcCacheFile
,FileFd::ReadOnly
);
640 if (SCacheF
.Read((unsigned char *)Map
->Data() + Map
->RawAllocate(SCacheF
.Size()),
641 SCacheF
.Size()) == false)
644 TotalSize
= ComputeSize(Files
.begin()+EndOfSource
,Files
.end());
646 // Build the status cache
647 pkgCacheGenerator
Gen(Map
.Get(),&Progress
);
648 if (_error
->PendingError() == true)
650 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
651 Files
.begin()+EndOfSource
,Files
.end()) == false)
656 TotalSize
= ComputeSize(Files
.begin(),Files
.end());
658 // Build the source cache
659 pkgCacheGenerator
Gen(Map
.Get(),&Progress
);
660 if (_error
->PendingError() == true)
662 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
663 Files
.begin(),Files
.begin()+EndOfSource
) == false)
667 if (Writeable
== true && SrcCacheFile
.empty() == false)
669 FileFd
SCacheF(SrcCacheFile
,FileFd::WriteEmpty
);
670 if (_error
->PendingError() == true)
672 // Write out the main data
673 if (SCacheF
.Write(Map
->Data(),Map
->Size()) == false)
674 return _error
->Error(_("IO Error saving source cache"));
677 // Write out the proper header
678 Gen
.GetCache().HeaderP
->Dirty
= false;
679 if (SCacheF
.Seek(0) == false ||
680 SCacheF
.Write(Map
->Data(),sizeof(*Gen
.GetCache().HeaderP
)) == false)
681 return _error
->Error(_("IO Error saving source cache"));
683 Gen
.GetCache().HeaderP
->Dirty
= true;
686 // Build the status cache
687 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
688 Files
.begin()+EndOfSource
,Files
.end()) == false)
692 if (_error
->PendingError() == true)
698 delete Map
.UnGuard();
699 *OutMap
= new MMap(*CacheF
,MMap::Public
| MMap::ReadOnly
);
703 *OutMap
= Map
.UnGuard();
710 // MakeOnlyStatusCache - Build a cache with just the status files /*{{{*/
711 // ---------------------------------------------------------------------
713 bool pkgMakeOnlyStatusCache(OpProgress
&Progress
,DynamicMMap
**OutMap
)
715 unsigned long MapSize
= _config
->FindI("APT::Cache-Limit",4*1024*1024);
716 vector
<pkgIndexFile
*> Files
;
717 unsigned long EndOfSource
= Files
.size();
718 if (_system
->AddStatusFiles(Files
) == false)
721 SPtr
<DynamicMMap
> Map
;
722 Map
= new DynamicMMap(MMap::Public
,MapSize
);
723 unsigned long CurrentSize
= 0;
724 unsigned long TotalSize
= 0;
726 TotalSize
= ComputeSize(Files
.begin()+EndOfSource
,Files
.end());
728 // Build the status cache
729 Progress
.OverallProgress(0,1,1,_("Reading Package Lists"));
730 pkgCacheGenerator
Gen(Map
.Get(),&Progress
);
731 if (_error
->PendingError() == true)
733 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
734 Files
.begin()+EndOfSource
,Files
.end()) == false)
737 if (_error
->PendingError() == true)
739 *OutMap
= Map
.UnGuard();