123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- // -*- mode: cpp; mode: fold -*-
- // Description /*{{{*/
- // $Id: filelist.cc,v 1.4.2.1 2004/01/16 18:58:50 mdz Exp $
- /* ######################################################################
- File Listing - Manages a Cache of File -> Package names.
- Diversions add some significant complexity to the system. To keep
- storage space down in the very special case of a diverted file no
- extra bytes are allocated in the Node structure. Instead a diversion
- is inserted directly into the hash table and its flag bit set. Every
- lookup for that filename will always return the diversion.
-
- The hash buckets are stored in sorted form, with diversions having
- the highest sort order. Identical files are assigned the same file
- pointer, thus after a search all of the nodes owning that file can be
- found by iterating down the bucket.
-
- Re-updates of diversions (another extremely special case) are done by
- marking all diversions as untouched, then loading the entire diversion
- list again, touching each diversion and then finally going back and
- releasing all untouched diversions. It is assumed that the diversion
- table will always be quite small and be a very irregular case.
- Diversions that are user-installed are represented by a package with
- an empty name string.
- Conf files are handled like diversions by changing the meaning of the
- Pointer field to point to a conf file entry - again to reduce over
- head for a special case.
-
- ##################################################################### */
- /*}}}*/
- // Include Files /*{{{*/
- #include<config.h>
- #include <apt-pkg/filelist.h>
- #include <apt-pkg/mmap.h>
- #include <apt-pkg/error.h>
- #include <apt-pkg/strutl.h>
- #include <string.h>
- #include <iostream>
- #include <apti18n.h>
- /*}}}*/
- using namespace std;
- // FlCache::Header::Header - Constructor /*{{{*/
- // ---------------------------------------------------------------------
- /* Initialize the header variables. These are the defaults used when
- creating new caches */
- pkgFLCache::Header::Header()
- {
- Signature = 0xEA3F1295;
-
- /* Whenever the structures change the major version should be bumped,
- whenever the generator changes the minor version should be bumped. */
- MajorVersion = 1;
- MinorVersion = 0;
- Dirty = true;
-
- HeaderSz = sizeof(pkgFLCache::Header);
- NodeSz = sizeof(pkgFLCache::Node);
- DirSz = sizeof(pkgFLCache::Directory);
- PackageSz = sizeof(pkgFLCache::Package);
- DiversionSz = sizeof(pkgFLCache::Diversion);
- ConfFileSz = sizeof(pkgFLCache::ConfFile);
-
- NodeCount = 0;
- DirCount = 0;
- PackageCount = 0;
- DiversionCount = 0;
- ConfFileCount = 0;
- HashSize = 1 << 14;
- FileHash = 0;
- DirTree = 0;
- Packages = 0;
- Diversions = 0;
- UniqNodes = 0;
- memset(Pools,0,sizeof(Pools));
- }
- /*}}}*/
- // FLCache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
- // ---------------------------------------------------------------------
- /* Compare to make sure we are matching versions */
- APT_PURE bool pkgFLCache::Header::CheckSizes(Header &Against) const
- {
- if (HeaderSz == Against.HeaderSz &&
- NodeSz == Against.NodeSz &&
- DirSz == Against.DirSz &&
- DiversionSz == Against.DiversionSz &&
- PackageSz == Against.PackageSz &&
- ConfFileSz == Against.ConfFileSz)
- return true;
- return false;
- }
- /*}}}*/
- // FLCache::pkgFLCache - Constructor /*{{{*/
- // ---------------------------------------------------------------------
- /* If this is a new cache then a new header and hash table are instantaited
- otherwise the existing ones are mearly attached */
- pkgFLCache::pkgFLCache(DynamicMMap &Map) : Map(Map)
- {
- if (_error->PendingError() == true)
- return;
- LastTreeLookup = 0;
- LastLookupSize = 0;
-
- // Apply the typecasts
- HeaderP = (Header *)Map.Data();
- NodeP = (Node *)Map.Data();
- DirP = (Directory *)Map.Data();
- DiverP = (Diversion *)Map.Data();
- PkgP = (Package *)Map.Data();
- ConfP = (ConfFile *)Map.Data();
- StrP = (char *)Map.Data();
- AnyP = (unsigned char *)Map.Data();
-
- // New mapping, create the basic cache structures
- if (Map.Size() == 0)
- {
- Map.RawAllocate(sizeof(pkgFLCache::Header));
- *HeaderP = pkgFLCache::Header();
- HeaderP->FileHash = Map.RawAllocate(sizeof(pkgFLCache::Node)*HeaderP->HashSize,
- sizeof(pkgFLCache::Node))/sizeof(pkgFLCache::Node);
- }
- FileHash = NodeP + HeaderP->FileHash;
-
- // Setup the dynamic map manager
- HeaderP->Dirty = true;
- Map.Sync(0,sizeof(pkgFLCache::Header));
- Map.UsePools(*HeaderP->Pools,sizeof(HeaderP->Pools)/sizeof(HeaderP->Pools[0]));
- }
- /*}}}*/
- // FLCache::TreeLookup - Perform a lookup in a generic tree /*{{{*/
- // ---------------------------------------------------------------------
- /* This is a simple generic tree lookup. The first three entries of
- the Directory structure are used as a template, but any other similar
- structure could be used in it's place. */
- map_ptrloc pkgFLCache::TreeLookup(map_ptrloc *Base,const char *Text,
- const char *TextEnd,unsigned long Size,
- unsigned int *Count,bool Insert)
- {
- pkgFLCache::Directory *Dir;
-
- // Check our last entry cache
- if (LastTreeLookup != 0 && LastLookupSize == Size)
- {
- Dir = (pkgFLCache::Directory *)(AnyP + LastTreeLookup*Size);
- if (stringcmp(Text,TextEnd,StrP + Dir->Name) == 0)
- return LastTreeLookup;
- }
-
- while (1)
- {
- // Allocate a new one
- if (*Base == 0)
- {
- if (Insert == false)
- return 0;
-
- *Base = Map.Allocate(Size);
- if (*Base == 0)
- return 0;
-
- (*Count)++;
- Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
- Dir->Name = Map.WriteString(Text,TextEnd - Text);
- LastTreeLookup = *Base;
- LastLookupSize = Size;
- return *Base;
- }
-
- // Compare this node
- Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
- int Res = stringcmp(Text,TextEnd,StrP + Dir->Name);
- if (Res == 0)
- {
- LastTreeLookup = *Base;
- LastLookupSize = Size;
- return *Base;
- }
-
- if (Res > 0)
- Base = &Dir->Left;
- if (Res < 0)
- Base = &Dir->Right;
- }
- }
- /*}}}*/
- // FLCache::PrintTree - Print out a tree /*{{{*/
- // ---------------------------------------------------------------------
- /* This is a simple generic tree dumper, ment for debugging. */
- void pkgFLCache::PrintTree(map_ptrloc Base,unsigned long Size)
- {
- if (Base == 0)
- return;
-
- pkgFLCache::Directory *Dir = (pkgFLCache::Directory *)(AnyP + Base*Size);
- PrintTree(Dir->Left,Size);
- cout << (StrP + Dir->Name) << endl;
- PrintTree(Dir->Right,Size);
- }
- /*}}}*/
- // FLCache::GetPkg - Get a package pointer /*{{{*/
- // ---------------------------------------------------------------------
- /* Locate a package by name in it's tree, this is just a wrapper for
- TreeLookup */
- pkgFLCache::PkgIterator pkgFLCache::GetPkg(const char *Name,const char *NameEnd,
- bool Insert)
- {
- if (NameEnd == 0)
- NameEnd = Name + strlen(Name);
-
- map_ptrloc Pos = TreeLookup(&HeaderP->Packages,Name,NameEnd,
- sizeof(pkgFLCache::Package),
- &HeaderP->PackageCount,Insert);
- if (Pos == 0)
- return pkgFLCache::PkgIterator();
- return pkgFLCache::PkgIterator(*this,PkgP + Pos);
- }
- /*}}}*/
- // FLCache::GetNode - Get the node associated with the filename /*{{{*/
- // ---------------------------------------------------------------------
- /* Lookup a node in the hash table. If Insert is true then a new node is
- always inserted. The hash table can have multiple instances of a
- single name available. A search returns the first. It is important
- that additions for the same name insert after the first entry of
- the name group. */
- pkgFLCache::NodeIterator pkgFLCache::GetNode(const char *Name,
- const char *NameEnd,
- map_ptrloc Loc,
- bool Insert,bool Divert)
- {
- // Split the name into file and directory, hashing as it is copied
- const char *File = Name;
- unsigned long HashPos = 0;
- for (const char *I = Name; I < NameEnd; I++)
- {
- HashPos = 1637*HashPos + *I;
- if (*I == '/')
- File = I;
- }
-
- // Search for it
- Node *Hash = NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
- int Res = 0;
- map_ptrloc FilePtr = 0;
- while (Hash->Pointer != 0)
- {
- // Compare
- Res = stringcmp(File+1,NameEnd,StrP + Hash->File);
- if (Res == 0)
- Res = stringcmp(Name,File,StrP + DirP[Hash->Dir].Name);
-
- // Diversion?
- if (Res == 0 && Insert == true)
- {
- /* Dir and File match exactly, we need to reuse the file name
- when we link it in */
- FilePtr = Hash->File;
- Res = Divert - ((Hash->Flags & Node::Diversion) == Node::Diversion);
- }
-
- // Is a match
- if (Res == 0)
- {
- if (Insert == false)
- return NodeIterator(*this,Hash);
-
- // Only one diversion per name!
- if (Divert == true)
- return NodeIterator(*this,Hash);
- break;
- }
-
- // Out of sort order
- if (Res > 0)
- break;
-
- if (Hash->Next != 0)
- Hash = NodeP + Hash->Next;
- else
- break;
- }
-
- // Fail, not found
- if (Insert == false)
- return NodeIterator(*this);
- // Find a directory node
- map_ptrloc Dir = TreeLookup(&HeaderP->DirTree,Name,File,
- sizeof(pkgFLCache::Directory),
- &HeaderP->DirCount,true);
- if (Dir == 0)
- return NodeIterator(*this);
- // Allocate a new node
- if (Hash->Pointer != 0)
- {
- // Overwrite or append
- if (Res > 0)
- {
- Node *Next = NodeP + Map.Allocate(sizeof(*Hash));
- if (Next == NodeP)
- return NodeIterator(*this);
- *Next = *Hash;
- Hash->Next = Next - NodeP;
- }
- else
- {
- unsigned long NewNext = Map.Allocate(sizeof(*Hash));
- if (NewNext == 0)
- return NodeIterator(*this);
- NodeP[NewNext].Next = Hash->Next;
- Hash->Next = NewNext;
- Hash = NodeP + Hash->Next;
- }
- }
-
- // Insert into the new item
- Hash->Dir = Dir;
- Hash->Pointer = Loc;
- Hash->Flags = 0;
- if (Divert == true)
- Hash->Flags |= Node::Diversion;
-
- if (FilePtr != 0)
- Hash->File = FilePtr;
- else
- {
- HeaderP->UniqNodes++;
- Hash->File = Map.WriteString(File+1,NameEnd - File-1);
- }
-
- // Link the node to the package list
- if (Divert == false && Loc == 0)
- {
- Hash->Next = PkgP[Loc].Files;
- PkgP[Loc].Files = Hash - NodeP;
- }
-
- HeaderP->NodeCount++;
- return NodeIterator(*this,Hash);
- }
- /*}}}*/
- // FLCache::HashNode - Return the hash bucket for the node /*{{{*/
- // ---------------------------------------------------------------------
- /* This is one of two hashing functions. The other is inlined into the
- GetNode routine. */
- APT_PURE pkgFLCache::Node *pkgFLCache::HashNode(NodeIterator const &Nde)
- {
- // Hash the node
- unsigned long HashPos = 0;
- for (const char *I = Nde.DirN(); *I != 0; I++)
- HashPos = 1637*HashPos + *I;
- HashPos = 1637*HashPos + '/';
- for (const char *I = Nde.File(); *I != 0; I++)
- HashPos = 1637*HashPos + *I;
- return NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
- }
- /*}}}*/
- // FLCache::DropNode - Drop a node from the hash table /*{{{*/
- // ---------------------------------------------------------------------
- /* This erases a node from the hash table. Note that this does not unlink
- the node from the package linked list. */
- void pkgFLCache::DropNode(map_ptrloc N)
- {
- if (N == 0)
- return;
-
- NodeIterator Nde(*this,NodeP + N);
-
- if (Nde->NextPkg != 0)
- _error->Warning(_("DropNode called on still linked node"));
-
- // Locate it in the hash table
- Node *Last = 0;
- Node *Hash = HashNode(Nde);
- while (Hash->Pointer != 0)
- {
- // Got it
- if (Hash == Nde)
- {
- // Top of the bucket..
- if (Last == 0)
- {
- Hash->Pointer = 0;
- if (Hash->Next == 0)
- return;
- *Hash = NodeP[Hash->Next];
- // Release Hash->Next
- return;
- }
- Last->Next = Hash->Next;
- // Release Hash
- return;
- }
-
- Last = Hash;
- if (Hash->Next != 0)
- Hash = NodeP + Hash->Next;
- else
- break;
- }
-
- _error->Error(_("Failed to locate the hash element!"));
- }
- /*}}}*/
- // FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
- // ---------------------------------------------------------------------
- /* Tag all the diversions as untouched */
- void pkgFLCache::BeginDiverLoad()
- {
- for (DiverIterator I = DiverBegin(); I.end() == false; I++)
- I->Flags = 0;
- }
- /*}}}*/
- // FLCache::FinishDiverLoad - Finish up a new diversion load /*{{{*/
- // ---------------------------------------------------------------------
- /* This drops any untouched diversions. In effect removing any diversions
- that where not loaded (ie missing from the diversion file) */
- void pkgFLCache::FinishDiverLoad()
- {
- map_ptrloc *Cur = &HeaderP->Diversions;
- while (*Cur != 0)
- {
- Diversion *Div = DiverP + *Cur;
- if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
- {
- Cur = &Div->Next;
- continue;
- }
-
- // Purge!
- DropNode(Div->DivertTo);
- DropNode(Div->DivertFrom);
- *Cur = Div->Next;
- }
- }
- /*}}}*/
- // FLCache::AddDiversion - Add a new diversion /*{{{*/
- // ---------------------------------------------------------------------
- /* Add a new diversion to the diverion tables and make sure that it is
- unique and non-chaining. */
- bool pkgFLCache::AddDiversion(PkgIterator const &Owner,
- const char *From,const char *To)
- {
- /* Locate the two hash nodes we are going to manipulate. If there
- are pre-existing diversions then they will be returned */
- NodeIterator FromN = GetNode(From,From+strlen(From),0,true,true);
- NodeIterator ToN = GetNode(To,To+strlen(To),0,true,true);
- if (FromN.end() == true || ToN.end() == true)
- return _error->Error(_("Failed to allocate diversion"));
- // Should never happen
- if ((FromN->Flags & Node::Diversion) != Node::Diversion ||
- (ToN->Flags & Node::Diversion) != Node::Diversion)
- return _error->Error(_("Internal error in AddDiversion"));
- // Now, try to reclaim an existing diversion..
- map_ptrloc Diver = 0;
- if (FromN->Pointer != 0)
- Diver = FromN->Pointer;
-
- /* Make sure from and to point to the same diversion, if they don't
- then we are trying to intermix diversions - very bad */
- if (ToN->Pointer != 0 && ToN->Pointer != Diver)
- {
- // It could be that the other diversion is no longer in use
- if ((DiverP[ToN->Pointer].Flags & Diversion::Touched) == Diversion::Touched)
- return _error->Error(_("Trying to overwrite a diversion, %s -> %s and %s/%s"),
- From,To,ToN.File(),ToN.Dir().Name());
-
- // We can erase it.
- Diversion *Div = DiverP + ToN->Pointer;
- ToN->Pointer = 0;
-
- if (Div->DivertTo == ToN.Offset())
- Div->DivertTo = 0;
- if (Div->DivertFrom == ToN.Offset())
- Div->DivertFrom = 0;
-
- // This diversion will be cleaned up by FinishDiverLoad
- }
-
- // Allocate a new diversion
- if (Diver == 0)
- {
- Diver = Map.Allocate(sizeof(Diversion));
- if (Diver == 0)
- return false;
- DiverP[Diver].Next = HeaderP->Diversions;
- HeaderP->Diversions = Diver;
- HeaderP->DiversionCount++;
- }
- // Can only have one diversion of the same files
- Diversion *Div = DiverP + Diver;
- if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
- return _error->Error(_("Double add of diversion %s -> %s"),From,To);
-
- // Setup the From/To links
- if (Div->DivertFrom != FromN.Offset() && Div->DivertFrom != ToN.Offset())
- DropNode(Div->DivertFrom);
- Div->DivertFrom = FromN.Offset();
- if (Div->DivertTo != FromN.Offset() && Div->DivertTo != ToN.Offset())
- DropNode(Div->DivertTo);
- Div->DivertTo = ToN.Offset();
-
- // Link it to the two nodes
- FromN->Pointer = Diver;
- ToN->Pointer = Diver;
-
- // And the package
- Div->OwnerPkg = Owner.Offset();
- Div->Flags |= Diversion::Touched;
-
- return true;
- }
- /*}}}*/
- // FLCache::AddConfFile - Add a new configuration file /*{{{*/
- // ---------------------------------------------------------------------
- /* This simply adds a new conf file node to the hash table. This is only
- used by the status file reader. It associates a hash with each conf
- file entry that exists in the status file and the list file for
- the proper package. Duplicate conf files (across packages) are left
- up to other routines to deal with. */
- bool pkgFLCache::AddConfFile(const char *Name,const char *NameEnd,
- PkgIterator const &Owner,
- const unsigned char *Sum)
- {
- NodeIterator Nde = GetNode(Name,NameEnd,0,false,false);
- if (Nde.end() == true)
- return true;
-
- unsigned long File = Nde->File;
- for (; Nde->File == File && Nde.end() == false; Nde++)
- {
- if (Nde.RealPackage() != Owner)
- continue;
- if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
- return _error->Error(_("Duplicate conf file %s/%s"),Nde.DirN(),Nde.File());
-
- // Allocate a new conf file structure
- map_ptrloc Conf = Map.Allocate(sizeof(ConfFile));
- if (Conf == 0)
- return false;
- ConfP[Conf].OwnerPkg = Owner.Offset();
- memcpy(ConfP[Conf].MD5,Sum,sizeof(ConfP[Conf].MD5));
-
- Nde->Pointer = Conf;
- Nde->Flags |= Node::ConfFile;
- return true;
- }
-
- /* This means the conf file has been replaced, but the entry in the
- status file was not updated */
- return true;
- }
- /*}}}*/
- // NodeIterator::RealPackage - Return the package for this node /*{{{*/
- // ---------------------------------------------------------------------
- /* Since the package pointer is indirected in all sorts of interesting ways
- this is used to get a pointer to the owning package */
- APT_PURE pkgFLCache::Package *pkgFLCache::NodeIterator::RealPackage() const
- {
- if (Nde->Pointer == 0)
- return 0;
-
- if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
- return Owner->PkgP + Owner->ConfP[Nde->Pointer].OwnerPkg;
- // Diversions are ignored
- if ((Nde->Flags & Node::Diversion) == Node::Diversion)
- return 0;
-
- return Owner->PkgP + Nde->Pointer;
- }
- /*}}}*/
|