More progress updates
[ntk/apt.git] / apt-pkg / pkgcachegen.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: pkgcachegen.cc,v 1.14 1998/08/26 04:52:23 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 #include <apt-pkg/pkgcachegen.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/version.h>
20 #include <apt-pkg/progress.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/deblistparser.h>
24
25 #include <strutl.h>
26
27 #include <sys/stat.h>
28 #include <unistd.h>
29 /*}}}*/
30
31 // CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/
32 // ---------------------------------------------------------------------
33 /* We set the diry flag and make sure that is written to the disk */
34 pkgCacheGenerator::pkgCacheGenerator(DynamicMMap &Map,OpProgress &Prog) :
35 Map(Map), Cache(Map), Progress(Prog)
36 {
37 if (_error->PendingError() == true)
38 return;
39
40 if (Map.Size() == 0)
41 {
42 Map.RawAllocate(sizeof(pkgCache::Header));
43 *Cache.HeaderP = pkgCache::Header();
44 }
45 Cache.HeaderP->Dirty = true;
46 Map.Sync(0,sizeof(pkgCache::Header));
47 Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
48 }
49 /*}}}*/
50 // CacheGenerator::~pkgCacheGenerator - Destructor /*{{{*/
51 // ---------------------------------------------------------------------
52 /* We sync the data then unset the dirty flag in two steps so as to
53 advoid a problem during a crash */
54 pkgCacheGenerator::~pkgCacheGenerator()
55 {
56 if (_error->PendingError() == true)
57 return;
58 if (Map.Sync() == false)
59 return;
60
61 Cache.HeaderP->Dirty = false;
62 Map.Sync(0,sizeof(pkgCache::Header));
63 }
64 /*}}}*/
65 // CacheGenerator::MergeList - Merge the package list /*{{{*/
66 // ---------------------------------------------------------------------
67 /* This provides the generation of the entries in the cache. Each loop
68 goes through a single package record from the underlying parse engine. */
69 bool pkgCacheGenerator::MergeList(ListParser &List)
70 {
71 List.Owner = this;
72
73 while (List.Step() == true)
74 {
75 // Get a pointer to the package structure
76 string PackageName = List.Package();
77 pkgCache::PkgIterator Pkg;
78 if (NewPackage(Pkg,PackageName) == false)
79 return _error->Error("Error occured while processing %s (NewPackage)",PackageName.c_str());
80 Progress.Progress(List.Offset());
81
82 /* Get a pointer to the version structure. We know the list is sorted
83 so we use that fact in the search. Insertion of new versions is
84 done with correct sorting */
85 string Version = List.Version();
86 if (Version.empty() == true)
87 {
88 if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false)
89 return _error->Error("Error occured while processing %s (UsePackage1)",PackageName.c_str());
90 continue;
91 }
92
93 pkgCache::VerIterator Ver = Pkg.VersionList();
94 unsigned long *Last = &Pkg->VersionList;
95 int Res = 1;
96 for (; Ver.end() == false; Last = &Ver->NextVer, Ver++)
97 {
98 Res = pkgVersionCompare(Version.begin(),Version.end(),Ver.VerStr(),
99 Ver.VerStr() + strlen(Ver.VerStr()));
100 if (Res >= 0)
101 break;
102 }
103
104 /* We already have a version for this item, record that we
105 saw it */
106 if (Res == 0)
107 {
108 if (List.UsePackage(Pkg,Ver) == false)
109 return _error->Error("Error occured while processing %s (UsePackage2)",PackageName.c_str());
110
111 if (NewFileVer(Ver,List) == false)
112 return _error->Error("Error occured while processing %s (NewFileVer1)",PackageName.c_str());
113
114 continue;
115 }
116
117 // Add a new version
118 *Last = NewVersion(Ver,Version,*Last);
119 Ver->ParentPkg = Pkg.Index();
120 if (List.NewVersion(Ver) == false)
121 return _error->Error("Error occured while processing %s (NewVersion1)",PackageName.c_str());
122
123 if (List.UsePackage(Pkg,Ver) == false)
124 return _error->Error("Error occured while processing %s (UsePackage3)",PackageName.c_str());
125
126 if (NewFileVer(Ver,List) == false)
127 return _error->Error("Error occured while processing %s (NewVersion2)",PackageName.c_str());
128 }
129
130 return true;
131 }
132 /*}}}*/
133 // CacheGenerator::NewPackage - Add a new package /*{{{*/
134 // ---------------------------------------------------------------------
135 /* This creates a new package structure and adds it to the hash table */
136 bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,string Name)
137 {
138 Pkg = Cache.FindPkg(Name);
139 if (Pkg.end() == false)
140 return true;
141
142 // Get a structure
143 unsigned long Package = Map.Allocate(sizeof(pkgCache::Package));
144 if (Package == 0)
145 return false;
146
147 Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package);
148
149 // Insert it into the hash table
150 unsigned long Hash = Cache.Hash(Name);
151 Pkg->NextPackage = Cache.HeaderP->HashTable[Hash];
152 Cache.HeaderP->HashTable[Hash] = Package;
153
154 // Set the name and the ID
155 Pkg->Name = Map.WriteString(Name);
156 if (Pkg->Name == 0)
157 return false;
158 Pkg->ID = Cache.HeaderP->PackageCount++;
159
160 return true;
161 }
162 /*}}}*/
163 // CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
164 // ---------------------------------------------------------------------
165 /* */
166 bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
167 ListParser &List)
168 {
169 // Get a structure
170 unsigned long VerFile = Map.Allocate(sizeof(pkgCache::VerFile));
171 if (VerFile == 0)
172 return 0;
173
174 pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
175 VF->File = CurrentFile - Cache.PkgFileP;
176 VF->NextFile = Ver->FileList;
177 Ver->FileList = VF.Index();
178 VF->Offset = List.Offset();
179 VF->Size = List.Size();
180 if (Cache.HeaderP->MaxVerFileSize < VF->Size)
181 Cache.HeaderP->MaxVerFileSize = VF->Size;
182 return true;
183 }
184 /*}}}*/
185 // CacheGenerator::NewVersion - Create a new Version /*{{{*/
186 // ---------------------------------------------------------------------
187 /* This puts a version structure in the linked list */
188 unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
189 string VerStr,
190 unsigned long Next)
191 {
192 // Get a structure
193 unsigned long Version = Map.Allocate(sizeof(pkgCache::Version));
194 if (Version == 0)
195 return 0;
196
197 // Fill it in
198 Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
199 Ver->NextVer = Next;
200 Ver->ID = Cache.HeaderP->VersionCount++;
201 Ver->VerStr = Map.WriteString(VerStr);
202 if (Ver->VerStr == 0)
203 return 0;
204
205 return Version;
206 }
207 /*}}}*/
208 // ListParser::NewDepends - Create a dependency element /*{{{*/
209 // ---------------------------------------------------------------------
210 /* This creates a dependency element in the tree. It is linked to the
211 version and to the package that it is pointing to. */
212 bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
213 string PackageName,
214 string Version,
215 unsigned int Op,
216 unsigned int Type)
217 {
218 pkgCache &Cache = Owner->Cache;
219
220 // Get a structure
221 unsigned long Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency));
222 if (Dependency == 0)
223 return false;
224
225 // Fill it in
226 pkgCache::DepIterator Dep(Cache,Cache.DepP + Dependency);
227 Dep->ParentVer = Ver.Index();
228 Dep->Type = Type;
229 Dep->CompareOp = Op;
230 Dep->ID = Cache.HeaderP->DependsCount++;
231
232 // Locate the target package
233 pkgCache::PkgIterator Pkg;
234 if (Owner->NewPackage(Pkg,PackageName) == false)
235 return false;
236
237 // Probe the reverse dependency list for a version string that matches
238 if (Version.empty() == false)
239 {
240 for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++)
241 if (I->Version != 0 && I.TargetVer() == Version)
242 Dep->Version = I->Version;
243 if (Dep->Version == 0)
244 if ((Dep->Version = WriteString(Version)) == 0)
245 return false;
246 }
247
248 // Link it to the package
249 Dep->Package = Pkg.Index();
250 Dep->NextRevDepends = Pkg->RevDepends;
251 Pkg->RevDepends = Dep.Index();
252
253 // Link it to the version (at the end of the list)
254 unsigned long *Last = &Ver->DependsList;
255 for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
256 Last = &D->NextDepends;
257 Dep->NextDepends = *Last;
258 *Last = Dep.Index();
259
260 return true;
261 }
262 /*}}}*/
263 // ListParser::NewProvides - Create a Provides element /*{{{*/
264 // ---------------------------------------------------------------------
265 /* */
266 bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver,
267 string PackageName,
268 string Version)
269 {
270 pkgCache &Cache = Owner->Cache;
271
272 // We do not add self referencing provides
273 if (Ver.ParentPkg().Name() == PackageName)
274 return true;
275
276 // Get a structure
277 unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides));
278 if (Provides == 0)
279 return false;
280
281 // Fill it in
282 pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
283 Prv->Version = Ver.Index();
284 Prv->NextPkgProv = Ver->ProvidesList;
285 Ver->ProvidesList = Prv.Index();
286 if (Version.empty() == false && (Prv->Version = WriteString(Version)) == 0)
287 return false;
288
289 // Locate the target package
290 pkgCache::PkgIterator Pkg;
291 if (Owner->NewPackage(Pkg,PackageName) == false)
292 return false;
293
294 // Link it to the package
295 Prv->ParentPkg = Pkg.Index();
296 Prv->NextProvides = Pkg->ProvidesList;
297 Pkg->ProvidesList = Prv.Index();
298
299 return true;
300 }
301 /*}}}*/
302 // CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/
303 // ---------------------------------------------------------------------
304 /* This is used to select which file is to be associated with all newly
305 added versions. */
306 bool pkgCacheGenerator::SelectFile(string File,unsigned long Flags)
307 {
308 struct stat Buf;
309 if (stat(File.c_str(),&Buf) == -1)
310 return _error->Errno("stat","Couldn't stat ",File.c_str());
311
312 // Get some space for the structure
313 CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile));
314 if (CurrentFile == Cache.PkgFileP)
315 return false;
316
317 // Fill it in
318 CurrentFile->FileName = Map.WriteString(File);
319 CurrentFile->Size = Buf.st_size;
320 CurrentFile->mtime = Buf.st_mtime;
321 CurrentFile->NextFile = Cache.HeaderP->FileList;
322 CurrentFile->Flags = Flags;
323 PkgFileName = File;
324 Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
325 Cache.HeaderP->PackageFileCount++;
326
327 if (CurrentFile->FileName == 0)
328 return false;
329
330 Progress.SubProgress(Buf.st_size);
331 return true;
332 }
333 /*}}}*/
334 // CacheGenerator::WriteUniqueString - Insert a unique string /*{{{*/
335 // ---------------------------------------------------------------------
336 /* This is used to create handles to strings. Given the same text it
337 always returns the same number */
338 unsigned long pkgCacheGenerator::WriteUniqString(const char *S,
339 unsigned int Size)
340 {
341 // Search for an insertion point
342 pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList;
343 int Res = 1;
344 unsigned long *Last = &Cache.HeaderP->StringList;
345 for (; I != Cache.StringItemP; Last = &I->NextItem,
346 I = Cache.StringItemP + I->NextItem)
347 {
348 Res = stringcmp(S,S+Size,Cache.StrP + I->String);
349 if (Res >= 0)
350 break;
351 }
352
353 // Match
354 if (Res == 0)
355 return I->String;
356
357 // Get a structure
358 unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem));
359 if (Item == 0)
360 return 0;
361
362 // Fill in the structure
363 pkgCache::StringItem *ItemP = Cache.StringItemP + Item;
364 ItemP->NextItem = I - Cache.StringItemP;
365 *Last = Item;
366 ItemP->String = Map.WriteString(S,Size);
367 if (ItemP->String == 0)
368 return 0;
369
370 return ItemP->String;
371 }
372 /*}}}*/
373
374 // SrcCacheCheck - Check if the source package cache is uptodate /*{{{*/
375 // ---------------------------------------------------------------------
376 /* The source cache is checked against the source list and the files
377 on disk, any difference results in a false. */
378 bool pkgSrcCacheCheck(pkgSourceList &List)
379 {
380 if (_error->PendingError() == true)
381 return false;
382
383 // Open the source package cache
384 string CacheFile = _config->FindDir("Dir::Cache::srcpkgcache");
385 string ListDir = _config->FindDir("Dir::State::lists");
386 if (FileExists(CacheFile) == false)
387 return false;
388
389 FileFd CacheF(CacheFile,FileFd::ReadOnly);
390 if (_error->PendingError() == true)
391 {
392 _error->Discard();
393 return false;
394 }
395
396 MMap Map(CacheF,MMap::Public | MMap::ReadOnly);
397 if (_error->PendingError() == true)
398 {
399 _error->Discard();
400 return false;
401 }
402
403 pkgCache Cache(Map);
404 if (_error->PendingError() == true)
405 {
406 _error->Discard();
407 return false;
408 }
409
410 // They are certianly out of sync
411 if (Cache.Head().PackageFileCount != List.size())
412 return false;
413
414 for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++)
415 {
416 // Search for a match in the source list
417 bool Bad = true;
418 for (pkgSourceList::const_iterator I = List.begin();
419 I != List.end(); I++)
420 {
421 string File = ListDir + URItoFileName(I->PackagesURI());
422 if (F.FileName() == File)
423 {
424 Bad = false;
425 break;
426 }
427 }
428
429 // Check if the file matches what was cached
430 Bad |= !F.IsOk();
431 if (Bad == true)
432 return false;
433 }
434
435 return true;
436 }
437 /*}}}*/
438 // PkgCacheCheck - Check if the package cache is uptodate /*{{{*/
439 // ---------------------------------------------------------------------
440 /* This does a simple check of all files used to compose the cache */
441 bool pkgPkgCacheCheck(string CacheFile)
442 {
443 if (_error->PendingError() == true)
444 return false;
445
446 // Open the source package cache
447 if (FileExists(CacheFile) == false)
448 return false;
449
450 FileFd CacheF(CacheFile,FileFd::ReadOnly);
451 if (_error->PendingError() == true)
452 {
453 _error->Discard();
454 return false;
455 }
456
457 MMap Map(CacheF,MMap::Public | MMap::ReadOnly);
458 if (_error->PendingError() == true)
459 {
460 _error->Discard();
461 return false;
462 }
463
464 pkgCache Cache(Map);
465 if (_error->PendingError() == true)
466 {
467 _error->Discard();
468 return false;
469 }
470
471 // Cheack each file
472 for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++)
473 if (F.IsOk() == false)
474 return false;
475 return true;
476 }
477 /*}}}*/
478 // AddSourcesSize - Add the size of the status files /*{{{*/
479 // ---------------------------------------------------------------------
480 /* This adds the size of all the status files to the size counter */
481 static bool pkgAddSourcesSize(unsigned long &TotalSize)
482 {
483 // Grab the file names
484 string xstatus = _config->FindDir("Dir::State::xstatus");
485 string userstatus = _config->FindDir("Dir::State::userstatus");
486 string status = _config->FindDir("Dir::State::status");
487
488 // Grab the sizes
489 struct stat Buf;
490 if (stat(xstatus.c_str(),&Buf) == 0)
491 TotalSize += Buf.st_size;
492 if (stat(userstatus.c_str(),&Buf) == 0)
493 TotalSize += Buf.st_size;
494 if (stat(status.c_str(),&Buf) != 0)
495 return _error->Errno("stat","Couldn't stat the status file %s",status.c_str());
496 TotalSize += Buf.st_size;
497
498 return true;
499 }
500 /*}}}*/
501 // MergeStatus - Add the status files to the cache /*{{{*/
502 // ---------------------------------------------------------------------
503 /* This adds the status files to the map */
504 static bool pkgMergeStatus(OpProgress &Progress,pkgCacheGenerator &Gen,
505 unsigned long &CurrentSize,unsigned long TotalSize)
506 {
507 // Grab the file names
508 string Status[3];
509 Status[0] = _config->FindDir("Dir::State::xstatus");
510 Status[1]= _config->FindDir("Dir::State::userstatus");
511 Status[2] = _config->FindDir("Dir::State::status");
512
513 for (int I = 0; I != 3; I++)
514 {
515 // Check if the file exists and it is not the primary status file.
516 string File = Status[I];
517 if (I != 2 && FileExists(File) == false)
518 continue;
519
520 FileFd Pkg(File,FileFd::ReadOnly);
521 debListParser Parser(Pkg);
522 Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),"Reading Package Lists");
523 if (_error->PendingError() == true)
524 return _error->Error("Problem opening %s",File.c_str());
525 CurrentSize += Pkg.Size();
526
527 Progress.SubProgress(0,"Local Package State - " + flNotDir(File));
528 if (Gen.SelectFile(File,pkgCache::Flag::NotSource) == false)
529 return _error->Error("Problem with SelectFile %s",File.c_str());
530
531 if (Gen.MergeList(Parser) == false)
532 return _error->Error("Problem with MergeList %s",File.c_str());
533 Progress.Progress(Pkg.Size());
534 }
535
536 return true;
537 }
538 /*}}}*/
539 // MakeStatusCache - Generates a cache that includes the status files /*{{{*/
540 // ---------------------------------------------------------------------
541 /* This copies the package source cache and then merges the status and
542 xstatus files into it. */
543 bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress)
544 {
545 Progress.OverallProgress(0,1,1,"Reading Package Lists");
546
547 string CacheFile = _config->FindDir("Dir::Cache::pkgcache");
548 bool SrcOk = pkgSrcCacheCheck(List);
549 bool PkgOk = pkgPkgCacheCheck(CacheFile);
550
551 // Rebuild the source and package caches
552 if (SrcOk == false)
553 {
554 string SCacheFile = _config->FindDir("Dir::Cache::srcpkgcache");
555 string ListDir = _config->FindDir("Dir::State::lists");
556
557 FileFd SCacheF(SCacheFile,FileFd::WriteEmpty);
558 FileFd CacheF(CacheFile,FileFd::WriteEmpty);
559 DynamicMMap Map(CacheF,MMap::Public);
560 if (_error->PendingError() == true)
561 return false;
562
563 pkgCacheGenerator Gen(Map,Progress);
564
565 // Prepare the progress indicator
566 unsigned long TotalSize = 0;
567 struct stat Buf;
568 for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++)
569 {
570 string File = ListDir + URItoFileName(I->PackagesURI());
571 if (stat(File.c_str(),&Buf) != 0)
572 return _error->Errno("stat","Couldn't stat source package list %s",File.c_str());
573 TotalSize += Buf.st_size;
574 }
575
576 if (pkgAddSourcesSize(TotalSize) == false)
577 return false;
578
579 // Generate the pkg source cache
580 unsigned long CurrentSize = 0;
581 for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++)
582 {
583 string File = ListDir + URItoFileName(I->PackagesURI());
584 FileFd Pkg(File,FileFd::ReadOnly);
585 debListParser Parser(Pkg);
586 Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),"Reading Package Lists");
587 if (_error->PendingError() == true)
588 return _error->Error("Problem opening %s",File.c_str());
589 CurrentSize += Pkg.Size();
590
591 Progress.SubProgress(0,I->PackagesInfo());
592 if (Gen.SelectFile(File) == false)
593 return _error->Error("Problem with SelectFile %s",File.c_str());
594
595 if (Gen.MergeList(Parser) == false)
596 return _error->Error("Problem with MergeList %s",File.c_str());
597 }
598
599 // Write the src cache
600 Gen.GetCache().HeaderP->Dirty = false;
601 if (SCacheF.Write(Map.Data(),Map.Size()) == false)
602 return _error->Error("IO Error saving source cache");
603 Gen.GetCache().HeaderP->Dirty = true;
604
605 // Merge in the source caches
606 return pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize);
607 }
608
609 if (PkgOk == true)
610 {
611 Progress.OverallProgress(1,1,1,"Reading Package Lists");
612 return true;
613 }
614
615 // We use the source cache to generate the package cache
616 string SCacheFile = _config->FindDir("Dir::Cache::srcpkgcache");
617
618 FileFd SCacheF(SCacheFile,FileFd::ReadOnly);
619 FileFd CacheF(CacheFile,FileFd::WriteEmpty);
620 DynamicMMap Map(CacheF,MMap::Public);
621 if (_error->PendingError() == true)
622 return false;
623
624 // Preload the map with the source cache
625 if (SCacheF.Read((unsigned char *)Map.Data() + Map.RawAllocate(SCacheF.Size()),
626 SCacheF.Size()) == false)
627 return false;
628
629 pkgCacheGenerator Gen(Map,Progress);
630
631 // Compute the progress
632 unsigned long TotalSize = 0;
633 if (pkgAddSourcesSize(TotalSize) == false)
634 return false;
635
636 unsigned long CurrentSize = 0;
637 return pkgMergeStatus(Progress,Gen,CurrentSize,TotalSize);
638 }
639 /*}}}*/