]> git.saurik.com Git - apt.git/blob - apt-inst/filelist.cc
Bug 87390
[apt.git] / apt-inst / filelist.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: filelist.cc,v 1.2 2001/02/20 07:03:16 jgg Exp $
4 /* ######################################################################
5
6 File Listing - Manages a Cache of File -> Package names.
7
8 Diversions add some signficant complexity to the system. To keep
9 storage space down in the very special case of a diverted file no
10 extra bytes are allocated in the Node structure. Instead a diversion
11 is inserted directly into the hash table and its flag bit set. Every
12 lookup for that filename will always return the diversion.
13
14 The hash buckets are stored in sorted form, with diversions having
15 the higest sort order. Identical files are assigned the same file
16 pointer, thus after a search all of the nodes owning that file can be
17 found by iterating down the bucket.
18
19 Re-updates of diversions (another extremely special case) are done by
20 marking all diversions as untouched, then loading the entire diversion
21 list again, touching each diversion and then finally going back and
22 releasing all untouched diversions. It is assumed that the diversion
23 table will always be quite small and be a very irregular case.
24
25 Diversions that are user-installed are represented by a package with
26 an empty name string.
27
28 Conf files are handled like diversions by changing the meaning of the
29 Pointer field to point to a conf file entry - again to reduce over
30 head for a special case.
31
32 ##################################################################### */
33 /*}}}*/
34 // Include Files /*{{{*/
35 #ifdef __GNUG__
36 #pragma implementation "apt-pkg/filelist.h"
37 #endif
38
39 #include <apt-pkg/filelist.h>
40 #include <apt-pkg/mmap.h>
41 #include <apt-pkg/error.h>
42 #include <apt-pkg/strutl.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <iostream>
48 /*}}}*/
49
50 // FlCache::Header::Header - Constructor /*{{{*/
51 // ---------------------------------------------------------------------
52 /* Initialize the header variables. These are the defaults used when
53 creating new caches */
54 pkgFLCache::Header::Header()
55 {
56 Signature = 0xEA3F1295;
57
58 /* Whenever the structures change the major version should be bumped,
59 whenever the generator changes the minor version should be bumped. */
60 MajorVersion = 1;
61 MinorVersion = 0;
62 Dirty = true;
63
64 HeaderSz = sizeof(pkgFLCache::Header);
65 NodeSz = sizeof(pkgFLCache::Node);
66 DirSz = sizeof(pkgFLCache::Directory);
67 PackageSz = sizeof(pkgFLCache::Package);
68 DiversionSz = sizeof(pkgFLCache::Diversion);
69 ConfFileSz = sizeof(pkgFLCache::ConfFile);
70
71 NodeCount = 0;
72 DirCount = 0;
73 PackageCount = 0;
74 DiversionCount = 0;
75 ConfFileCount = 0;
76 HashSize = 1 << 14;
77
78 FileHash = 0;
79 DirTree = 0;
80 Packages = 0;
81 Diversions = 0;
82 UniqNodes = 0;
83 memset(Pools,0,sizeof(Pools));
84 }
85 /*}}}*/
86 // FLCache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
87 // ---------------------------------------------------------------------
88 /* Compare to make sure we are matching versions */
89 bool pkgFLCache::Header::CheckSizes(Header &Against) const
90 {
91 if (HeaderSz == Against.HeaderSz &&
92 NodeSz == Against.NodeSz &&
93 DirSz == Against.DirSz &&
94 DiversionSz == Against.DiversionSz &&
95 PackageSz == Against.PackageSz &&
96 ConfFileSz == Against.ConfFileSz)
97 return true;
98 return false;
99 }
100 /*}}}*/
101
102 // FLCache::pkgFLCache - Constructor /*{{{*/
103 // ---------------------------------------------------------------------
104 /* If this is a new cache then a new header and hash table are instantaited
105 otherwise the existing ones are mearly attached */
106 pkgFLCache::pkgFLCache(DynamicMMap &Map) : Map(Map)
107 {
108 if (_error->PendingError() == true)
109 return;
110
111 LastTreeLookup = 0;
112 LastLookupSize = 0;
113
114 // Apply the typecasts
115 HeaderP = (Header *)Map.Data();
116 NodeP = (Node *)Map.Data();
117 DirP = (Directory *)Map.Data();
118 DiverP = (Diversion *)Map.Data();
119 PkgP = (Package *)Map.Data();
120 ConfP = (ConfFile *)Map.Data();
121 StrP = (char *)Map.Data();
122 AnyP = (unsigned char *)Map.Data();
123
124 // New mapping, create the basic cache structures
125 if (Map.Size() == 0)
126 {
127 Map.RawAllocate(sizeof(pkgFLCache::Header));
128 *HeaderP = pkgFLCache::Header();
129 HeaderP->FileHash = Map.RawAllocate(sizeof(pkgFLCache::Node)*HeaderP->HashSize,
130 sizeof(pkgFLCache::Node))/sizeof(pkgFLCache::Node);
131 }
132
133 FileHash = NodeP + HeaderP->FileHash;
134
135 // Setup the dynamic map manager
136 HeaderP->Dirty = true;
137 Map.Sync(0,sizeof(pkgFLCache::Header));
138 Map.UsePools(*HeaderP->Pools,sizeof(HeaderP->Pools)/sizeof(HeaderP->Pools[0]));
139 }
140 /*}}}*/
141 // FLCache::TreeLookup - Perform a lookup in a generic tree /*{{{*/
142 // ---------------------------------------------------------------------
143 /* This is a simple generic tree lookup. The first three entries of
144 the Directory structure are used as a template, but any other similar
145 structure could be used in it's place. */
146 map_ptrloc pkgFLCache::TreeLookup(map_ptrloc *Base,const char *Text,
147 const char *TextEnd,unsigned long Size,
148 unsigned int *Count,bool Insert)
149 {
150 pkgFLCache::Directory *Dir;
151
152 // Check our last entry cache
153 if (LastTreeLookup != 0 && LastLookupSize == Size)
154 {
155 Dir = (pkgFLCache::Directory *)(AnyP + LastTreeLookup*Size);
156 if (stringcmp(Text,TextEnd,StrP + Dir->Name) == 0)
157 return LastTreeLookup;
158 }
159
160 while (1)
161 {
162 // Allocate a new one
163 if (*Base == 0)
164 {
165 if (Insert == false)
166 return 0;
167
168 *Base = Map.Allocate(Size);
169 if (*Base == 0)
170 return 0;
171
172 (*Count)++;
173 Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
174 Dir->Name = Map.WriteString(Text,TextEnd - Text);
175 LastTreeLookup = *Base;
176 LastLookupSize = Size;
177 return *Base;
178 }
179
180 // Compare this node
181 Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
182 int Res = stringcmp(Text,TextEnd,StrP + Dir->Name);
183 if (Res == 0)
184 {
185 LastTreeLookup = *Base;
186 LastLookupSize = Size;
187 return *Base;
188 }
189
190 if (Res > 0)
191 Base = &Dir->Left;
192 if (Res < 0)
193 Base = &Dir->Right;
194 }
195 }
196 /*}}}*/
197 // FLCache::PrintTree - Print out a tree /*{{{*/
198 // ---------------------------------------------------------------------
199 /* This is a simple generic tree dumper, ment for debugging. */
200 void pkgFLCache::PrintTree(map_ptrloc Base,unsigned long Size)
201 {
202 if (Base == 0)
203 return;
204
205 pkgFLCache::Directory *Dir = (pkgFLCache::Directory *)(AnyP + Base*Size);
206 PrintTree(Dir->Left,Size);
207 cout << (StrP + Dir->Name) << endl;
208 PrintTree(Dir->Right,Size);
209 }
210 /*}}}*/
211 // FLCache::GetPkg - Get a package pointer /*{{{*/
212 // ---------------------------------------------------------------------
213 /* Locate a package by name in it's tree, this is just a wrapper for
214 TreeLookup */
215 pkgFLCache::PkgIterator pkgFLCache::GetPkg(const char *Name,const char *NameEnd,
216 bool Insert)
217 {
218 if (NameEnd == 0)
219 NameEnd = Name + strlen(Name);
220
221 map_ptrloc Pos = TreeLookup(&HeaderP->Packages,Name,NameEnd,
222 sizeof(pkgFLCache::Package),
223 &HeaderP->PackageCount,Insert);
224 if (Pos == 0)
225 return pkgFLCache::PkgIterator();
226 return pkgFLCache::PkgIterator(*this,PkgP + Pos);
227 }
228 /*}}}*/
229 // FLCache::GetNode - Get the node associated with the filename /*{{{*/
230 // ---------------------------------------------------------------------
231 /* Lookup a node in the hash table. If Insert is true then a new node is
232 always inserted. The hash table can have multiple instances of a
233 single name available. A search returns the first. It is important
234 that additions for the same name insert after the first entry of
235 the name group. */
236 pkgFLCache::NodeIterator pkgFLCache::GetNode(const char *Name,
237 const char *NameEnd,
238 map_ptrloc Loc,
239 bool Insert,bool Divert)
240 {
241 // Split the name into file and directory, hashing as it is copied
242 const char *File = Name;
243 unsigned long HashPos = 0;
244 for (const char *I = Name; I < NameEnd; I++)
245 {
246 HashPos = 1637*HashPos + *I;
247 if (*I == '/')
248 File = I;
249 }
250
251 // Search for it
252 Node *Hash = NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
253 int Res = 0;
254 map_ptrloc FilePtr = 0;
255 while (Hash->Pointer != 0)
256 {
257 // Compare
258 Res = stringcmp(File+1,NameEnd,StrP + Hash->File);
259 if (Res == 0)
260 Res = stringcmp(Name,File,StrP + DirP[Hash->Dir].Name);
261
262 // Diversion?
263 if (Res == 0 && Insert == true)
264 {
265 /* Dir and File match exactly, we need to reuse the file name
266 when we link it in */
267 FilePtr = Hash->File;
268 Res = Divert - ((Hash->Flags & Node::Diversion) == Node::Diversion);
269 }
270
271 // Is a match
272 if (Res == 0)
273 {
274 if (Insert == false)
275 return NodeIterator(*this,Hash);
276
277 // Only one diversion per name!
278 if (Divert == true)
279 return NodeIterator(*this,Hash);
280 break;
281 }
282
283 // Out of sort order
284 if (Res > 0)
285 break;
286
287 if (Hash->Next != 0)
288 Hash = NodeP + Hash->Next;
289 else
290 break;
291 }
292
293 // Fail, not found
294 if (Insert == false)
295 return NodeIterator(*this);
296
297 // Find a directory node
298 map_ptrloc Dir = TreeLookup(&HeaderP->DirTree,Name,File,
299 sizeof(pkgFLCache::Directory),
300 &HeaderP->DirCount,true);
301 if (Dir == 0)
302 return NodeIterator(*this);
303
304 // Allocate a new node
305 if (Hash->Pointer != 0)
306 {
307 // Overwrite or append
308 if (Res > 0)
309 {
310 Node *Next = NodeP + Map.Allocate(sizeof(*Hash));
311 if (Next == NodeP)
312 return NodeIterator(*this);
313 *Next = *Hash;
314 Hash->Next = Next - NodeP;
315 }
316 else
317 {
318 unsigned long NewNext = Map.Allocate(sizeof(*Hash));
319 if (NewNext == 0)
320 return NodeIterator(*this);
321 NodeP[NewNext].Next = Hash->Next;
322 Hash->Next = NewNext;
323 Hash = NodeP + Hash->Next;
324 }
325 }
326
327 // Insert into the new item
328 Hash->Dir = Dir;
329 Hash->Pointer = Loc;
330 Hash->Flags = 0;
331 if (Divert == true)
332 Hash->Flags |= Node::Diversion;
333
334 if (FilePtr != 0)
335 Hash->File = FilePtr;
336 else
337 {
338 HeaderP->UniqNodes++;
339 Hash->File = Map.WriteString(File+1,NameEnd - File-1);
340 }
341
342 // Link the node to the package list
343 if (Divert == false && Loc == 0)
344 {
345 Hash->Next = PkgP[Loc].Files;
346 PkgP[Loc].Files = Hash - NodeP;
347 }
348
349 HeaderP->NodeCount++;
350 return NodeIterator(*this,Hash);
351 }
352 /*}}}*/
353 // FLCache::HashNode - Return the hash bucket for the node /*{{{*/
354 // ---------------------------------------------------------------------
355 /* This is one of two hashing functions. The other is inlined into the
356 GetNode routine. */
357 pkgFLCache::Node *pkgFLCache::HashNode(NodeIterator const &Nde)
358 {
359 // Hash the node
360 unsigned long HashPos = 0;
361 for (const char *I = Nde.DirN(); *I != 0; I++)
362 HashPos = 1637*HashPos + *I;
363 HashPos = 1637*HashPos + '/';
364 for (const char *I = Nde.File(); *I != 0; I++)
365 HashPos = 1637*HashPos + *I;
366 return NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
367 }
368 /*}}}*/
369 // FLCache::DropNode - Drop a node from the hash table /*{{{*/
370 // ---------------------------------------------------------------------
371 /* This erases a node from the hash table. Note that this does not unlink
372 the node from the package linked list. */
373 void pkgFLCache::DropNode(map_ptrloc N)
374 {
375 if (N == 0)
376 return;
377
378 NodeIterator Nde(*this,NodeP + N);
379
380 if (Nde->NextPkg != 0)
381 _error->Warning("DropNode called on still linked node");
382
383 // Locate it in the hash table
384 Node *Last = 0;
385 Node *Hash = HashNode(Nde);
386 while (Hash->Pointer != 0)
387 {
388 // Got it
389 if (Hash == Nde)
390 {
391 // Top of the bucket..
392 if (Last == 0)
393 {
394 Hash->Pointer = 0;
395 if (Hash->Next == 0)
396 return;
397 *Hash = NodeP[Hash->Next];
398 // Release Hash->Next
399 return;
400 }
401 Last->Next = Hash->Next;
402 // Release Hash
403 return;
404 }
405
406 Last = Hash;
407 if (Hash->Next != 0)
408 Hash = NodeP + Hash->Next;
409 else
410 break;
411 }
412
413 _error->Error("Failed to locate the hash element!");
414 }
415 /*}}}*/
416 // FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
417 // ---------------------------------------------------------------------
418 /* Tag all the diversions as untouched */
419 void pkgFLCache::BeginDiverLoad()
420 {
421 for (DiverIterator I = DiverBegin(); I.end() == false; I++)
422 I->Flags = 0;
423 }
424 /*}}}*/
425 // FLCache::FinishDiverLoad - Finish up a new diversion load /*{{{*/
426 // ---------------------------------------------------------------------
427 /* This drops any untouched diversions. In effect removing any diversions
428 that where not loaded (ie missing from the diversion file) */
429 void pkgFLCache::FinishDiverLoad()
430 {
431 map_ptrloc *Cur = &HeaderP->Diversions;
432 while (*Cur != 0)
433 {
434 Diversion *Div = DiverP + *Cur;
435 if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
436 {
437 Cur = &Div->Next;
438 continue;
439 }
440
441 // Purge!
442 DropNode(Div->DivertTo);
443 DropNode(Div->DivertFrom);
444 *Cur = Div->Next;
445 }
446 }
447 /*}}}*/
448 // FLCache::AddDiversion - Add a new diversion /*{{{*/
449 // ---------------------------------------------------------------------
450 /* Add a new diversion to the diverion tables and make sure that it is
451 unique and non-chaining. */
452 bool pkgFLCache::AddDiversion(PkgIterator const &Owner,
453 const char *From,const char *To)
454 {
455 /* Locate the two hash nodes we are going to manipulate. If there
456 are pre-existing diversions then they will be returned */
457 NodeIterator FromN = GetNode(From,From+strlen(From),0,true,true);
458 NodeIterator ToN = GetNode(To,To+strlen(To),0,true,true);
459 if (FromN.end() == true || ToN.end() == true)
460 return _error->Error("Failed to allocate diversion");
461
462 // Should never happen
463 if ((FromN->Flags & Node::Diversion) != Node::Diversion ||
464 (ToN->Flags & Node::Diversion) != Node::Diversion)
465 return _error->Error("Internal Error in AddDiversion");
466
467 // Now, try to reclaim an existing diversion..
468 map_ptrloc Diver = 0;
469 if (FromN->Pointer != 0)
470 Diver = FromN->Pointer;
471
472 /* Make sure from and to point to the same diversion, if they dont
473 then we are trying to intermix diversions - very bad */
474 if (ToN->Pointer != 0 && ToN->Pointer != Diver)
475 {
476 // It could be that the other diversion is no longer in use
477 if ((DiverP[ToN->Pointer].Flags & Diversion::Touched) == Diversion::Touched)
478 return _error->Error("Trying to overwrite a diversion, %s -> %s and %s/%s",
479 From,To,ToN.File(),ToN.Dir().Name());
480
481 // We can erase it.
482 Diversion *Div = DiverP + ToN->Pointer;
483 ToN->Pointer = 0;
484
485 if (Div->DivertTo == ToN.Offset())
486 Div->DivertTo = 0;
487 if (Div->DivertFrom == ToN.Offset())
488 Div->DivertFrom = 0;
489
490 // This diversion will be cleaned up by FinishDiverLoad
491 }
492
493 // Allocate a new diversion
494 if (Diver == 0)
495 {
496 Diver = Map.Allocate(sizeof(Diversion));
497 if (Diver == 0)
498 return false;
499 DiverP[Diver].Next = HeaderP->Diversions;
500 HeaderP->Diversions = Diver;
501 HeaderP->DiversionCount++;
502 }
503
504 // Can only have one diversion of the same files
505 Diversion *Div = DiverP + Diver;
506 if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
507 return _error->Error("Double add of diversion %s -> %s",From,To);
508
509 // Setup the From/To links
510 if (Div->DivertFrom != FromN.Offset() && Div->DivertFrom != ToN.Offset())
511 DropNode(Div->DivertFrom);
512 Div->DivertFrom = FromN.Offset();
513 if (Div->DivertTo != FromN.Offset() && Div->DivertTo != ToN.Offset())
514 DropNode(Div->DivertTo);
515 Div->DivertTo = ToN.Offset();
516
517 // Link it to the two nodes
518 FromN->Pointer = Diver;
519 ToN->Pointer = Diver;
520
521 // And the package
522 Div->OwnerPkg = Owner.Offset();
523 Div->Flags |= Diversion::Touched;
524
525 return true;
526 }
527 /*}}}*/
528 // FLCache::AddConfFile - Add a new configuration file /*{{{*/
529 // ---------------------------------------------------------------------
530 /* This simply adds a new conf file node to the hash table. This is only
531 used by the status file reader. It associates a hash with each conf
532 file entry that exists in the status file and the list file for
533 the proper package. Duplicate conf files (across packages) are left
534 up to other routines to deal with. */
535 bool pkgFLCache::AddConfFile(const char *Name,const char *NameEnd,
536 PkgIterator const &Owner,
537 const unsigned char *Sum)
538 {
539 NodeIterator Nde = GetNode(Name,NameEnd,0,false,false);
540 if (Nde.end() == true)
541 return true;
542
543 unsigned long File = Nde->File;
544 for (; Nde->File == File && Nde.end() == false; Nde++)
545 {
546 if (Nde.RealPackage() != Owner)
547 continue;
548
549 if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
550 return _error->Error("Duplicate conf file %s/%s",Nde.DirN(),Nde.File());
551
552 // Allocate a new conf file structure
553 map_ptrloc Conf = Map.Allocate(sizeof(ConfFile));
554 if (Conf == 0)
555 return false;
556 ConfP[Conf].OwnerPkg = Owner.Offset();
557 memcpy(ConfP[Conf].MD5,Sum,sizeof(ConfP[Conf].MD5));
558
559 Nde->Pointer = Conf;
560 Nde->Flags |= Node::ConfFile;
561 return true;
562 }
563
564 /* This means the conf file has been replaced, but the entry in the
565 status file was not updated */
566 return true;
567 }
568 /*}}}*/
569
570 // NodeIterator::RealPackage - Return the package for this node /*{{{*/
571 // ---------------------------------------------------------------------
572 /* Since the package pointer is indirected in all sorts of interesting ways
573 this is used to get a pointer to the owning package */
574 pkgFLCache::Package *pkgFLCache::NodeIterator::RealPackage() const
575 {
576 if (Nde->Pointer == 0)
577 return 0;
578
579 if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
580 return Owner->PkgP + Owner->ConfP[Nde->Pointer].OwnerPkg;
581
582 // Diversions are ignored
583 if ((Nde->Flags & Node::Diversion) == Node::Diversion)
584 return 0;
585
586 return Owner->PkgP + Nde->Pointer;
587 }
588 /*}}}*/