diff options
Diffstat (limited to 'apt-inst/filelist.cc')
-rw-r--r-- | apt-inst/filelist.cc | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/apt-inst/filelist.cc b/apt-inst/filelist.cc new file mode 100644 index 000000000..211fc935e --- /dev/null +++ b/apt-inst/filelist.cc @@ -0,0 +1,588 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: filelist.cc,v 1.2 2001/02/20 07:03:16 jgg Exp $ +/* ###################################################################### + + File Listing - Manages a Cache of File -> Package names. + + Diversions add some signficant 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 higest 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 /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "apt-pkg/filelist.h" +#endif + +#include <apt-pkg/filelist.h> +#include <apt-pkg/mmap.h> +#include <apt-pkg/error.h> +#include <apt-pkg/strutl.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> + /*}}}*/ + +// 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 */ +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. */ +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 dont + 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 */ +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; +} + /*}}}*/ |