summaryrefslogtreecommitdiff
path: root/apt-inst/deb
diff options
context:
space:
mode:
authorArch Librarian <arch@canonical.com>2004-09-20 16:56:32 +0000
committerArch Librarian <arch@canonical.com>2004-09-20 16:56:32 +0000
commitb2e465d6d32d2dc884f58b94acb7e35f671a87fe (patch)
tree5928383b9bde7b0ba9812e6526ad746466e558f7 /apt-inst/deb
parent00b47c98ca4a4349686a082eba6d77decbb03a4d (diff)
Join with aliencode
Author: jgg Date: 2001-02-20 07:03:16 GMT Join with aliencode
Diffstat (limited to 'apt-inst/deb')
-rw-r--r--apt-inst/deb/debfile.cc262
-rw-r--r--apt-inst/deb/debfile.h92
-rw-r--r--apt-inst/deb/dpkgdb.cc490
-rw-r--r--apt-inst/deb/dpkgdb.h53
4 files changed, 897 insertions, 0 deletions
diff --git a/apt-inst/deb/debfile.cc b/apt-inst/deb/debfile.cc
new file mode 100644
index 000000000..c93ba88a8
--- /dev/null
+++ b/apt-inst/deb/debfile.cc
@@ -0,0 +1,262 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: debfile.cc,v 1.2 2001/02/20 07:03:17 jgg Exp $
+/* ######################################################################
+
+ Debian Archive File (.deb)
+
+ .DEB archives are AR files containing two tars and an empty marker
+ member called 'debian-binary'. The two tars contain the meta data and
+ the actual archive contents. Thus this class is a very simple wrapper
+ around ar/tar to simply extract the right tar files.
+
+ It also uses the deb package list parser to parse the control file
+ into the cache.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#ifdef __GNUG__
+#pragma implementation "apt-pkg/debfile.h"
+#endif
+
+#include <apt-pkg/debfile.h>
+#include <apt-pkg/extracttar.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/deblistparser.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+ /*}}}*/
+
+// DebFile::debDebFile - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Open the AR file and check for consistency */
+debDebFile::debDebFile(FileFd &File) : File(File), AR(File)
+{
+ if (_error->PendingError() == true)
+ return;
+
+ // Check the members for validity
+ if (CheckMember("debian-binary") == false ||
+ CheckMember("control.tar.gz") == false ||
+ CheckMember("data.tar.gz") == false)
+ return;
+}
+ /*}}}*/
+// DebFile::CheckMember - Check if a named member is in the archive /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to check for a correct deb and to give nicer error messages
+ for people playing around. */
+bool debDebFile::CheckMember(const char *Name)
+{
+ if (AR.FindMember(Name) == 0)
+ return _error->Error("This is not a valid DEB archive, missing '%s' member",Name);
+ return true;
+}
+ /*}}}*/
+// DebFile::GotoMember - Jump to a Member /*{{{*/
+// ---------------------------------------------------------------------
+/* Jump in the file to the start of a named member and return the information
+ about that member. The caller can then read from the file up to the
+ returned size. Note, since this relies on the file position this is
+ a destructive operation, it also changes the last returned Member
+ structure - so don't nest them! */
+const ARArchive::Member *debDebFile::GotoMember(const char *Name)
+{
+ // Get the archive member and positition the file
+ const ARArchive::Member *Member = AR.FindMember(Name);
+ if (Member == 0)
+ {
+ _error->Error("Internal Error, could not locate member %s",Name);
+ return 0;
+ }
+ if (File.Seek(Member->Start) == false)
+ return 0;
+
+ return Member;
+}
+ /*}}}*/
+// DebFile::ExtractControl - Extract Control information /*{{{*/
+// ---------------------------------------------------------------------
+/* Extract the control information into the Database's temporary
+ directory. */
+bool debDebFile::ExtractControl(pkgDataBase &DB)
+{
+ // Get the archive member and positition the file
+ const ARArchive::Member *Member = GotoMember("control.tar.gz");
+ if (Member == 0)
+ return false;
+
+ // Prepare Tar
+ ControlExtract Extract;
+ ExtractTar Tar(File,Member->Size);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Get into the temporary directory
+ string Cwd = SafeGetCWD();
+ string Tmp;
+ if (DB.GetMetaTmp(Tmp) == false)
+ return false;
+ if (chdir(Tmp.c_str()) != 0)
+ return _error->Errno("chdir","Couldn't change to %s",Tmp.c_str());
+
+ // Do extraction
+ if (Tar.Go(Extract) == false)
+ return false;
+
+ // Switch out of the tmp directory.
+ if (chdir(Cwd.c_str()) != 0)
+ chdir("/");
+
+ return true;
+}
+ /*}}}*/
+// DebFile::ExtractArchive - Extract the archive data itself /*{{{*/
+// ---------------------------------------------------------------------
+/* Simple wrapper around tar.. */
+bool debDebFile::ExtractArchive(pkgDirStream &Stream)
+{
+ // Get the archive member and positition the file
+ const ARArchive::Member *Member = AR.FindMember("data.tar.gz");
+ if (Member == 0)
+ return _error->Error("Internal Error, could not locate member");
+ if (File.Seek(Member->Start) == false)
+ return false;
+
+ // Prepare Tar
+ ExtractTar Tar(File,Member->Size);
+ if (_error->PendingError() == true)
+ return false;
+ return Tar.Go(Stream);
+}
+ /*}}}*/
+// DebFile::MergeControl - Merge the control information /*{{{*/
+// ---------------------------------------------------------------------
+/* This reads the extracted control file into the cache and returns the
+ version that was parsed. All this really does is select the correct
+ parser and correct file to parse. */
+pkgCache::VerIterator debDebFile::MergeControl(pkgDataBase &DB)
+{
+ // Open the control file
+ string Tmp;
+ if (DB.GetMetaTmp(Tmp) == false)
+ return pkgCache::VerIterator(DB.GetCache());
+ FileFd Fd(Tmp + "control",FileFd::ReadOnly);
+ if (_error->PendingError() == true)
+ return pkgCache::VerIterator(DB.GetCache());
+
+ // Parse it
+ debListParser Parse(&Fd);
+ pkgCache::VerIterator Ver(DB.GetCache());
+ if (DB.GetGenerator().MergeList(Parse,&Ver) == false)
+ return pkgCache::VerIterator(DB.GetCache());
+
+ if (Ver.end() == true)
+ _error->Error("Failed to locate a valid control file");
+ return Ver;
+}
+ /*}}}*/
+
+// DebFile::ControlExtract::DoItem - Control Tar Extraction /*{{{*/
+// ---------------------------------------------------------------------
+/* This directory stream handler for the control tar handles extracting
+ it into the temporary meta directory. It only extracts files, it does
+ not create directories, links or anything else. */
+bool debDebFile::ControlExtract::DoItem(Item &Itm,int &Fd)
+{
+ if (Itm.Type != Item::File)
+ return true;
+
+ /* Cleanse the file name, prevent people from trying to unpack into
+ absolute paths, .., etc */
+ for (char *I = Itm.Name; *I != 0; I++)
+ if (*I == '/')
+ *I = '_';
+
+ /* Force the ownership to be root and ensure correct permissions,
+ go-w, the rest are left untouched */
+ Itm.UID = 0;
+ Itm.GID = 0;
+ Itm.Mode &= ~(S_IWGRP | S_IWOTH);
+
+ return pkgDirStream::DoItem(Itm,Fd);
+}
+ /*}}}*/
+
+// MemControlExtract::DoItem - Check if it is the control file /*{{{*/
+// ---------------------------------------------------------------------
+/* This sets up to extract the control block member file into a memory
+ block of just the right size. All other files go into the bit bucket. */
+bool debDebFile::MemControlExtract::DoItem(Item &Itm,int &Fd)
+{
+ // At the control file, allocate buffer memory.
+ if (Member == Itm.Name)
+ {
+ delete [] Control;
+ Control = new char[Itm.Size+2];
+ IsControl = true;
+ Fd = -2; // Signal to pass to Process
+ Length = Itm.Size;
+ }
+ else
+ IsControl = false;
+
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::Process - Process extracting the control file /*{{{*/
+// ---------------------------------------------------------------------
+/* Just memcopy the block from the tar extractor and put it in the right
+ place in the pre-allocated memory block. */
+bool debDebFile::MemControlExtract::Process(Item &Itm,const unsigned char *Data,
+ unsigned long Size,unsigned long Pos)
+{
+ memcpy(Control + Pos, Data,Size);
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::Read - Read the control information from the deb /*{{{*/
+// ---------------------------------------------------------------------
+/* This uses the internal tar extractor to fetch the control file, and then
+ it parses it into a tag section parser. */
+bool debDebFile::MemControlExtract::Read(debDebFile &Deb)
+{
+ // Get the archive member and positition the file
+ const ARArchive::Member *Member = Deb.GotoMember("control.tar.gz");
+ if (Member == 0)
+ return false;
+
+ // Extract it.
+ ExtractTar Tar(Deb.GetFile(),Member->Size);
+ if (Tar.Go(*this) == false)
+ return false;
+
+ if (Control == 0)
+ return true;
+
+ Control[Length] = '\n';
+ Control[Length+1] = '\n';
+ if (Section.Scan(Control,Length+2) == false)
+ return _error->Error("Unparsible control file");
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::TakeControl - Parse a memory block /*{{{*/
+// ---------------------------------------------------------------------
+/* The given memory block is loaded into the parser and parsed as a control
+ record. */
+bool debDebFile::MemControlExtract::TakeControl(const void *Data,unsigned long Size)
+{
+ delete [] Control;
+ Control = new char[Size+2];
+ Length = Size;
+ memcpy(Control,Data,Size);
+
+ Control[Length] = '\n';
+ Control[Length+1] = '\n';
+ return Section.Scan(Control,Length+2);
+}
+ /*}}}*/
+
diff --git a/apt-inst/deb/debfile.h b/apt-inst/deb/debfile.h
new file mode 100644
index 000000000..d89b85268
--- /dev/null
+++ b/apt-inst/deb/debfile.h
@@ -0,0 +1,92 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: debfile.h,v 1.2 2001/02/20 07:03:17 jgg Exp $
+/* ######################################################################
+
+ Debian Archive File (.deb)
+
+ This Class handles all the operations performed directly on .deb
+ files. It makes use of the AR and TAR classes to give the necessary
+ external interface.
+
+ There are only two things that can be done with a raw package,
+ extract it's control information and extract the contents itself.
+
+ This should probably subclass an as-yet unwritten super class to
+ produce a generic archive mechanism.
+
+ The memory control file extractor is useful to extract a single file
+ into memory from the control.tar.gz
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_DEBFILE_H
+#define PKGLIB_DEBFILE_H
+
+#ifdef __GNUG__
+#pragma interface "apt-pkg/debfile.h"
+#endif
+
+#include <apt-pkg/arfile.h>
+#include <apt-pkg/database.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/tagfile.h>
+
+class debDebFile
+{
+ protected:
+
+ FileFd &File;
+ ARArchive AR;
+
+ bool CheckMember(const char *Name);
+
+ public:
+
+ class ControlExtract;
+ class MemControlExtract;
+
+ bool ExtractControl(pkgDataBase &DB);
+ bool ExtractArchive(pkgDirStream &Stream);
+ pkgCache::VerIterator MergeControl(pkgDataBase &DB);
+ const ARArchive::Member *GotoMember(const char *Name);
+ inline FileFd &GetFile() {return File;};
+
+ debDebFile(FileFd &File);
+};
+
+class debDebFile::ControlExtract : public pkgDirStream
+{
+ public:
+
+ virtual bool DoItem(Item &Itm,int &Fd);
+};
+
+class debDebFile::MemControlExtract : public pkgDirStream
+{
+ bool IsControl;
+
+ public:
+
+ char *Control;
+ pkgTagSection Section;
+ unsigned long Length;
+ string Member;
+
+ // Members from DirStream
+ virtual bool DoItem(Item &Itm,int &Fd);
+ virtual bool Process(Item &Itm,const unsigned char *Data,
+ unsigned long Size,unsigned long Pos);
+
+
+ // Helpers
+ bool Read(debDebFile &Deb);
+ bool TakeControl(const void *Data,unsigned long Size);
+
+ MemControlExtract() : IsControl(false), Control(0), Length(0), Member("control") {};
+ MemControlExtract(string Member) : IsControl(false), Control(0), Length(0), Member(Member) {};
+ ~MemControlExtract() {delete [] Control;};
+};
+ /*}}}*/
+
+#endif
diff --git a/apt-inst/deb/dpkgdb.cc b/apt-inst/deb/dpkgdb.cc
new file mode 100644
index 000000000..85fec1ccd
--- /dev/null
+++ b/apt-inst/deb/dpkgdb.cc
@@ -0,0 +1,490 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: dpkgdb.cc,v 1.2 2001/02/20 07:03:17 jgg Exp $
+/* ######################################################################
+
+ DPKGv1 Database Implemenation
+
+ This class provides parsers and other implementations for the DPKGv1
+ database. It reads the diversion file, the list files and the status
+ file to build both the list of currently installed files and the
+ currently installed package list.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#ifdef __GNUG__
+#pragma implementation "apt-pkg/dpkgdb.h"
+#endif
+
+#include <apt-pkg/dpkgdb.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/tagfile.h>
+#include <apt-pkg/strutl.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+ /*}}}*/
+
+// EraseDir - Erase A Directory /*{{{*/
+// ---------------------------------------------------------------------
+/* This is necessary to create a new empty sub directory. The caller should
+ invoke mkdir after this with the proper permissions and check for
+ error. Maybe stick this in fileutils */
+static bool EraseDir(const char *Dir)
+{
+ // First we try a simple RM
+ if (rmdir(Dir) == 0 ||
+ errno == ENOENT)
+ return true;
+
+ // A file? Easy enough..
+ if (errno == ENOTDIR)
+ {
+ if (unlink(Dir) != 0)
+ return _error->Errno("unlink","Failed to remove %s",Dir);
+ return true;
+ }
+
+ // Should not happen
+ if (errno != ENOTEMPTY)
+ return _error->Errno("rmdir","Failed to remove %s",Dir);
+
+ // Purge it using rm
+ int Pid = ExecFork();
+
+ // Spawn the subprocess
+ if (Pid == 0)
+ {
+ execlp(_config->Find("Dir::Bin::rm","/bin/rm").c_str(),
+ "rm","-rf","--",Dir,0);
+ _exit(100);
+ }
+ return ExecWait(Pid,_config->Find("dir::bin::rm","/bin/rm").c_str());
+}
+ /*}}}*/
+// DpkgDB::debDpkgDB - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+debDpkgDB::debDpkgDB() : CacheMap(0), FileMap(0)
+{
+ AdminDir = flNotFile(_config->Find("Dir::State::status"));
+ DiverInode = 0;
+ DiverTime = 0;
+}
+ /*}}}*/
+// DpkgDB::~debDpkgDB - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+debDpkgDB::~debDpkgDB()
+{
+ delete Cache;
+ Cache = 0;
+ delete CacheMap;
+ CacheMap = 0;
+
+ delete FList;
+ FList = 0;
+ delete FileMap;
+ FileMap = 0;
+}
+ /*}}}*/
+// DpkgDB::InitMetaTmp - Get the temp dir for meta information /*{{{*/
+// ---------------------------------------------------------------------
+/* This creats+empties the meta temporary directory /var/lib/dpkg/tmp.ci
+ Only one package at a time can be using the returned meta directory. */
+bool debDpkgDB::InitMetaTmp(string &Dir)
+{
+ string Tmp = AdminDir + "tmp.ci/";
+ if (EraseDir(Tmp.c_str()) == false)
+ return _error->Error("Unable to create %s",Tmp.c_str());
+ if (mkdir(Tmp.c_str(),0755) != 0)
+ return _error->Errno("mkdir","Unable to create %s",Tmp.c_str());
+
+ // Verify it is on the same filesystem as the main info directory
+ dev_t Dev;
+ struct stat St;
+ if (stat((AdminDir + "info").c_str(),&St) != 0)
+ return _error->Errno("stat","Failed to stat %sinfo",AdminDir.c_str());
+ Dev = St.st_dev;
+ if (stat(Tmp.c_str(),&St) != 0)
+ return _error->Errno("stat","Failed to stat %s",Tmp.c_str());
+ if (Dev != St.st_dev)
+ return _error->Error("The info and temp directories need to be on the same filesystem");
+
+ // Done
+ Dir = Tmp;
+ return true;
+}
+ /*}}}*/
+// DpkgDB::ReadyPkgCache - Prepare the cache with the current status /*{{{*/
+// ---------------------------------------------------------------------
+/* This reads in the status file into an empty cache. This really needs
+ to be somehow unified with the high level APT notion of the Database
+ directory, but there is no clear way on how to do that yet. */
+bool debDpkgDB::ReadyPkgCache(OpProgress &Progress)
+{
+ if (Cache != 0)
+ {
+ Progress.OverallProgress(1,1,1,"Reading Package Lists");
+ return true;
+ }
+
+ if (CacheMap != 0)
+ {
+ delete CacheMap;
+ CacheMap = 0;
+ }
+
+ if (pkgMakeOnlyStatusCache(Progress,&CacheMap) == false)
+ return false;
+ Cache->DropProgress();
+
+ return true;
+}
+ /*}}}*/
+// DpkgDB::ReadFList - Read the File Listings in /*{{{*/
+// ---------------------------------------------------------------------
+/* This reads the file listing in from the state directory. This is a
+ performance critical routine, as it needs to parse about 50k lines of
+ text spread over a hundred or more files. For an initial cold start
+ most of the time is spent in reading file inodes and so on, not
+ actually parsing. */
+bool debDpkgDB::ReadFList(OpProgress &Progress)
+{
+ // Count the number of packages we need to read information for
+ unsigned long Total = 0;
+ pkgCache &Cache = this->Cache->GetCache();
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
+ {
+ // Only not installed packages have no files.
+ if (I->CurrentState == pkgCache::State::NotInstalled)
+ continue;
+ Total++;
+ }
+
+ /* Switch into the admin dir, this prevents useless lookups for the
+ path components */
+ string Cwd = SafeGetCWD();
+ if (chdir((AdminDir + "info/").c_str()) != 0)
+ return _error->Errno("chdir","Failed to change to the admin dir %sinfo",AdminDir.c_str());
+
+ // Allocate a buffer. Anything larger than this buffer will be mmaped
+ unsigned long BufSize = 32*1024;
+ char *Buffer = new char[BufSize];
+
+ // Begin Loading them
+ unsigned long Count = 0;
+ char Name[300];
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
+ {
+ /* Only not installed packages have no files. ConfFile packages have
+ file lists but we don't want to read them in */
+ if (I->CurrentState == pkgCache::State::NotInstalled ||
+ I->CurrentState == pkgCache::State::ConfigFiles)
+ continue;
+
+ // Fetch a package handle to associate with the file
+ pkgFLCache::PkgIterator FlPkg = FList->GetPkg(I.Name(),0,true);
+ if (FlPkg.end() == true)
+ {
+ _error->Error("Internal Error getting a Package Name");
+ break;
+ }
+
+ Progress.OverallProgress(Count,Total,1,"Reading File Listing");
+
+ // Open the list file
+ snprintf(Name,sizeof(Name),"%s.list",I.Name());
+ int Fd = open(Name,O_RDONLY);
+
+ /* Okay this is very strange and bad.. Best thing is to bail and
+ instruct the user to look into it. */
+ struct stat Stat;
+ if (Fd == -1 || fstat(Fd,&Stat) != 0)
+ {
+ _error->Errno("open","Failed to open the list file '%sinfo/%s'. If you "
+ "cannot restore this file then make it empty "
+ "and immediately re-install the same version of the package!",
+ AdminDir.c_str(),Name);
+ break;
+ }
+
+ // Set File to be a memory buffer containing the whole file
+ char *File;
+ if ((unsigned)Stat.st_size < BufSize)
+ {
+ if (read(Fd,Buffer,Stat.st_size) != Stat.st_size)
+ {
+ _error->Errno("read","Failed reading the list file %sinfo/%s",
+ AdminDir.c_str(),Name);
+ close(Fd);
+ break;
+ }
+ File = Buffer;
+ }
+ else
+ {
+ // Use mmap
+ File = (char *)mmap(0,Stat.st_size,PROT_READ,MAP_PRIVATE,Fd,0);
+ if (File == (char *)(-1))
+ {
+ _error->Errno("mmap","Failed reading the list file %sinfo/%s",
+ AdminDir.c_str(),Name);
+ close(Fd);
+ break;
+ }
+ }
+
+ // Parse it
+ const char *Start = File;
+ const char *End = File;
+ const char *Finish = File + Stat.st_size;
+ for (; End < Finish; End++)
+ {
+ // Not an end of line
+ if (*End != '\n' && End + 1 < Finish)
+ continue;
+
+ // Skip blank lines
+ if (End - Start > 1)
+ {
+ pkgFLCache::NodeIterator Node = FList->GetNode(Start,End,
+ FlPkg.Offset(),true,false);
+ if (Node.end() == true)
+ {
+ _error->Error("Internal Error getting a Node");
+ break;
+ }
+ }
+
+ // Skip past the end of line
+ for (; *End == '\n' && End < Finish; End++);
+ Start = End;
+ }
+
+ close(Fd);
+ if ((unsigned)Stat.st_size >= BufSize)
+ munmap((caddr_t)File,Stat.st_size);
+
+ // Failed
+ if (End < Finish)
+ break;
+
+ Count++;
+ }
+
+ delete [] Buffer;
+ if (chdir(Cwd.c_str()) != 0)
+ chdir("/");
+
+ return !_error->PendingError();
+}
+ /*}}}*/
+// DpkgDB::ReadDiversions - Load the diversions file /*{{{*/
+// ---------------------------------------------------------------------
+/* Read the diversion file in from disk. This is usually invoked by
+ LoadChanges before performing an operation that uses the FLCache. */
+bool debDpkgDB::ReadDiversions()
+{
+ struct stat Stat;
+ if (stat((AdminDir + "diversions").c_str(),&Stat) != 0)
+ return true;
+
+ if (_error->PendingError() == true)
+ return false;
+
+ FILE *Fd = fopen((AdminDir + "diversions").c_str(),"r");
+ if (Fd == 0)
+ return _error->Errno("fopen","Failed to open the diversions file %sdiversions",AdminDir.c_str());
+
+ FList->BeginDiverLoad();
+ while (1)
+ {
+ char From[300];
+ char To[300];
+ char Package[100];
+
+ // Read the three lines in
+ if (fgets(From,sizeof(From),Fd) == 0)
+ break;
+ if (fgets(To,sizeof(To),Fd) == 0 ||
+ fgets(Package,sizeof(Package),Fd) == 0)
+ {
+ _error->Error("The diversion file is corrupted");
+ break;
+ }
+
+ // Strip the \ns
+ unsigned long Len = strlen(From);
+ if (Len < 2 || From[Len-1] != '\n')
+ _error->Error("Invalid line in the diversion file: %s",From);
+ else
+ From[Len-1] = 0;
+ Len = strlen(To);
+ if (Len < 2 || To[Len-1] != '\n')
+ _error->Error("Invalid line in the diversion file: %s",To);
+ else
+ To[Len-1] = 0;
+ Len = strlen(Package);
+ if (Len < 2 || Package[Len-1] != '\n')
+ _error->Error("Invalid line in the diversion file: %s",Package);
+ else
+ Package[Len-1] = 0;
+
+ // Make sure the lines were parsed OK
+ if (_error->PendingError() == true)
+ break;
+
+ // Fetch a package
+ if (strcmp(Package,":") == 0)
+ Package[0] = 0;
+ pkgFLCache::PkgIterator FlPkg = FList->GetPkg(Package,0,true);
+ if (FlPkg.end() == true)
+ {
+ _error->Error("Internal Error getting a Package Name");
+ break;
+ }
+
+ // Install the diversion
+ if (FList->AddDiversion(FlPkg,From,To) == false)
+ {
+ _error->Error("Internal Error adding a diversion");
+ break;
+ }
+ }
+ if (_error->PendingError() == false)
+ FList->FinishDiverLoad();
+
+ DiverInode = Stat.st_ino;
+ DiverTime = Stat.st_mtime;
+
+ fclose(Fd);
+ return !_error->PendingError();
+}
+ /*}}}*/
+// DpkgDB::ReadFileList - Read the file listing /*{{{*/
+// ---------------------------------------------------------------------
+/* Read in the file listing. The file listing is created from three
+ sources, *.list, Conffile sections and the Diversion table. */
+bool debDpkgDB::ReadyFileList(OpProgress &Progress)
+{
+ if (Cache == 0)
+ return _error->Error("The pkg cache must be initialize first");
+ if (FList != 0)
+ {
+ Progress.OverallProgress(1,1,1,"Reading File List");
+ return true;
+ }
+
+ // Create the cache and read in the file listing
+ FileMap = new DynamicMMap(MMap::Public);
+ FList = new pkgFLCache(*FileMap);
+ if (_error->PendingError() == true ||
+ ReadFList(Progress) == false ||
+ ReadConfFiles() == false ||
+ ReadDiversions() == false)
+ {
+ delete FList;
+ delete FileMap;
+ FileMap = 0;
+ FList = 0;
+ return false;
+ }
+
+ cout << "Node: " << FList->HeaderP->NodeCount << ',' << FList->HeaderP->UniqNodes << endl;
+ cout << "Dir: " << FList->HeaderP->DirCount << endl;
+ cout << "Package: " << FList->HeaderP->PackageCount << endl;
+ cout << "HashSize: " << FList->HeaderP->HashSize << endl;
+ cout << "Size: " << FileMap->Size() << endl;
+ cout << endl;
+
+ return true;
+}
+ /*}}}*/
+// DpkgDB::ReadConfFiles - Read the conf file sections from the s-file /*{{{*/
+// ---------------------------------------------------------------------
+/* Reading the conf files is done by reparsing the status file. This is
+ actually rather fast so it is no big deal. */
+bool debDpkgDB::ReadConfFiles()
+{
+ FileFd File(_config->FindFile("Dir::State::status"),FileFd::ReadOnly);
+ pkgTagFile Tags(&File);
+ if (_error->PendingError() == true)
+ return false;
+
+ pkgTagSection Section;
+ while (1)
+ {
+ // Skip to the next section
+ unsigned long Offset = Tags.Offset();
+ if (Tags.Step(Section) == false)
+ break;
+
+ // Parse the line
+ const char *Start;
+ const char *Stop;
+ if (Section.Find("Conffiles",Start,Stop) == false)
+ continue;
+
+ const char *PkgStart;
+ const char *PkgEnd;
+ if (Section.Find("Package",PkgStart,PkgEnd) == false)
+ return _error->Error("Failed to find a Package: Header, offset %lu",Offset);
+
+ // Snag a package record for it
+ pkgFLCache::PkgIterator FlPkg = FList->GetPkg(PkgStart,PkgEnd,true);
+ if (FlPkg.end() == true)
+ return _error->Error("Internal Error getting a Package Name");
+
+ // Parse the conf file lines
+ while (1)
+ {
+ for (; isspace(*Start) != 0 && Start < Stop; Start++);
+ if (Start == Stop)
+ break;
+
+ // Split it into words
+ const char *End = Start;
+ for (; isspace(*End) == 0 && End < Stop; End++);
+ const char *StartMd5 = End;
+ for (; isspace(*StartMd5) != 0 && StartMd5 < Stop; StartMd5++);
+ const char *EndMd5 = StartMd5;
+ for (; isspace(*EndMd5) == 0 && EndMd5 < Stop; EndMd5++);
+ if (StartMd5 == EndMd5 || Start == End)
+ return _error->Error("Bad ConfFile section in the status file. Offset %lu",Offset);
+
+ // Insert a new entry
+ unsigned char MD5[16];
+ if (Hex2Num(StartMd5,EndMd5,MD5,16) == false)
+ return _error->Error("Error parsing MD5. Offset %lu",Offset);
+
+ if (FList->AddConfFile(Start,End,FlPkg,MD5) == false)
+ return false;
+ Start = EndMd5;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+// DpkgDB::LoadChanges - Read in any changed state files /*{{{*/
+// ---------------------------------------------------------------------
+/* The only file in the dpkg system that can change while packages are
+ unpacking is the diversions file. */
+bool debDpkgDB::LoadChanges()
+{
+ struct stat Stat;
+ if (stat((AdminDir + "diversions").c_str(),&Stat) != 0)
+ return true;
+ if (DiverInode == Stat.st_ino && DiverTime == Stat.st_mtime)
+ return true;
+ return ReadDiversions();
+}
+ /*}}}*/
diff --git a/apt-inst/deb/dpkgdb.h b/apt-inst/deb/dpkgdb.h
new file mode 100644
index 000000000..ddbb6d6f2
--- /dev/null
+++ b/apt-inst/deb/dpkgdb.h
@@ -0,0 +1,53 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: dpkgdb.h,v 1.2 2001/02/20 07:03:17 jgg Exp $
+/* ######################################################################
+
+ DPKGv1 Data Base Implemenation
+
+ The DPKGv1 database is typically stored in /var/lib/dpkg/. For
+ DPKGv1 the 'meta' information is the contents of the .deb control.tar.gz
+ member prepended by the package name. The meta information is unpacked
+ in its temporary directory and then migrated into the main list dir
+ at a checkpoint.
+
+ Journaling is providing by syncronized file writes to the updates sub
+ directory.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_DPKGDB_H
+#define PKGLIB_DPKGDB_H
+
+#ifdef __GNUG__
+#pragma interface "apt-pkg/dpkgdb.h"
+#endif
+
+#include <apt-pkg/database.h>
+
+class debDpkgDB : public pkgDataBase
+{
+ protected:
+
+ string AdminDir;
+ DynamicMMap *CacheMap;
+ DynamicMMap *FileMap;
+ unsigned long DiverInode;
+ signed long DiverTime;
+
+ virtual bool InitMetaTmp(string &Dir);
+ bool ReadFList(OpProgress &Progress);
+ bool ReadDiversions();
+ bool ReadConfFiles();
+
+ public:
+
+ virtual bool ReadyFileList(OpProgress &Progress);
+ virtual bool ReadyPkgCache(OpProgress &Progress);
+ virtual bool LoadChanges();
+
+ debDpkgDB();
+ virtual ~debDpkgDB();
+};
+
+#endif