]>
git.saurik.com Git - apt.git/blob - apt-inst/filelist.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: filelist.cc,v 1.4.2.1 2004/01/16 18:58:50 mdz 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 /*{{{*/
37 #include <apt-pkg/filelist.h>
38 #include <apt-pkg/mmap.h>
39 #include <apt-pkg/error.h>
40 #include <apt-pkg/strutl.h>
51 // FlCache::Header::Header - Constructor /*{{{*/
52 // ---------------------------------------------------------------------
53 /* Initialize the header variables. These are the defaults used when
54 creating new caches */
55 pkgFLCache::Header::Header()
57 Signature
= 0xEA3F1295;
59 /* Whenever the structures change the major version should be bumped,
60 whenever the generator changes the minor version should be bumped. */
65 HeaderSz
= sizeof(pkgFLCache::Header
);
66 NodeSz
= sizeof(pkgFLCache::Node
);
67 DirSz
= sizeof(pkgFLCache::Directory
);
68 PackageSz
= sizeof(pkgFLCache::Package
);
69 DiversionSz
= sizeof(pkgFLCache::Diversion
);
70 ConfFileSz
= sizeof(pkgFLCache::ConfFile
);
84 memset(Pools
,0,sizeof(Pools
));
87 // FLCache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
88 // ---------------------------------------------------------------------
89 /* Compare to make sure we are matching versions */
90 bool pkgFLCache::Header::CheckSizes(Header
&Against
) const
92 if (HeaderSz
== Against
.HeaderSz
&&
93 NodeSz
== Against
.NodeSz
&&
94 DirSz
== Against
.DirSz
&&
95 DiversionSz
== Against
.DiversionSz
&&
96 PackageSz
== Against
.PackageSz
&&
97 ConfFileSz
== Against
.ConfFileSz
)
103 // FLCache::pkgFLCache - Constructor /*{{{*/
104 // ---------------------------------------------------------------------
105 /* If this is a new cache then a new header and hash table are instantaited
106 otherwise the existing ones are mearly attached */
107 pkgFLCache::pkgFLCache(DynamicMMap
&Map
) : Map(Map
)
109 if (_error
->PendingError() == true)
115 // Apply the typecasts
116 HeaderP
= (Header
*)Map
.Data();
117 NodeP
= (Node
*)Map
.Data();
118 DirP
= (Directory
*)Map
.Data();
119 DiverP
= (Diversion
*)Map
.Data();
120 PkgP
= (Package
*)Map
.Data();
121 ConfP
= (ConfFile
*)Map
.Data();
122 StrP
= (char *)Map
.Data();
123 AnyP
= (unsigned char *)Map
.Data();
125 // New mapping, create the basic cache structures
128 Map
.RawAllocate(sizeof(pkgFLCache::Header
));
129 *HeaderP
= pkgFLCache::Header();
130 HeaderP
->FileHash
= Map
.RawAllocate(sizeof(pkgFLCache::Node
)*HeaderP
->HashSize
,
131 sizeof(pkgFLCache::Node
))/sizeof(pkgFLCache::Node
);
134 FileHash
= NodeP
+ HeaderP
->FileHash
;
136 // Setup the dynamic map manager
137 HeaderP
->Dirty
= true;
138 Map
.Sync(0,sizeof(pkgFLCache::Header
));
139 Map
.UsePools(*HeaderP
->Pools
,sizeof(HeaderP
->Pools
)/sizeof(HeaderP
->Pools
[0]));
142 // FLCache::TreeLookup - Perform a lookup in a generic tree /*{{{*/
143 // ---------------------------------------------------------------------
144 /* This is a simple generic tree lookup. The first three entries of
145 the Directory structure are used as a template, but any other similar
146 structure could be used in it's place. */
147 map_ptrloc
pkgFLCache::TreeLookup(map_ptrloc
*Base
,const char *Text
,
148 const char *TextEnd
,unsigned long Size
,
149 unsigned int *Count
,bool Insert
)
151 pkgFLCache::Directory
*Dir
;
153 // Check our last entry cache
154 if (LastTreeLookup
!= 0 && LastLookupSize
== Size
)
156 Dir
= (pkgFLCache::Directory
*)(AnyP
+ LastTreeLookup
*Size
);
157 if (stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
) == 0)
158 return LastTreeLookup
;
163 // Allocate a new one
169 *Base
= Map
.Allocate(Size
);
174 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
175 Dir
->Name
= Map
.WriteString(Text
,TextEnd
- Text
);
176 LastTreeLookup
= *Base
;
177 LastLookupSize
= Size
;
182 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
183 int Res
= stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
);
186 LastTreeLookup
= *Base
;
187 LastLookupSize
= Size
;
198 // FLCache::PrintTree - Print out a tree /*{{{*/
199 // ---------------------------------------------------------------------
200 /* This is a simple generic tree dumper, ment for debugging. */
201 void pkgFLCache::PrintTree(map_ptrloc Base
,unsigned long Size
)
206 pkgFLCache::Directory
*Dir
= (pkgFLCache::Directory
*)(AnyP
+ Base
*Size
);
207 PrintTree(Dir
->Left
,Size
);
208 cout
<< (StrP
+ Dir
->Name
) << endl
;
209 PrintTree(Dir
->Right
,Size
);
212 // FLCache::GetPkg - Get a package pointer /*{{{*/
213 // ---------------------------------------------------------------------
214 /* Locate a package by name in it's tree, this is just a wrapper for
216 pkgFLCache::PkgIterator
pkgFLCache::GetPkg(const char *Name
,const char *NameEnd
,
220 NameEnd
= Name
+ strlen(Name
);
222 map_ptrloc Pos
= TreeLookup(&HeaderP
->Packages
,Name
,NameEnd
,
223 sizeof(pkgFLCache::Package
),
224 &HeaderP
->PackageCount
,Insert
);
226 return pkgFLCache::PkgIterator();
227 return pkgFLCache::PkgIterator(*this,PkgP
+ Pos
);
230 // FLCache::GetNode - Get the node associated with the filename /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Lookup a node in the hash table. If Insert is true then a new node is
233 always inserted. The hash table can have multiple instances of a
234 single name available. A search returns the first. It is important
235 that additions for the same name insert after the first entry of
237 pkgFLCache::NodeIterator
pkgFLCache::GetNode(const char *Name
,
240 bool Insert
,bool Divert
)
242 // Split the name into file and directory, hashing as it is copied
243 const char *File
= Name
;
244 unsigned long HashPos
= 0;
245 for (const char *I
= Name
; I
< NameEnd
; I
++)
247 HashPos
= 1637*HashPos
+ *I
;
253 Node
*Hash
= NodeP
+ HeaderP
->FileHash
+ (HashPos
% HeaderP
->HashSize
);
255 map_ptrloc FilePtr
= 0;
256 while (Hash
->Pointer
!= 0)
259 Res
= stringcmp(File
+1,NameEnd
,StrP
+ Hash
->File
);
261 Res
= stringcmp(Name
,File
,StrP
+ DirP
[Hash
->Dir
].Name
);
264 if (Res
== 0 && Insert
== true)
266 /* Dir and File match exactly, we need to reuse the file name
267 when we link it in */
268 FilePtr
= Hash
->File
;
269 Res
= Divert
- ((Hash
->Flags
& Node::Diversion
) == Node::Diversion
);
276 return NodeIterator(*this,Hash
);
278 // Only one diversion per name!
280 return NodeIterator(*this,Hash
);
289 Hash
= NodeP
+ Hash
->Next
;
296 return NodeIterator(*this);
298 // Find a directory node
299 map_ptrloc Dir
= TreeLookup(&HeaderP
->DirTree
,Name
,File
,
300 sizeof(pkgFLCache::Directory
),
301 &HeaderP
->DirCount
,true);
303 return NodeIterator(*this);
305 // Allocate a new node
306 if (Hash
->Pointer
!= 0)
308 // Overwrite or append
311 Node
*Next
= NodeP
+ Map
.Allocate(sizeof(*Hash
));
313 return NodeIterator(*this);
315 Hash
->Next
= Next
- NodeP
;
319 unsigned long NewNext
= Map
.Allocate(sizeof(*Hash
));
321 return NodeIterator(*this);
322 NodeP
[NewNext
].Next
= Hash
->Next
;
323 Hash
->Next
= NewNext
;
324 Hash
= NodeP
+ Hash
->Next
;
328 // Insert into the new item
333 Hash
->Flags
|= Node::Diversion
;
336 Hash
->File
= FilePtr
;
339 HeaderP
->UniqNodes
++;
340 Hash
->File
= Map
.WriteString(File
+1,NameEnd
- File
-1);
343 // Link the node to the package list
344 if (Divert
== false && Loc
== 0)
346 Hash
->Next
= PkgP
[Loc
].Files
;
347 PkgP
[Loc
].Files
= Hash
- NodeP
;
350 HeaderP
->NodeCount
++;
351 return NodeIterator(*this,Hash
);
354 // FLCache::HashNode - Return the hash bucket for the node /*{{{*/
355 // ---------------------------------------------------------------------
356 /* This is one of two hashing functions. The other is inlined into the
358 pkgFLCache::Node
*pkgFLCache::HashNode(NodeIterator
const &Nde
)
361 unsigned long HashPos
= 0;
362 for (const char *I
= Nde
.DirN(); *I
!= 0; I
++)
363 HashPos
= 1637*HashPos
+ *I
;
364 HashPos
= 1637*HashPos
+ '/';
365 for (const char *I
= Nde
.File(); *I
!= 0; I
++)
366 HashPos
= 1637*HashPos
+ *I
;
367 return NodeP
+ HeaderP
->FileHash
+ (HashPos
% HeaderP
->HashSize
);
370 // FLCache::DropNode - Drop a node from the hash table /*{{{*/
371 // ---------------------------------------------------------------------
372 /* This erases a node from the hash table. Note that this does not unlink
373 the node from the package linked list. */
374 void pkgFLCache::DropNode(map_ptrloc N
)
379 NodeIterator
Nde(*this,NodeP
+ N
);
381 if (Nde
->NextPkg
!= 0)
382 _error
->Warning(_("DropNode called on still linked node"));
384 // Locate it in the hash table
386 Node
*Hash
= HashNode(Nde
);
387 while (Hash
->Pointer
!= 0)
392 // Top of the bucket..
398 *Hash
= NodeP
[Hash
->Next
];
399 // Release Hash->Next
402 Last
->Next
= Hash
->Next
;
409 Hash
= NodeP
+ Hash
->Next
;
414 _error
->Error(_("Failed to locate the hash element!"));
417 // FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
418 // ---------------------------------------------------------------------
419 /* Tag all the diversions as untouched */
420 void pkgFLCache::BeginDiverLoad()
422 for (DiverIterator I
= DiverBegin(); I
.end() == false; I
++)
426 // FLCache::FinishDiverLoad - Finish up a new diversion load /*{{{*/
427 // ---------------------------------------------------------------------
428 /* This drops any untouched diversions. In effect removing any diversions
429 that where not loaded (ie missing from the diversion file) */
430 void pkgFLCache::FinishDiverLoad()
432 map_ptrloc
*Cur
= &HeaderP
->Diversions
;
435 Diversion
*Div
= DiverP
+ *Cur
;
436 if ((Div
->Flags
& Diversion::Touched
) == Diversion::Touched
)
443 DropNode(Div
->DivertTo
);
444 DropNode(Div
->DivertFrom
);
449 // FLCache::AddDiversion - Add a new diversion /*{{{*/
450 // ---------------------------------------------------------------------
451 /* Add a new diversion to the diverion tables and make sure that it is
452 unique and non-chaining. */
453 bool pkgFLCache::AddDiversion(PkgIterator
const &Owner
,
454 const char *From
,const char *To
)
456 /* Locate the two hash nodes we are going to manipulate. If there
457 are pre-existing diversions then they will be returned */
458 NodeIterator FromN
= GetNode(From
,From
+strlen(From
),0,true,true);
459 NodeIterator ToN
= GetNode(To
,To
+strlen(To
),0,true,true);
460 if (FromN
.end() == true || ToN
.end() == true)
461 return _error
->Error(_("Failed to allocate diversion"));
463 // Should never happen
464 if ((FromN
->Flags
& Node::Diversion
) != Node::Diversion
||
465 (ToN
->Flags
& Node::Diversion
) != Node::Diversion
)
466 return _error
->Error(_("Internal error in AddDiversion"));
468 // Now, try to reclaim an existing diversion..
469 map_ptrloc Diver
= 0;
470 if (FromN
->Pointer
!= 0)
471 Diver
= FromN
->Pointer
;
473 /* Make sure from and to point to the same diversion, if they dont
474 then we are trying to intermix diversions - very bad */
475 if (ToN
->Pointer
!= 0 && ToN
->Pointer
!= Diver
)
477 // It could be that the other diversion is no longer in use
478 if ((DiverP
[ToN
->Pointer
].Flags
& Diversion::Touched
) == Diversion::Touched
)
479 return _error
->Error(_("Trying to overwrite a diversion, %s -> %s and %s/%s"),
480 From
,To
,ToN
.File(),ToN
.Dir().Name());
483 Diversion
*Div
= DiverP
+ ToN
->Pointer
;
486 if (Div
->DivertTo
== ToN
.Offset())
488 if (Div
->DivertFrom
== ToN
.Offset())
491 // This diversion will be cleaned up by FinishDiverLoad
494 // Allocate a new diversion
497 Diver
= Map
.Allocate(sizeof(Diversion
));
500 DiverP
[Diver
].Next
= HeaderP
->Diversions
;
501 HeaderP
->Diversions
= Diver
;
502 HeaderP
->DiversionCount
++;
505 // Can only have one diversion of the same files
506 Diversion
*Div
= DiverP
+ Diver
;
507 if ((Div
->Flags
& Diversion::Touched
) == Diversion::Touched
)
508 return _error
->Error(_("Double add of diversion %s -> %s"),From
,To
);
510 // Setup the From/To links
511 if (Div
->DivertFrom
!= FromN
.Offset() && Div
->DivertFrom
!= ToN
.Offset())
512 DropNode(Div
->DivertFrom
);
513 Div
->DivertFrom
= FromN
.Offset();
514 if (Div
->DivertTo
!= FromN
.Offset() && Div
->DivertTo
!= ToN
.Offset())
515 DropNode(Div
->DivertTo
);
516 Div
->DivertTo
= ToN
.Offset();
518 // Link it to the two nodes
519 FromN
->Pointer
= Diver
;
520 ToN
->Pointer
= Diver
;
523 Div
->OwnerPkg
= Owner
.Offset();
524 Div
->Flags
|= Diversion::Touched
;
529 // FLCache::AddConfFile - Add a new configuration file /*{{{*/
530 // ---------------------------------------------------------------------
531 /* This simply adds a new conf file node to the hash table. This is only
532 used by the status file reader. It associates a hash with each conf
533 file entry that exists in the status file and the list file for
534 the proper package. Duplicate conf files (across packages) are left
535 up to other routines to deal with. */
536 bool pkgFLCache::AddConfFile(const char *Name
,const char *NameEnd
,
537 PkgIterator
const &Owner
,
538 const unsigned char *Sum
)
540 NodeIterator Nde
= GetNode(Name
,NameEnd
,0,false,false);
541 if (Nde
.end() == true)
544 unsigned long File
= Nde
->File
;
545 for (; Nde
->File
== File
&& Nde
.end() == false; Nde
++)
547 if (Nde
.RealPackage() != Owner
)
550 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
551 return _error
->Error(_("Duplicate conf file %s/%s"),Nde
.DirN(),Nde
.File());
553 // Allocate a new conf file structure
554 map_ptrloc Conf
= Map
.Allocate(sizeof(ConfFile
));
557 ConfP
[Conf
].OwnerPkg
= Owner
.Offset();
558 memcpy(ConfP
[Conf
].MD5
,Sum
,sizeof(ConfP
[Conf
].MD5
));
561 Nde
->Flags
|= Node::ConfFile
;
565 /* This means the conf file has been replaced, but the entry in the
566 status file was not updated */
571 // NodeIterator::RealPackage - Return the package for this node /*{{{*/
572 // ---------------------------------------------------------------------
573 /* Since the package pointer is indirected in all sorts of interesting ways
574 this is used to get a pointer to the owning package */
575 pkgFLCache::Package
*pkgFLCache::NodeIterator::RealPackage() const
577 if (Nde
->Pointer
== 0)
580 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
581 return Owner
->PkgP
+ Owner
->ConfP
[Nde
->Pointer
].OwnerPkg
;
583 // Diversions are ignored
584 if ((Nde
->Flags
& Node::Diversion
) == Node::Diversion
)
587 return Owner
->PkgP
+ Nde
->Pointer
;