Browse Source

Get rid of pkgExtract and pkgFLCache

These classes are not actually being used, they were part of the
dpkg replacement that never happened.
tags/devuan/2.0.1+devuan1
Julian Andres Klode 1 year ago
parent
commit
d03105d9fd
4 changed files with 0 additions and 1461 deletions
  1. +0
    -514
      apt-inst/extract.cc
  2. +0
    -49
      apt-inst/extract.h
  3. +0
    -586
      apt-inst/filelist.cc
  4. +0
    -312
      apt-inst/filelist.h

+ 0
- 514
apt-inst/extract.cc View File

@@ -1,514 +0,0 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* ######################################################################

Archive Extraction Directory Stream
Extraction for each file is a bit of an involved process. Each object
undergoes an atomic backup, overwrite, erase sequence. First the
object is unpacked to '.dpkg.new' then the original is hardlinked to
'.dpkg.tmp' and finally the new object is renamed to overwrite the old
one. From an external perspective the file never ceased to exist.
After the archive has been successfully unpacked the .dpkg.tmp files
are erased. A failure causes all the .dpkg.tmp files to be restored.
Decisions about unpacking go like this:
- Store the original filename in the file listing
- Resolve any diversions that would effect this file, all checks
below apply to the diverted name, not the real one.
- Resolve any symlinked configuration files.
- If the existing file does not exist then .dpkg-tmp is checked for.
[Note, this is reduced to only check if a file was expected to be
there]
- If the existing link/file is not a directory then it is replaced
regardless
- If the existing link/directory is being replaced by a directory then
absolutely nothing happens.
- If the existing link/directory is being replaced by a link then
absolutely nothing happens.
- If the existing link/directory is being replaced by a non-directory
then this will abort if the package is not the sole owner of the
directory. [Note, this is changed to not happen if the directory
non-empty - that is, it only includes files that are part of this
package - prevents removing user files accidentally.]
- If the non-directory exists in the listing database and it
does not belong to the current package then an overwrite condition
is invoked.
As we unpack we record the file list differences in the FL cache. If
we need to unroll the FL cache knows which files have been unpacked
and can undo. When we need to erase then it knows which files have not
been unpacked.
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
#include <config.h>

#include <apt-pkg/debversion.h>
#include <apt-pkg/dirstream.h>
#include <apt-pkg/error.h>
#include <apt-pkg/extract.h>
#include <apt-pkg/filelist.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/mmap.h>
#include <apt-pkg/pkgcache.h>

#include <iostream>
#include <string>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#include <apti18n.h>
/*}}}*/
using namespace std;

static const char *TempExt = "dpkg-tmp";
//static const char *NewExt = "dpkg-new";

// Extract::pkgExtract - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
pkgExtract::pkgExtract(pkgFLCache &FLCache,pkgCache::VerIterator Ver) :
FLCache(FLCache), Ver(Ver)
{
FLPkg = FLCache.GetPkg(Ver.ParentPkg().Name(),true);
if (FLPkg.end() == true)
return;
Debug = true;
}
/*}}}*/
// Extract::DoItem - Handle a single item from the stream /*{{{*/
// ---------------------------------------------------------------------
/* This performs the setup for the extraction.. */
bool pkgExtract::DoItem(Item &Itm, int &/*Fd*/)
{
/* Strip any leading/trailing /s from the filename, then copy it to the
temp buffer and re-apply the leading / We use a class variable
to store the new filename for use by the three extraction funcs */
char *End = FileName+1;
const char *I = Itm.Name;
for (; *I != 0 && *I == '/'; I++);
*FileName = '/';
for (; *I != 0 && End < FileName + sizeof(FileName); I++, End++)
*End = *I;
if (End + 20 >= FileName + sizeof(FileName))
return _error->Error(_("The path %s is too long"),Itm.Name);
for (; End > FileName && End[-1] == '/'; End--);
*End = 0;
Itm.Name = FileName;
/* Lookup the file. Nde is the file [group] we are going to write to and
RealNde is the actual node we are manipulating. Due to diversions
they may be entirely different. */
pkgFLCache::NodeIterator Nde = FLCache.GetNode(Itm.Name,End,0,false,false);
pkgFLCache::NodeIterator RealNde = Nde;
// See if the file is already in the file listing
unsigned long FileGroup = RealNde->File;
for (; RealNde.end() == false && FileGroup == RealNde->File; RealNde++)
if (RealNde.RealPackage() == FLPkg)
break;

// Nope, create an entry
if (RealNde.end() == true)
{
RealNde = FLCache.GetNode(Itm.Name,End,FLPkg.Offset(),true,false);
if (RealNde.end() == true)
return false;
RealNde->Flags |= pkgFLCache::Node::NewFile;
}

/* Check if this entry already was unpacked. The only time this should
ever happen is if someone has hacked tar to support capabilities, in
which case this needs to be modified anyhow.. */
if ((RealNde->Flags & pkgFLCache::Node::Unpacked) ==
pkgFLCache::Node::Unpacked)
return _error->Error(_("Unpacking %s more than once"),Itm.Name);
if (Nde.end() == true)
Nde = RealNde;

/* Consider a diverted file - We are not permitted to divert directories,
but everything else is fair game (including conf files!) */
if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
{
if (Itm.Type == Item::Directory)
return _error->Error(_("The directory %s is diverted"),Itm.Name);

/* A package overwriting a diversion target is just the same as
overwriting a normally owned file and is checked for below in
the overwrites mechanism */

/* If this package is trying to overwrite the target of a diversion,
that is never, ever permitted */
pkgFLCache::DiverIterator Div = Nde.Diversion();
if (Div.DivertTo() == Nde)
return _error->Error(_("The package is trying to write to the "
"diversion target %s/%s"),Nde.DirN(),Nde.File());
// See if it is us and we are following it in the right direction
if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
{
Nde = Div.DivertTo();
End = FileName + snprintf(FileName,sizeof(FileName)-20,"%s/%s",
Nde.DirN(),Nde.File());
if (End <= FileName)
return _error->Error(_("The diversion path is too long"));
}
}
// Deal with symlinks and conf files
if ((RealNde->Flags & pkgFLCache::Node::NewConfFile) ==
pkgFLCache::Node::NewConfFile)
{
string Res = flNoLink(Itm.Name);
if (Res.length() > sizeof(FileName))
return _error->Error(_("The path %s is too long"),Res.c_str());
if (Debug == true)
clog << "Followed conf file from " << FileName << " to " << Res << endl;
Itm.Name = strcpy(FileName,Res.c_str());
}
/* Get information about the existing file, and attempt to restore
a backup if it does not exist */
struct stat LExisting;
bool EValid = false;
if (lstat(Itm.Name,&LExisting) != 0)
{
// This is bad news.
if (errno != ENOENT)
return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
// See if we can recover the backup file
if (Nde.end() == false)
{
char Temp[sizeof(FileName)];
snprintf(Temp,sizeof(Temp),"%s.%s",Itm.Name,TempExt);
if (rename(Temp,Itm.Name) != 0 && errno != ENOENT)
return _error->Errno("rename",_("Failed to rename %s to %s"),
Temp,Itm.Name);
if (stat(Itm.Name,&LExisting) != 0)
{
if (errno != ENOENT)
return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
}
else
EValid = true;
}
}
else
EValid = true;
/* If the file is a link we need to stat its destination, get the
existing file modes */
struct stat Existing = LExisting;
if (EValid == true && S_ISLNK(Existing.st_mode))
{
if (stat(Itm.Name,&Existing) != 0)
{
if (errno != ENOENT)
return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
Existing = LExisting;
}
}
// We pretend a non-existing file looks like it is a normal file
if (EValid == false)
Existing.st_mode = S_IFREG;
/* Okay, at this point 'Existing' is the stat information for the
real non-link file */
/* The only way this can be a no-op is if a directory is being
replaced by a directory or by a link */
if (S_ISDIR(Existing.st_mode) != 0 &&
(Itm.Type == Item::Directory || Itm.Type == Item::SymbolicLink))
return true;
/* Non-Directory being replaced by non-directory. We check for over
writes here. */
if (Nde.end() == false)
{
if (HandleOverwrites(Nde) == false)
return false;
}
/* Directory being replaced by a non-directory - this needs to see if
the package is the owner and then see if the directory would be
empty after the package is removed [ie no user files will be
erased] */
if (S_ISDIR(Existing.st_mode) != 0)
{
if (CheckDirReplace(Itm.Name) == false)
return _error->Error(_("The directory %s is being replaced by a non-directory"),Itm.Name);
}
if (Debug == true)
clog << "Extract " << string(Itm.Name,End) << endl;
/* if (Count != 0)
return _error->Error(_("Done"));*/
return true;
}
/*}}}*/
// Extract::Finished - Sequence finished, erase the temp files /*{{{*/
// ---------------------------------------------------------------------
/* */
APT_PURE bool pkgExtract::Finished()
{
return true;
}
/*}}}*/
// Extract::Aborted - Sequence aborted, undo all our unpacking /*{{{*/
// ---------------------------------------------------------------------
/* This undoes everything that was done by all calls to the DoItem method
and restores the File Listing cache to its original form. It bases its
actions on the flags value for each node in the cache. */
bool pkgExtract::Aborted()
{
if (Debug == true)
clog << "Aborted, backing out" << endl;
pkgFLCache::NodeIterator Files = FLPkg.Files();
map_ptrloc *Last = &FLPkg->Files;
/* Loop over all files, restore those that have been unpacked from their
dpkg-tmp entries */
while (Files.end() == false)
{
// Locate the hash bucket for the node and locate its group head
pkgFLCache::NodeIterator Nde(FLCache,FLCache.HashNode(Files));
for (; Nde.end() == false && Files->File != Nde->File; Nde++);
if (Nde.end() == true)
return _error->Error(_("Failed to locate node in its hash bucket"));
if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
Nde.DirN(),Nde.File()) <= 0)
return _error->Error(_("The path is too long"));
// Deal with diversions
if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
{
pkgFLCache::DiverIterator Div = Nde.Diversion();
// See if it is us and we are following it in the right direction
if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
{
Nde = Div.DivertTo();
if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
Nde.DirN(),Nde.File()) <= 0)
return _error->Error(_("The diversion path is too long"));
}
}
// Deal with overwrites+replaces
for (; Nde.end() == false && Files->File == Nde->File; Nde++)
{
if ((Nde->Flags & pkgFLCache::Node::Replaced) ==
pkgFLCache::Node::Replaced)
{
if (Debug == true)
clog << "De-replaced " << FileName << " from " << Nde.RealPackage()->Name << endl;
Nde->Flags &= ~pkgFLCache::Node::Replaced;
}
}
// Undo the change in the filesystem
if (Debug == true)
clog << "Backing out " << FileName;
// Remove a new node
if ((Files->Flags & pkgFLCache::Node::NewFile) ==
pkgFLCache::Node::NewFile)
{
if (Debug == true)
clog << " [new node]" << endl;
pkgFLCache::Node *Tmp = Files;
Files++;
*Last = Tmp->NextPkg;
Tmp->NextPkg = 0;

FLCache.DropNode(Tmp - FLCache.NodeP);
}
else
{
if (Debug == true)
clog << endl;
Last = &Files->NextPkg;
Files++;
}
}
return true;
}
/*}}}*/
// Extract::Fail - Extraction of a file Failed /*{{{*/
// ---------------------------------------------------------------------
/* */
bool pkgExtract::Fail(Item &Itm,int Fd)
{
return pkgDirStream::Fail(Itm,Fd);
}
/*}}}*/
// Extract::FinishedFile - Finished a file /*{{{*/
// ---------------------------------------------------------------------
/* */
bool pkgExtract::FinishedFile(Item &Itm,int Fd)
{
return pkgDirStream::FinishedFile(Itm,Fd);
}
/*}}}*/
// Extract::HandleOverwrites - See if a replaces covers this overwrite /*{{{*/
// ---------------------------------------------------------------------
/* Check if the file is in a package that is being replaced by this
package or if the file is being overwritten. Note that if the file
is really a directory but it has been erased from the filesystem
this will fail with an overwrite message. This is a limitation of the
dpkg file information format.
XX If a new package installs and another package replaces files in this
package what should we do? */
bool pkgExtract::HandleOverwrites(pkgFLCache::NodeIterator Nde,
bool DiverCheck)
{
pkgFLCache::NodeIterator TmpNde = Nde;
unsigned long DiverOwner = 0;
unsigned long FileGroup = Nde->File;
for (; Nde.end() == false && FileGroup == Nde->File; Nde++)
{
if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
{
/* Store the diversion owner if this is the forward direction
of the diversion */
if (DiverCheck == true)
DiverOwner = Nde.Diversion()->OwnerPkg;
continue;
}

pkgFLCache::PkgIterator FPkg(FLCache,Nde.RealPackage());
if (FPkg.end() == true || FPkg == FLPkg)
continue;
/* This tests trips when we are checking a diversion to see
if something has already been diverted by this diversion */
if (FPkg.Offset() == DiverOwner)
continue;

// Now see if this package matches one in a replace depends
pkgCache::DepIterator Dep = Ver.DependsList();
bool Ok = false;
for (; Dep.end() == false; ++Dep)
{
if (Dep->Type != pkgCache::Dep::Replaces)
continue;
// Does the replaces apply to this package?
if (strcmp(Dep.TargetPkg().Name(),FPkg.Name()) != 0)
continue;
/* Check the version for match. I do not think CurrentVer can be
0 if we are here.. */
pkgCache::PkgIterator Pkg = Dep.TargetPkg();
if (Pkg->CurrentVer == 0)
{
_error->Warning(_("Overwrite package match with no version for %s"),Pkg.Name());
continue;
}

// Replaces is met
if (debVS.CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,Dep.TargetVer()) == true)
{
if (Debug == true)
clog << "Replaced file " << Nde.DirN() << '/' << Nde.File() << " from " << Pkg.Name() << endl;
Nde->Flags |= pkgFLCache::Node::Replaced;
Ok = true;
break;
}
}
// Negative Hit
if (Ok == false)
return _error->Error(_("File %s/%s overwrites the one in the package %s"),
Nde.DirN(),Nde.File(),FPkg.Name());
}
/* If this is a diversion we might have to recurse to process
the other side of it */
if ((TmpNde->Flags & pkgFLCache::Node::Diversion) != 0)
{
pkgFLCache::DiverIterator Div = TmpNde.Diversion();
if (Div.DivertTo() == TmpNde)
return HandleOverwrites(Div.DivertFrom(),true);
}
return true;
}
/*}}}*/
// Extract::CheckDirReplace - See if this directory can be erased /*{{{*/
// ---------------------------------------------------------------------
/* If this directory is owned by a single package and that package is
replacing it with something non-directoryish then dpkg allows this.
We increase the requirement to be that the directory is non-empty after
the package is removed */
bool pkgExtract::CheckDirReplace(string Dir,unsigned int Depth)
{
// Looping?
if (Depth > 40)
return false;
if (Dir[Dir.size() - 1] != '/')
Dir += '/';
DIR *D = opendir(Dir.c_str());
if (D == 0)
return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());

string File;
for (struct dirent *Dent = readdir(D); Dent != 0; Dent = readdir(D))
{
// Skip some files
if (strcmp(Dent->d_name,".") == 0 ||
strcmp(Dent->d_name,"..") == 0)
continue;
// Look up the node
File = Dir + Dent->d_name;
pkgFLCache::NodeIterator Nde = FLCache.GetNode(File.c_str(),
File.c_str() + File.length(),0,false,false);

// The file is not owned by this package
if (Nde.end() != false || Nde.RealPackage() != FLPkg)
{
closedir(D);
return false;
}
// See if it is a directory
struct stat St;
if (lstat(File.c_str(),&St) != 0)
{
closedir(D);
return _error->Errno("lstat",_("Unable to stat %s"),File.c_str());
}
// Recurse down directories
if (S_ISDIR(St.st_mode) != 0)
{
if (CheckDirReplace(File,Depth + 1) == false)
{
closedir(D);
return false;
}
}
}
// No conflicts
closedir(D);
return true;
}
/*}}}*/

+ 0
- 49
apt-inst/extract.h View File

@@ -1,49 +0,0 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* ######################################################################

Archive Extraction Directory Stream
This Directory Stream implements extraction of an archive into the
filesystem. It makes the choices on what files should be unpacked and
replaces as well as guiding the actual unpacking.
When the unpacking sequence is completed one of the two functions,
Finished or Aborted must be called.
##################################################################### */
/*}}}*/
#ifndef PKGLIB_EXTRACT_H
#define PKGLIB_EXTRACT_H

#include <apt-pkg/dirstream.h>
#include <apt-pkg/filelist.h>
#include <apt-pkg/pkgcache.h>

#include <string>

class pkgExtract : public pkgDirStream
{
pkgFLCache &FLCache;
pkgCache::VerIterator Ver;
pkgFLCache::PkgIterator FLPkg;
char FileName[1024];
bool Debug;
bool HandleOverwrites(pkgFLCache::NodeIterator Nde,
bool DiverCheck = false);
bool CheckDirReplace(std::string Dir,unsigned int Depth = 0);
public:
virtual bool DoItem(Item &Itm,int &Fd) APT_OVERRIDE;
virtual bool Fail(Item &Itm,int Fd) APT_OVERRIDE;
virtual bool FinishedFile(Item &Itm,int Fd) APT_OVERRIDE;

bool Finished();
bool Aborted();
pkgExtract(pkgFLCache &FLCache,pkgCache::VerIterator Ver);
};

#endif

+ 0
- 586
apt-inst/filelist.cc View File

@@ -1,586 +0,0 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* ######################################################################

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/error.h>
#include <apt-pkg/filelist.h>
#include <apt-pkg/mmap.h>
#include <apt-pkg/strutl.h>

#include <iostream>
#include <string.h>
#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, meant 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;
}
/*}}}*/

+ 0
- 312
apt-inst/filelist.h View File

@@ -1,312 +0,0 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* ######################################################################

File Listing - Manages a Cache of File -> Package names.

This is identical to the Package cache, except that the generator
(which is much simpler) is integrated directly into the main class,
and it has been designed to handle live updates.
The storage content of the class is maintained in a memory map and is
written directly to the file system. Performance is traded against
space to give something that performs well and remains small.
The average per file usage is 32 bytes which yields about a meg every
36k files. Directory paths are collected into a binary tree and stored
only once, this offsets the cost of the hash nodes enough to keep
memory usage slightly less than the sum of the filenames.
The file names are stored into a fixed size chained hash table that is
linked to the package name and to the directory component.

Each file node has a set of associated flags that indicate the current
state of the file.
##################################################################### */
/*}}}*/
#ifndef PKGLIB_FILELIST_H
#define PKGLIB_FILELIST_H

#include <apt-pkg/mmap.h>

#include <cstring>
#include <string>

class pkgFLCache
{
public:
struct Header;
struct Node;
struct Directory;
struct Package;
struct Diversion;
struct ConfFile;

class NodeIterator;
class DirIterator;
class PkgIterator;
class DiverIterator;

protected:
std::string CacheFile;
DynamicMMap &Map;
map_ptrloc LastTreeLookup;
unsigned long LastLookupSize;

// Helpers for the addition algorithms
map_ptrloc TreeLookup(map_ptrloc *Base,const char *Text,const char *TextEnd,
unsigned long Size,unsigned int *Count = 0,
bool Insert = false);

public:

// Pointers to the arrays of items
Header *HeaderP;
Node *NodeP;
Directory *DirP;
Package *PkgP;
Diversion *DiverP;
ConfFile *ConfP;
char *StrP;
unsigned char *AnyP;

// Quick accessors
Node *FileHash;

// Accessors
Header &Head() {return *HeaderP;};
void PrintTree(map_ptrloc Base,unsigned long Size);

// Add/Find things
PkgIterator GetPkg(const char *Name,const char *End,bool Insert);
inline PkgIterator GetPkg(const char *Name,bool Insert);
NodeIterator GetNode(const char *Name,
const char *NameEnd,
map_ptrloc Loc,
bool Insert,bool Divert);
Node *HashNode(NodeIterator const &N);
void DropNode(map_ptrloc Node);

inline DiverIterator DiverBegin();

// Diversion control
void BeginDiverLoad();
void FinishDiverLoad();
bool AddDiversion(PkgIterator const &Owner,const char *From,
const char *To);
bool AddConfFile(const char *Name,const char *NameEnd,
PkgIterator const &Owner,const unsigned char *Sum);

explicit pkgFLCache(DynamicMMap &Map);
// ~pkgFLCache();
};

struct pkgFLCache::Header
{
// Signature information
unsigned long Signature;
short MajorVersion;
short MinorVersion;
bool Dirty;

// Size of structure values
unsigned HeaderSz;
unsigned NodeSz;
unsigned DirSz;
unsigned PackageSz;
unsigned DiversionSz;
unsigned ConfFileSz;

// Structure Counts;
unsigned int NodeCount;
unsigned int DirCount;
unsigned int PackageCount;
unsigned int DiversionCount;
unsigned int ConfFileCount;
unsigned int HashSize;
unsigned long UniqNodes;

// Offsets
map_ptrloc FileHash;
map_ptrloc DirTree;
map_ptrloc Packages;
map_ptrloc Diversions;

/* Allocation pools, there should be one of these for each structure
excluding the header */
DynamicMMap::Pool Pools[5];

bool CheckSizes(Header &Against) const;
Header();
};

/* The bit field is used to advoid incurring an extra 4 bytes x 40000,
Pointer is the most infrequently used member of the structure */
struct pkgFLCache::Node
{
map_ptrloc Dir; // Dir
map_ptrloc File; // String
unsigned Pointer:24; // Package/Diversion/ConfFile
unsigned Flags:8; // Package
map_ptrloc Next; // Node
map_ptrloc NextPkg; // Node

enum Flags {Diversion = (1<<0),ConfFile = (1<<1),
NewConfFile = (1<<2),NewFile = (1<<3),
Unpacked = (1<<4),Replaced = (1<<5)};
};

struct pkgFLCache::Directory
{
map_ptrloc Left; // Directory
map_ptrloc Right; // Directory
map_ptrloc Name; // String
};

struct pkgFLCache::Package
{
map_ptrloc Left; // Package
map_ptrloc Right; // Package
map_ptrloc Name; // String
map_ptrloc Files; // Node
};

struct pkgFLCache::Diversion
{
map_ptrloc OwnerPkg; // Package
map_ptrloc DivertFrom; // Node
map_ptrloc DivertTo; // String

map_ptrloc Next; // Diversion
unsigned long Flags;

enum Flags {Touched = (1<<0)};
};

struct pkgFLCache::ConfFile
{
map_ptrloc OwnerPkg; // Package
unsigned char MD5[16];
};

class pkgFLCache::PkgIterator
{
Package *Pkg;
pkgFLCache *Owner;

public:

inline bool end() const {return Owner == 0 || Pkg == Owner->PkgP?true:false;}

// Accessors
inline Package *operator ->() {return Pkg;}
inline Package const *operator ->() const {return Pkg;}
inline Package const &operator *() const {return *Pkg;}
inline operator Package *() {return Pkg == Owner->PkgP?0:Pkg;}
inline operator Package const *() const {return Pkg == Owner->PkgP?0:Pkg;}

inline unsigned long Offset() const {return Pkg - Owner->PkgP;}
inline const char *Name() const {return Pkg->Name == 0?0:Owner->StrP + Pkg->Name;}
inline pkgFLCache::NodeIterator Files() const;

PkgIterator() : Pkg(0), Owner(0) {}
PkgIterator(pkgFLCache &Owner,Package *Trg) : Pkg(Trg), Owner(&Owner) {}
};

class pkgFLCache::DirIterator
{
Directory *Dir;
pkgFLCache *Owner;

public:

// Accessors
inline Directory *operator ->() {return Dir;}
inline Directory const *operator ->() const {return Dir;}
inline Directory const &operator *() const {return *Dir;}
inline operator Directory *() {return Dir == Owner->DirP?0:Dir;}
inline operator Directory const *() const {return Dir == Owner->DirP?0:Dir;}

inline const char *Name() const {return Dir->Name == 0?0:Owner->StrP + Dir->Name;}

DirIterator() : Dir(0), Owner(0) {}
DirIterator(pkgFLCache &Owner,Directory *Trg) : Dir(Trg), Owner(&Owner) {}
};

class pkgFLCache::DiverIterator
{
Diversion *Diver;
pkgFLCache *Owner;

public:

// Iteration
void operator ++(int) {if (Diver != Owner->DiverP) Diver = Owner->DiverP + Diver->Next;}
inline void operator ++() {operator ++(0);}
inline bool end() const {return Owner == 0 || Diver == Owner->DiverP;}

// Accessors
inline Diversion *operator ->() {return Diver;}
inline Diversion const *operator ->() const {return Diver;}
inline Diversion const &operator *() const {return *Diver;}
inline operator Diversion *() {return Diver == Owner->DiverP?0:Diver;}
inline operator Diversion const *() const {return Diver == Owner->DiverP?0:Diver;}

inline PkgIterator OwnerPkg() const {return PkgIterator(*Owner,Owner->PkgP + Diver->OwnerPkg);}
inline NodeIterator DivertFrom() const;
inline NodeIterator DivertTo() const;

DiverIterator() : Diver(0), Owner(0) {};
DiverIterator(pkgFLCache &Owner,Diversion *Trg) : Diver(Trg), Owner(&Owner) {}
};

class pkgFLCache::NodeIterator
{
Node *Nde;
enum {NdePkg, NdeHash} Type;
pkgFLCache *Owner;

public:

// Iteration
void operator ++(int) {if (Nde != Owner->NodeP) Nde = Owner->NodeP +
(Type == NdePkg?Nde->NextPkg:Nde->Next);}
inline void operator ++() {operator ++(0);}
inline bool end() const {return Owner == 0 || Nde == Owner->NodeP;}

// Accessors
inline Node *operator ->() {return Nde;}
inline Node const *operator ->() const {return Nde;}
inline Node const &operator *() const {return *Nde;}
inline operator Node *() {return Nde == Owner->NodeP?0:Nde;}
inline operator Node const *() const {return Nde == Owner->NodeP?0:Nde;}
inline unsigned long Offset() const {return Nde - Owner->NodeP;}
inline DirIterator Dir() const {return DirIterator(*Owner,Owner->DirP + Nde->Dir);}
inline DiverIterator Diversion() const {return DiverIterator(*Owner,Owner->DiverP + Nde->Pointer);}
inline const char *File() const {return Nde->File == 0?0:Owner->StrP + Nde->File;}
inline const char *DirN() const {return Owner->StrP + Owner->DirP[Nde->Dir].Name;}
Package *RealPackage() const;

NodeIterator() : Nde(0), Type(NdeHash), Owner(0) {};
explicit NodeIterator(pkgFLCache &Owner) : Nde(Owner.NodeP), Type(NdeHash), Owner(&Owner) {}
NodeIterator(pkgFLCache &Owner,Node *Trg) : Nde(Trg), Type(NdeHash), Owner(&Owner) {}
NodeIterator(pkgFLCache &Owner,Node *Trg,Package *) : Nde(Trg), Type(NdePkg), Owner(&Owner) {}
};

/* Inlines with forward references that cannot be included directly in their
respsective classes */
inline pkgFLCache::NodeIterator pkgFLCache::DiverIterator::DivertFrom() const
{return NodeIterator(*Owner,Owner->NodeP + Diver->DivertFrom);}
inline pkgFLCache::NodeIterator pkgFLCache::DiverIterator::DivertTo() const
{return NodeIterator(*Owner,Owner->NodeP + Diver->DivertTo);}

inline pkgFLCache::NodeIterator pkgFLCache::PkgIterator::Files() const
{return NodeIterator(*Owner,Owner->NodeP + Pkg->Files,Pkg);}

inline pkgFLCache::DiverIterator pkgFLCache::DiverBegin()
{return DiverIterator(*this,DiverP + HeaderP->Diversions);}

inline pkgFLCache::PkgIterator pkgFLCache::GetPkg(const char *Name,bool Insert)
{return GetPkg(Name,Name+strlen(Name),Insert);}

#endif

Loading…
Cancel
Save