]>
git.saurik.com Git - apt.git/blob - apt-inst/filelist.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: filelist.cc,v 1.3 2001/05/27 23:45:39 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>
52 // FlCache::Header::Header - Constructor /*{{{*/
53 // ---------------------------------------------------------------------
54 /* Initialize the header variables. These are the defaults used when
55 creating new caches */
56 pkgFLCache::Header::Header()
58 Signature
= 0xEA3F1295;
60 /* Whenever the structures change the major version should be bumped,
61 whenever the generator changes the minor version should be bumped. */
66 HeaderSz
= sizeof(pkgFLCache::Header
);
67 NodeSz
= sizeof(pkgFLCache::Node
);
68 DirSz
= sizeof(pkgFLCache::Directory
);
69 PackageSz
= sizeof(pkgFLCache::Package
);
70 DiversionSz
= sizeof(pkgFLCache::Diversion
);
71 ConfFileSz
= sizeof(pkgFLCache::ConfFile
);
85 memset(Pools
,0,sizeof(Pools
));
88 // FLCache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
89 // ---------------------------------------------------------------------
90 /* Compare to make sure we are matching versions */
91 bool pkgFLCache::Header::CheckSizes(Header
&Against
) const
93 if (HeaderSz
== Against
.HeaderSz
&&
94 NodeSz
== Against
.NodeSz
&&
95 DirSz
== Against
.DirSz
&&
96 DiversionSz
== Against
.DiversionSz
&&
97 PackageSz
== Against
.PackageSz
&&
98 ConfFileSz
== Against
.ConfFileSz
)
104 // FLCache::pkgFLCache - Constructor /*{{{*/
105 // ---------------------------------------------------------------------
106 /* If this is a new cache then a new header and hash table are instantaited
107 otherwise the existing ones are mearly attached */
108 pkgFLCache::pkgFLCache(DynamicMMap
&Map
) : Map(Map
)
110 if (_error
->PendingError() == true)
116 // Apply the typecasts
117 HeaderP
= (Header
*)Map
.Data();
118 NodeP
= (Node
*)Map
.Data();
119 DirP
= (Directory
*)Map
.Data();
120 DiverP
= (Diversion
*)Map
.Data();
121 PkgP
= (Package
*)Map
.Data();
122 ConfP
= (ConfFile
*)Map
.Data();
123 StrP
= (char *)Map
.Data();
124 AnyP
= (unsigned char *)Map
.Data();
126 // New mapping, create the basic cache structures
129 Map
.RawAllocate(sizeof(pkgFLCache::Header
));
130 *HeaderP
= pkgFLCache::Header();
131 HeaderP
->FileHash
= Map
.RawAllocate(sizeof(pkgFLCache::Node
)*HeaderP
->HashSize
,
132 sizeof(pkgFLCache::Node
))/sizeof(pkgFLCache::Node
);
135 FileHash
= NodeP
+ HeaderP
->FileHash
;
137 // Setup the dynamic map manager
138 HeaderP
->Dirty
= true;
139 Map
.Sync(0,sizeof(pkgFLCache::Header
));
140 Map
.UsePools(*HeaderP
->Pools
,sizeof(HeaderP
->Pools
)/sizeof(HeaderP
->Pools
[0]));
143 // FLCache::TreeLookup - Perform a lookup in a generic tree /*{{{*/
144 // ---------------------------------------------------------------------
145 /* This is a simple generic tree lookup. The first three entries of
146 the Directory structure are used as a template, but any other similar
147 structure could be used in it's place. */
148 map_ptrloc
pkgFLCache::TreeLookup(map_ptrloc
*Base
,const char *Text
,
149 const char *TextEnd
,unsigned long Size
,
150 unsigned int *Count
,bool Insert
)
152 pkgFLCache::Directory
*Dir
;
154 // Check our last entry cache
155 if (LastTreeLookup
!= 0 && LastLookupSize
== Size
)
157 Dir
= (pkgFLCache::Directory
*)(AnyP
+ LastTreeLookup
*Size
);
158 if (stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
) == 0)
159 return LastTreeLookup
;
164 // Allocate a new one
170 *Base
= Map
.Allocate(Size
);
175 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
176 Dir
->Name
= Map
.WriteString(Text
,TextEnd
- Text
);
177 LastTreeLookup
= *Base
;
178 LastLookupSize
= Size
;
183 Dir
= (pkgFLCache::Directory
*)(AnyP
+ *Base
*Size
);
184 int Res
= stringcmp(Text
,TextEnd
,StrP
+ Dir
->Name
);
187 LastTreeLookup
= *Base
;
188 LastLookupSize
= Size
;
199 // FLCache::PrintTree - Print out a tree /*{{{*/
200 // ---------------------------------------------------------------------
201 /* This is a simple generic tree dumper, ment for debugging. */
202 void pkgFLCache::PrintTree(map_ptrloc Base
,unsigned long Size
)
207 pkgFLCache::Directory
*Dir
= (pkgFLCache::Directory
*)(AnyP
+ Base
*Size
);
208 PrintTree(Dir
->Left
,Size
);
209 cout
<< (StrP
+ Dir
->Name
) << endl
;
210 PrintTree(Dir
->Right
,Size
);
213 // FLCache::GetPkg - Get a package pointer /*{{{*/
214 // ---------------------------------------------------------------------
215 /* Locate a package by name in it's tree, this is just a wrapper for
217 pkgFLCache::PkgIterator
pkgFLCache::GetPkg(const char *Name
,const char *NameEnd
,
221 NameEnd
= Name
+ strlen(Name
);
223 map_ptrloc Pos
= TreeLookup(&HeaderP
->Packages
,Name
,NameEnd
,
224 sizeof(pkgFLCache::Package
),
225 &HeaderP
->PackageCount
,Insert
);
227 return pkgFLCache::PkgIterator();
228 return pkgFLCache::PkgIterator(*this,PkgP
+ Pos
);
231 // FLCache::GetNode - Get the node associated with the filename /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Lookup a node in the hash table. If Insert is true then a new node is
234 always inserted. The hash table can have multiple instances of a
235 single name available. A search returns the first. It is important
236 that additions for the same name insert after the first entry of
238 pkgFLCache::NodeIterator
pkgFLCache::GetNode(const char *Name
,
241 bool Insert
,bool Divert
)
243 // Split the name into file and directory, hashing as it is copied
244 const char *File
= Name
;
245 unsigned long HashPos
= 0;
246 for (const char *I
= Name
; I
< NameEnd
; I
++)
248 HashPos
= 1637*HashPos
+ *I
;
254 Node
*Hash
= NodeP
+ HeaderP
->FileHash
+ (HashPos
% HeaderP
->HashSize
);
256 map_ptrloc FilePtr
= 0;
257 while (Hash
->Pointer
!= 0)
260 Res
= stringcmp(File
+1,NameEnd
,StrP
+ Hash
->File
);
262 Res
= stringcmp(Name
,File
,StrP
+ DirP
[Hash
->Dir
].Name
);
265 if (Res
== 0 && Insert
== true)
267 /* Dir and File match exactly, we need to reuse the file name
268 when we link it in */
269 FilePtr
= Hash
->File
;
270 Res
= Divert
- ((Hash
->Flags
& Node::Diversion
) == Node::Diversion
);
277 return NodeIterator(*this,Hash
);
279 // Only one diversion per name!
281 return NodeIterator(*this,Hash
);
290 Hash
= NodeP
+ Hash
->Next
;
297 return NodeIterator(*this);
299 // Find a directory node
300 map_ptrloc Dir
= TreeLookup(&HeaderP
->DirTree
,Name
,File
,
301 sizeof(pkgFLCache::Directory
),
302 &HeaderP
->DirCount
,true);
304 return NodeIterator(*this);
306 // Allocate a new node
307 if (Hash
->Pointer
!= 0)
309 // Overwrite or append
312 Node
*Next
= NodeP
+ Map
.Allocate(sizeof(*Hash
));
314 return NodeIterator(*this);
316 Hash
->Next
= Next
- NodeP
;
320 unsigned long NewNext
= Map
.Allocate(sizeof(*Hash
));
322 return NodeIterator(*this);
323 NodeP
[NewNext
].Next
= Hash
->Next
;
324 Hash
->Next
= NewNext
;
325 Hash
= NodeP
+ Hash
->Next
;
329 // Insert into the new item
334 Hash
->Flags
|= Node::Diversion
;
337 Hash
->File
= FilePtr
;
340 HeaderP
->UniqNodes
++;
341 Hash
->File
= Map
.WriteString(File
+1,NameEnd
- File
-1);
344 // Link the node to the package list
345 if (Divert
== false && Loc
== 0)
347 Hash
->Next
= PkgP
[Loc
].Files
;
348 PkgP
[Loc
].Files
= Hash
- NodeP
;
351 HeaderP
->NodeCount
++;
352 return NodeIterator(*this,Hash
);
355 // FLCache::HashNode - Return the hash bucket for the node /*{{{*/
356 // ---------------------------------------------------------------------
357 /* This is one of two hashing functions. The other is inlined into the
359 pkgFLCache::Node
*pkgFLCache::HashNode(NodeIterator
const &Nde
)
362 unsigned long HashPos
= 0;
363 for (const char *I
= Nde
.DirN(); *I
!= 0; I
++)
364 HashPos
= 1637*HashPos
+ *I
;
365 HashPos
= 1637*HashPos
+ '/';
366 for (const char *I
= Nde
.File(); *I
!= 0; I
++)
367 HashPos
= 1637*HashPos
+ *I
;
368 return NodeP
+ HeaderP
->FileHash
+ (HashPos
% HeaderP
->HashSize
);
371 // FLCache::DropNode - Drop a node from the hash table /*{{{*/
372 // ---------------------------------------------------------------------
373 /* This erases a node from the hash table. Note that this does not unlink
374 the node from the package linked list. */
375 void pkgFLCache::DropNode(map_ptrloc N
)
380 NodeIterator
Nde(*this,NodeP
+ N
);
382 if (Nde
->NextPkg
!= 0)
383 _error
->Warning("DropNode called on still linked node");
385 // Locate it in the hash table
387 Node
*Hash
= HashNode(Nde
);
388 while (Hash
->Pointer
!= 0)
393 // Top of the bucket..
399 *Hash
= NodeP
[Hash
->Next
];
400 // Release Hash->Next
403 Last
->Next
= Hash
->Next
;
410 Hash
= NodeP
+ Hash
->Next
;
415 _error
->Error("Failed to locate the hash element!");
418 // FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
419 // ---------------------------------------------------------------------
420 /* Tag all the diversions as untouched */
421 void pkgFLCache::BeginDiverLoad()
423 for (DiverIterator I
= DiverBegin(); I
.end() == false; I
++)
427 // FLCache::FinishDiverLoad - Finish up a new diversion load /*{{{*/
428 // ---------------------------------------------------------------------
429 /* This drops any untouched diversions. In effect removing any diversions
430 that where not loaded (ie missing from the diversion file) */
431 void pkgFLCache::FinishDiverLoad()
433 map_ptrloc
*Cur
= &HeaderP
->Diversions
;
436 Diversion
*Div
= DiverP
+ *Cur
;
437 if ((Div
->Flags
& Diversion::Touched
) == Diversion::Touched
)
444 DropNode(Div
->DivertTo
);
445 DropNode(Div
->DivertFrom
);
450 // FLCache::AddDiversion - Add a new diversion /*{{{*/
451 // ---------------------------------------------------------------------
452 /* Add a new diversion to the diverion tables and make sure that it is
453 unique and non-chaining. */
454 bool pkgFLCache::AddDiversion(PkgIterator
const &Owner
,
455 const char *From
,const char *To
)
457 /* Locate the two hash nodes we are going to manipulate. If there
458 are pre-existing diversions then they will be returned */
459 NodeIterator FromN
= GetNode(From
,From
+strlen(From
),0,true,true);
460 NodeIterator ToN
= GetNode(To
,To
+strlen(To
),0,true,true);
461 if (FromN
.end() == true || ToN
.end() == true)
462 return _error
->Error("Failed to allocate diversion");
464 // Should never happen
465 if ((FromN
->Flags
& Node::Diversion
) != Node::Diversion
||
466 (ToN
->Flags
& Node::Diversion
) != Node::Diversion
)
467 return _error
->Error("Internal Error in AddDiversion");
469 // Now, try to reclaim an existing diversion..
470 map_ptrloc Diver
= 0;
471 if (FromN
->Pointer
!= 0)
472 Diver
= FromN
->Pointer
;
474 /* Make sure from and to point to the same diversion, if they dont
475 then we are trying to intermix diversions - very bad */
476 if (ToN
->Pointer
!= 0 && ToN
->Pointer
!= Diver
)
478 // It could be that the other diversion is no longer in use
479 if ((DiverP
[ToN
->Pointer
].Flags
& Diversion::Touched
) == Diversion::Touched
)
480 return _error
->Error("Trying to overwrite a diversion, %s -> %s and %s/%s",
481 From
,To
,ToN
.File(),ToN
.Dir().Name());
484 Diversion
*Div
= DiverP
+ ToN
->Pointer
;
487 if (Div
->DivertTo
== ToN
.Offset())
489 if (Div
->DivertFrom
== ToN
.Offset())
492 // This diversion will be cleaned up by FinishDiverLoad
495 // Allocate a new diversion
498 Diver
= Map
.Allocate(sizeof(Diversion
));
501 DiverP
[Diver
].Next
= HeaderP
->Diversions
;
502 HeaderP
->Diversions
= Diver
;
503 HeaderP
->DiversionCount
++;
506 // Can only have one diversion of the same files
507 Diversion
*Div
= DiverP
+ Diver
;
508 if ((Div
->Flags
& Diversion::Touched
) == Diversion::Touched
)
509 return _error
->Error("Double add of diversion %s -> %s",From
,To
);
511 // Setup the From/To links
512 if (Div
->DivertFrom
!= FromN
.Offset() && Div
->DivertFrom
!= ToN
.Offset())
513 DropNode(Div
->DivertFrom
);
514 Div
->DivertFrom
= FromN
.Offset();
515 if (Div
->DivertTo
!= FromN
.Offset() && Div
->DivertTo
!= ToN
.Offset())
516 DropNode(Div
->DivertTo
);
517 Div
->DivertTo
= ToN
.Offset();
519 // Link it to the two nodes
520 FromN
->Pointer
= Diver
;
521 ToN
->Pointer
= Diver
;
524 Div
->OwnerPkg
= Owner
.Offset();
525 Div
->Flags
|= Diversion::Touched
;
530 // FLCache::AddConfFile - Add a new configuration file /*{{{*/
531 // ---------------------------------------------------------------------
532 /* This simply adds a new conf file node to the hash table. This is only
533 used by the status file reader. It associates a hash with each conf
534 file entry that exists in the status file and the list file for
535 the proper package. Duplicate conf files (across packages) are left
536 up to other routines to deal with. */
537 bool pkgFLCache::AddConfFile(const char *Name
,const char *NameEnd
,
538 PkgIterator
const &Owner
,
539 const unsigned char *Sum
)
541 NodeIterator Nde
= GetNode(Name
,NameEnd
,0,false,false);
542 if (Nde
.end() == true)
545 unsigned long File
= Nde
->File
;
546 for (; Nde
->File
== File
&& Nde
.end() == false; Nde
++)
548 if (Nde
.RealPackage() != Owner
)
551 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
552 return _error
->Error("Duplicate conf file %s/%s",Nde
.DirN(),Nde
.File());
554 // Allocate a new conf file structure
555 map_ptrloc Conf
= Map
.Allocate(sizeof(ConfFile
));
558 ConfP
[Conf
].OwnerPkg
= Owner
.Offset();
559 memcpy(ConfP
[Conf
].MD5
,Sum
,sizeof(ConfP
[Conf
].MD5
));
562 Nde
->Flags
|= Node::ConfFile
;
566 /* This means the conf file has been replaced, but the entry in the
567 status file was not updated */
572 // NodeIterator::RealPackage - Return the package for this node /*{{{*/
573 // ---------------------------------------------------------------------
574 /* Since the package pointer is indirected in all sorts of interesting ways
575 this is used to get a pointer to the owning package */
576 pkgFLCache::Package
*pkgFLCache::NodeIterator::RealPackage() const
578 if (Nde
->Pointer
== 0)
581 if ((Nde
->Flags
& Node::ConfFile
) == Node::ConfFile
)
582 return Owner
->PkgP
+ Owner
->ConfP
[Nde
->Pointer
].OwnerPkg
;
584 // Diversions are ignored
585 if ((Nde
->Flags
& Node::Diversion
) == Node::Diversion
)
588 return Owner
->PkgP
+ Nde
->Pointer
;