]>
git.saurik.com Git - apt.git/blob - apt-pkg/pkgcachegen.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: pkgcachegen.cc,v 1.47 2001/03/04 00:12:41 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 if ((*Start
)->FindInCache(Gen
.GetCache()).end() == false)
563 _error
->Warning("Duplicate sources.list entry %s",
564 (*Start
)->Describe().c_str());
568 unsigned long Size
= (*Start
)->Size();
569 Progress
.OverallProgress(CurrentSize
,TotalSize
,Size
,_("Reading Package Lists"));
572 if ((*Start
)->Merge(Gen
,Progress
) == false)
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
)
590 unsigned long MapSize
= _config
->FindI("APT::Cache-Limit",6*1024*1024);
592 vector
<pkgIndexFile
*> Files(List
.begin(),List
.end());
593 unsigned long EndOfSource
= Files
.size();
594 if (_system
->AddStatusFiles(Files
) == false)
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");
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;
606 if (SrcCacheFile
.empty() == false)
607 Writeable
= access(flNotFile(SrcCacheFile
).c_str(),W_OK
) == 0;
609 if (Writeable
== false && AllowMem
== false && CacheFile
.empty() == false)
610 return _error
->Error(_("Unable to write to %s"),flNotFile(CacheFile
).c_str());
612 Progress
.OverallProgress(0,1,1,_("Reading Package Lists"));
615 if (CheckValidity(CacheFile
,Files
.begin(),Files
.end(),OutMap
) == true)
617 Progress
.OverallProgress(1,1,1,_("Reading Package Lists"));
621 /* At this point we know we need to reconstruct the package cache,
624 SPtr
<DynamicMMap
> Map
;
625 if (Writeable
== true && CacheFile
.empty() == false)
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)
635 // Just build it in memory..
636 Map
= new DynamicMMap(MMap::Public
,MapSize
);
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)
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)
651 TotalSize
= ComputeSize(Files
.begin()+EndOfSource
,Files
.end());
653 // Build the status cache
654 pkgCacheGenerator
Gen(Map
.Get(),&Progress
);
655 if (_error
->PendingError() == true)
657 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
658 Files
.begin()+EndOfSource
,Files
.end()) == false)
663 TotalSize
= ComputeSize(Files
.begin(),Files
.end());
665 // Build the source cache
666 pkgCacheGenerator
Gen(Map
.Get(),&Progress
);
667 if (_error
->PendingError() == true)
669 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
670 Files
.begin(),Files
.begin()+EndOfSource
) == false)
674 if (Writeable
== true && SrcCacheFile
.empty() == false)
676 FileFd
SCacheF(SrcCacheFile
,FileFd::WriteEmpty
);
677 if (_error
->PendingError() == true)
679 // Write out the main data
680 if (SCacheF
.Write(Map
->Data(),Map
->Size()) == false)
681 return _error
->Error(_("IO Error saving source cache"));
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"));
690 Gen
.GetCache().HeaderP
->Dirty
= true;
693 // Build the status cache
694 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
695 Files
.begin()+EndOfSource
,Files
.end()) == false)
699 if (_error
->PendingError() == true)
705 delete Map
.UnGuard();
706 *OutMap
= new MMap(*CacheF
,MMap::Public
| MMap::ReadOnly
);
710 *OutMap
= Map
.UnGuard();
717 // MakeOnlyStatusCache - Build a cache with just the status files /*{{{*/
718 // ---------------------------------------------------------------------
720 bool pkgMakeOnlyStatusCache(OpProgress
&Progress
,DynamicMMap
**OutMap
)
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)
728 SPtr
<DynamicMMap
> Map
;
729 Map
= new DynamicMMap(MMap::Public
,MapSize
);
730 unsigned long CurrentSize
= 0;
731 unsigned long TotalSize
= 0;
733 TotalSize
= ComputeSize(Files
.begin()+EndOfSource
,Files
.end());
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)
740 if (BuildCache(Gen
,Progress
,CurrentSize
,TotalSize
,
741 Files
.begin()+EndOfSource
,Files
.end()) == false)
744 if (_error
->PendingError() == true)
746 *OutMap
= Map
.UnGuard();