]>
git.saurik.com Git - apt.git/blob - apt-inst/filelist.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: filelist.cc,v 1.2 2001/02/20 07:03:16 jgg Exp $
4 /* ######################################################################
6 File Listing - Manages a Cache of File -> Package names.
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.
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.
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.
25 Diversions that are user-installed are represented by a package with
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.
32 ##################################################################### */
34 // Include Files /*{{{*/
36 #pragma implementation "apt-pkg/filelist.h"
39 #include <apt-pkg/filelist.h>
40 #include <apt-pkg/mmap.h>
41 #include <apt-pkg/error.h>
42 #include <apt-pkg/strutl.h>
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()
56 Signature
= 0xEA3F1295;
58 /* Whenever the structures change the major version should be bumped,
59 whenever the generator changes the minor version should be bumped. */
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
);
83 memset(Pools
,0,sizeof(Pools
));
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
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
)
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
)
108 if (_error
->PendingError() == true)
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();
124 // New mapping, create the basic cache structures
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
);
133 FileHash
= NodeP
+ HeaderP
->FileHash
;
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]));
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
)
150 pkgFLCache::Directory
*Dir
;
152 // Check our last entry cache
153 if (LastTreeLookup
!= 0 && LastLookupSize
== Size
)
155 Dir
= (pkgFLCache::Directory
*)(AnyP
+ LastTreeLookup
*Size
);
156 if (stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
) == 0)
157 return LastTreeLookup
;
162 // Allocate a new one
168 *Base
= Map
.Allocate(Size
);
173 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
174 Dir
->Name
= Map
.WriteString(Text
,TextEnd
- Text
);
175 LastTreeLookup
= *Base
;
176 LastLookupSize
= Size
;
181 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
182 int Res
= stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
);
185 LastTreeLookup
= *Base
;
186 LastLookupSize
= Size
;
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
)
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
);
211 // FLCache::GetPkg - Get a package pointer /*{{{*/
212 // ---------------------------------------------------------------------
213 /* Locate a package by name in it's tree, this is just a wrapper for
215 pkgFLCache::PkgIterator
pkgFLCache::GetPkg(const char *Name
,const char *NameEnd
,
219 NameEnd
= Name
+ strlen(Name
);
221 map_ptrloc Pos
= TreeLookup(&HeaderP
->Packages
,Name
,NameEnd
,
222 sizeof(pkgFLCache::Package
),
223 &HeaderP
->PackageCount
,Insert
);
225 return pkgFLCache::PkgIterator();
226 return pkgFLCache::PkgIterator(*this,PkgP
+ Pos
);
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
236 pkgFLCache::NodeIterator
pkgFLCache::GetNode(const char *Name
,
239 bool Insert
,bool Divert
)
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
++)
246 HashPos
= 1637*HashPos
+ *I
;
252 Node
*Hash
= NodeP
+ HeaderP
->FileHash
+ (HashPos
% HeaderP
->HashSize
);
254 map_ptrloc FilePtr
= 0;
255 while (Hash
->Pointer
!= 0)
258 Res
= stringcmp(File
+1,NameEnd
,StrP
+ Hash
->File
);
260 Res
= stringcmp(Name
,File
,StrP
+ DirP
[Hash
->Dir
].Name
);
263 if (Res
== 0 && Insert
== true)
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
);
275 return NodeIterator(*this,Hash
);
277 // Only one diversion per name!
279 return NodeIterator(*this,Hash
);
288 Hash
= NodeP
+ Hash
->Next
;
295 return NodeIterator(*this);
297 // Find a directory node
298 map_ptrloc Dir
= TreeLookup(&HeaderP
->DirTree
,Name
,File
,
299 sizeof(pkgFLCache::Directory
),
300 &HeaderP
->DirCount
,true);
302 return NodeIterator(*this);
304 // Allocate a new node
305 if (Hash
->Pointer
!= 0)
307 // Overwrite or append
310 Node
*Next
= NodeP
+ Map
.Allocate(sizeof(*Hash
));
312 return NodeIterator(*this);
314 Hash
->Next
= Next
- NodeP
;
318 unsigned long NewNext
= Map
.Allocate(sizeof(*Hash
));
320 return NodeIterator(*this);
321 NodeP
[NewNext
].Next
= Hash
->Next
;
322 Hash
->Next
= NewNext
;
323 Hash
= NodeP
+ Hash
->Next
;
327 // Insert into the new item
332 Hash
->Flags
|= Node::Diversion
;
335 Hash
->File
= FilePtr
;
338 HeaderP
->UniqNodes
++;
339 Hash
->File
= Map
.WriteString(File
+1,NameEnd
- File
-1);
342 // Link the node to the package list
343 if (Divert
== false && Loc
== 0)
345 Hash
->Next
= PkgP
[Loc
].Files
;
346 PkgP
[Loc
].Files
= Hash
- NodeP
;
349 HeaderP
->NodeCount
++;
350 return NodeIterator(*this,Hash
);
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
357 pkgFLCache::Node
*pkgFLCache::HashNode(NodeIterator
const &Nde
)
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
);
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
)
378 NodeIterator
Nde(*this,NodeP
+ N
);
380 if (Nde
->NextPkg
!= 0)
381 _error
->Warning("DropNode called on still linked node");
383 // Locate it in the hash table
385 Node
*Hash
= HashNode(Nde
);
386 while (Hash
->Pointer
!= 0)
391 // Top of the bucket..
397 *Hash
= NodeP
[Hash
->Next
];
398 // Release Hash->Next
401 Last
->Next
= Hash
->Next
;
408 Hash
= NodeP
+ Hash
->Next
;
413 _error
->Error("Failed to locate the hash element!");
416 // FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
417 // ---------------------------------------------------------------------
418 /* Tag all the diversions as untouched */
419 void pkgFLCache::BeginDiverLoad()
421 for (DiverIterator I
= DiverBegin(); I
.end() == false; I
++)
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()
431 map_ptrloc
*Cur
= &HeaderP
->Diversions
;
434 Diversion
*Div
= DiverP
+ *Cur
;
435 if ((Div
->Flags
& Diversion::Touched
) == Diversion::Touched
)
442 DropNode(Div
->DivertTo
);
443 DropNode(Div
->DivertFrom
);
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
)
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");
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");
467 // Now, try to reclaim an existing diversion..
468 map_ptrloc Diver
= 0;
469 if (FromN
->Pointer
!= 0)
470 Diver
= FromN
->Pointer
;
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
)
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());
482 Diversion
*Div
= DiverP
+ ToN
->Pointer
;
485 if (Div
->DivertTo
== ToN
.Offset())
487 if (Div
->DivertFrom
== ToN
.Offset())
490 // This diversion will be cleaned up by FinishDiverLoad
493 // Allocate a new diversion
496 Diver
= Map
.Allocate(sizeof(Diversion
));
499 DiverP
[Diver
].Next
= HeaderP
->Diversions
;
500 HeaderP
->Diversions
= Diver
;
501 HeaderP
->DiversionCount
++;
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
);
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();
517 // Link it to the two nodes
518 FromN
->Pointer
= Diver
;
519 ToN
->Pointer
= Diver
;
522 Div
->OwnerPkg
= Owner
.Offset();
523 Div
->Flags
|= Diversion::Touched
;
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
)
539 NodeIterator Nde
= GetNode(Name
,NameEnd
,0,false,false);
540 if (Nde
.end() == true)
543 unsigned long File
= Nde
->File
;
544 for (; Nde
->File
== File
&& Nde
.end() == false; Nde
++)
546 if (Nde
.RealPackage() != Owner
)
549 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
550 return _error
->Error("Duplicate conf file %s/%s",Nde
.DirN(),Nde
.File());
552 // Allocate a new conf file structure
553 map_ptrloc Conf
= Map
.Allocate(sizeof(ConfFile
));
556 ConfP
[Conf
].OwnerPkg
= Owner
.Offset();
557 memcpy(ConfP
[Conf
].MD5
,Sum
,sizeof(ConfP
[Conf
].MD5
));
560 Nde
->Flags
|= Node::ConfFile
;
564 /* This means the conf file has been replaced, but the entry in the
565 status file was not updated */
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
576 if (Nde
->Pointer
== 0)
579 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
580 return Owner
->PkgP
+ Owner
->ConfP
[Nde
->Pointer
].OwnerPkg
;
582 // Diversions are ignored
583 if ((Nde
->Flags
& Node::Diversion
) == Node::Diversion
)
586 return Owner
->PkgP
+ Nde
->Pointer
;