diff options
author | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
---|---|---|
committer | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
commit | b2e465d6d32d2dc884f58b94acb7e35f671a87fe (patch) | |
tree | 5928383b9bde7b0ba9812e6526ad746466e558f7 /apt-inst/contrib | |
parent | 00b47c98ca4a4349686a082eba6d77decbb03a4d (diff) |
Join with aliencode
Author: jgg
Date: 2001-02-20 07:03:16 GMT
Join with aliencode
Diffstat (limited to 'apt-inst/contrib')
-rw-r--r-- | apt-inst/contrib/arfile.cc | 154 | ||||
-rw-r--r-- | apt-inst/contrib/arfile.h | 68 | ||||
-rw-r--r-- | apt-inst/contrib/extracttar.cc | 342 | ||||
-rw-r--r-- | apt-inst/contrib/extracttar.h | 54 |
4 files changed, 618 insertions, 0 deletions
diff --git a/apt-inst/contrib/arfile.cc b/apt-inst/contrib/arfile.cc new file mode 100644 index 000000000..c2964b7a9 --- /dev/null +++ b/apt-inst/contrib/arfile.cc @@ -0,0 +1,154 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: arfile.cc,v 1.2 2001/02/20 07:03:16 jgg Exp $ +/* ###################################################################### + + AR File - Handle an 'AR' archive + + AR Archives have plain text headers at the start of each file + section. The headers are aligned on a 2 byte boundry. + + Information about the structure of AR files can be found in ar(5) + on a BSD system, or in the binutils source. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "apt-pkg/arfile.h" +#endif +#include <apt-pkg/arfile.h> +#include <apt-pkg/strutl.h> +#include <apt-pkg/error.h> + +#include <stdlib.h> + /*}}}*/ + +struct ARArchive::MemberHeader +{ + char Name[16]; + char MTime[12]; + char UID[6]; + char GID[6]; + char Mode[8]; + char Size[10]; + char Magic[2]; +}; + +// ARArchive::ARArchive - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +ARArchive::ARArchive(FileFd &File) : List(0), File(File) +{ + LoadHeaders(); +} + /*}}}*/ +// ARArchive::~ARArchive - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +ARArchive::~ARArchive() +{ + while (List != 0) + { + Member *Tmp = List; + List = List->Next; + delete Tmp; + } +} + /*}}}*/ +// ARArchive::LoadHeaders - Load the headers from each file /*{{{*/ +// --------------------------------------------------------------------- +/* AR files are structured with a 8 byte magic string followed by a 60 + byte plain text header then the file data, another header, data, etc */ +bool ARArchive::LoadHeaders() +{ + signed long Left = File.Size(); + + // Check the magic byte + char Magic[8]; + if (File.Read(Magic,sizeof(Magic)) == false) + return false; + if (memcmp(Magic,"!<arch>\012",sizeof(Magic)) != 0) + return _error->Error("Invalid archive signature"); + Left -= sizeof(Magic); + + // Read the member list + while (Left > 0) + { + MemberHeader Head; + if (File.Read(&Head,sizeof(Head)) == false) + return _error->Error("Error reading archive member header"); + Left -= sizeof(Head); + + // Convert all of the integer members + Member *Memb = new Member(); + if (StrToNum(Head.MTime,Memb->MTime,sizeof(Head.MTime)) == false || + StrToNum(Head.UID,Memb->UID,sizeof(Head.UID)) == false || + StrToNum(Head.GID,Memb->GID,sizeof(Head.GID)) == false || + StrToNum(Head.Mode,Memb->Mode,sizeof(Head.Mode),8) == false || + StrToNum(Head.Size,Memb->Size,sizeof(Head.Size)) == false) + { + delete Memb; + return _error->Error("Invalid archive member header"); + } + + // Check for an extra long name string + if (memcmp(Head.Name,"#1/",3) == 0) + { + char S[300]; + unsigned long Len; + if (StrToNum(Head.Name+3,Len,sizeof(Head.Size)-3) == false || + Len >= strlen(S)) + { + delete Memb; + return _error->Error("Invalid archive member header"); + } + if (File.Read(S,Len) == false) + return false; + S[Len] = 0; + Memb->Name = S; + Memb->Size -= Len; + Left -= Len; + } + else + { + unsigned int I = sizeof(Head.Name) - 1; + for (; Head.Name[I] == ' '; I--); + Memb->Name = string(Head.Name,0,I+1); + } + + // Account for the AR header alignment + unsigned Skip = Memb->Size % 2; + + // Add it to the list + Memb->Next = List; + List = Memb; + Memb->Start = File.Tell(); + if (File.Skip(Memb->Size + Skip) == false) + return false; + if (Left < (signed)(Memb->Size + Skip)) + return _error->Error("Archive is too short"); + Left -= Memb->Size + Skip; + } + if (Left != 0) + return _error->Error("Failed to read the archive headers"); + + return true; +} + /*}}}*/ +// ARArchive::FindMember - Find a name in the member list /*{{{*/ +// --------------------------------------------------------------------- +/* Find a member with the given name */ +const ARArchive::Member *ARArchive::FindMember(const char *Name) const +{ + const Member *Res = List; + while (Res != 0) + { + if (Res->Name == Name) + return Res; + Res = Res->Next; + } + + return 0; +} + /*}}}*/ diff --git a/apt-inst/contrib/arfile.h b/apt-inst/contrib/arfile.h new file mode 100644 index 000000000..6c54d3e6b --- /dev/null +++ b/apt-inst/contrib/arfile.h @@ -0,0 +1,68 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: arfile.h,v 1.2 2001/02/20 07:03:16 jgg Exp $ +/* ###################################################################### + + AR File - Handle an 'AR' archive + + This is a reader for the usual 4.4 BSD AR format. It allows raw + stream access to a single member at a time. Basically all this class + provides is header parsing and verification. It is up to the client + to correctly make use of the stream start/stop points. + + ##################################################################### */ + /*}}}*/ +#ifndef PKGLIB_ARFILE_H +#define PKGLIB_ARFILE_H + +#ifdef __GNUG__ +#pragma interface "apt-pkg/arfile.h" +#endif + +#include <string> +#include <apt-pkg/fileutl.h> + +class ARArchive +{ + struct MemberHeader; + public: + struct Member; + + protected: + + // Linked list of members + Member *List; + + bool LoadHeaders(); + + public: + + // The stream file + FileFd &File; + + // Locate a member by name + const Member *FindMember(const char *Name) const; + + ARArchive(FileFd &File); + ~ARArchive(); +}; + +// A member of the archive +struct ARArchive::Member +{ + // Fields from the header + string Name; + unsigned long MTime; + unsigned long UID; + unsigned long GID; + unsigned long Mode; + unsigned long Size; + + // Location of the data. + unsigned long Start; + Member *Next; + + Member() : Start(0), Next(0) {}; +}; + +#endif diff --git a/apt-inst/contrib/extracttar.cc b/apt-inst/contrib/extracttar.cc new file mode 100644 index 000000000..57e083b5a --- /dev/null +++ b/apt-inst/contrib/extracttar.cc @@ -0,0 +1,342 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: extracttar.cc,v 1.2 2001/02/20 07:03:17 jgg Exp $ +/* ###################################################################### + + Extract a Tar - Tar Extractor + + Some performance measurements showed that zlib performed quite poorly + in comparision to a forked gzip process. This tar extractor makes use + of the fact that dup'd file descriptors have the same seek pointer + and that gzip will not read past the end of a compressed stream, + even if there is more data. We use the dup property to track extraction + progress and the gzip feature to just feed gzip a fd in the middle + of an AR file. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "apt-pkg/extracttar.h" +#endif +#include <apt-pkg/extracttar.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/strutl.h> +#include <apt-pkg/configuration.h> +#include <system.h> + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> + /*}}}*/ + +// The on disk header for a tar file. +struct ExtractTar::TarHeader +{ + char Name[100]; + char Mode[8]; + char UserID[8]; + char GroupID[8]; + char Size[12]; + char MTime[12]; + char Checksum[8]; + char LinkFlag; + char LinkName[100]; + char MagicNumber[8]; + char UserName[32]; + char GroupName[32]; + char Major[8]; + char Minor[8]; +}; + +// ExtractTar::ExtractTar - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +ExtractTar::ExtractTar(FileFd &Fd,unsigned long Max) : File(Fd), + MaxInSize(Max) + +{ + GZPid = -1; + InFd = -1; + Eof = false; +} + /*}}}*/ +// ExtractTar::ExtractTar - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +ExtractTar::~ExtractTar() +{ + Done(false); +} + /*}}}*/ +// ExtractTar::Done - Reap the gzip sub process /*{{{*/ +// --------------------------------------------------------------------- +/* If the force flag is given then error messages are suppressed - this + means we hit the end of the tar file but there was still gzip data. */ +bool ExtractTar::Done(bool Force) +{ + InFd.Close(); + if (GZPid <= 0) + return true; + + /* If there is a pending error then we are cleaning up gzip and are + not interested in it's failures */ + if (_error->PendingError() == true) + Force = true; + + // Make sure we clean it up! + kill(GZPid,SIGINT); + if (ExecWait(GZPid,_config->Find("dir::bin::gzip","/bin/gzip").c_str(), + Force) == false) + { + GZPid = -1; + return Force; + } + + GZPid = -1; + return true; +} + /*}}}*/ +// ExtractTar::StartGzip - Startup gzip /*{{{*/ +// --------------------------------------------------------------------- +/* This creates a gzip sub process that has its input as the file itself. + If this tar file is embedded into something like an ar file then + gzip will efficiently ignore the extra bits. */ +bool ExtractTar::StartGzip() +{ + int Pipes[2]; + if (pipe(Pipes) != 0) + return _error->Errno("pipe","Failed to create pipes"); + + // Fork off the process + GZPid = ExecFork(); + + // Spawn the subprocess + if (GZPid == 0) + { + // Setup the FDs + dup2(Pipes[1],STDOUT_FILENO); + dup2(File.Fd(),STDIN_FILENO); + int Fd = open("/dev/null",O_RDWR); + if (Fd == -1) + _exit(101); + dup2(Fd,STDERR_FILENO); + close(Fd); + SetCloseExec(STDOUT_FILENO,false); + SetCloseExec(STDIN_FILENO,false); + SetCloseExec(STDERR_FILENO,false); + + const char *Args[3]; + Args[0] = _config->Find("dir::bin::gzip","/bin/gzip").c_str(); + Args[1] = "-d"; + Args[2] = 0; + execv(Args[0],(char **)Args); + cerr << "Failed to exec gzip " << Args[0] << endl; + _exit(100); + } + + // Fix up our FDs + InFd.Fd(Pipes[0]); + close(Pipes[1]); + return true; +} + /*}}}*/ +// ExtractTar::Go - Perform extraction /*{{{*/ +// --------------------------------------------------------------------- +/* This reads each 512 byte block from the archive and extracts the header + information into the Item structure. Then it resolves the UID/GID and + invokes the correct processing function. */ +bool ExtractTar::Go(pkgDirStream &Stream) +{ + if (StartGzip() == false) + return false; + + // Loop over all blocks + string LastLongLink; + string LastLongName; + while (1) + { + bool BadRecord = false; + unsigned char Block[512]; + if (InFd.Read(Block,sizeof(Block),true) == false) + return false; + + if (InFd.Eof() == true) + break; + + // Get the checksum + TarHeader *Tar = (TarHeader *)Block; + unsigned long CheckSum; + if (StrToNum(Tar->Checksum,CheckSum,sizeof(Tar->Checksum),8) == false) + return _error->Error("Corrupted archive"); + + /* Compute the checksum field. The actual checksum is blanked out + with spaces so it is not included in the computation */ + unsigned long NewSum = 0; + memset(Tar->Checksum,' ',sizeof(Tar->Checksum)); + for (int I = 0; I != sizeof(Block); I++) + NewSum += Block[I]; + + /* Check for a block of nulls - in this case we kill gzip, GNU tar + does this.. */ + if (NewSum == ' '*sizeof(Tar->Checksum)) + return Done(true); + + if (NewSum != CheckSum) + return _error->Error("Tar Checksum failed, archive corrupted"); + + // Decode all of the fields + pkgDirStream::Item Itm; + unsigned long UID; + unsigned long GID; + if (StrToNum(Tar->Mode,Itm.Mode,sizeof(Tar->Mode),8) == false || + StrToNum(Tar->UserID,UID,sizeof(Tar->UserID),8) == false || + StrToNum(Tar->GroupID,GID,sizeof(Tar->GroupID),8) == false || + StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false || + StrToNum(Tar->MTime,Itm.MTime,sizeof(Tar->MTime),8) == false || + StrToNum(Tar->Major,Itm.Major,sizeof(Tar->Major),8) == false || + StrToNum(Tar->Minor,Itm.Minor,sizeof(Tar->Minor),8) == false) + return _error->Error("Corrupted archive"); + + // Grab the filename + if (LastLongName.empty() == false) + Itm.Name = (char *)LastLongName.c_str(); + else + { + Tar->Name[sizeof(Tar->Name)] = 0; + Itm.Name = Tar->Name; + } + if (Itm.Name[0] == '.' && Itm.Name[1] == '/' && Itm.Name[2] != 0) + Itm.Name += 2; + + // Grab the link target + Tar->Name[sizeof(Tar->LinkName)] = 0; + Itm.LinkTarget = Tar->LinkName; + + if (LastLongLink.empty() == false) + Itm.LinkTarget = (char *)LastLongLink.c_str(); + + // Convert the type over + switch (Tar->LinkFlag) + { + case NormalFile0: + case NormalFile: + Itm.Type = pkgDirStream::Item::File; + break; + + case HardLink: + Itm.Type = pkgDirStream::Item::HardLink; + break; + + case SymbolicLink: + Itm.Type = pkgDirStream::Item::SymbolicLink; + break; + + case CharacterDevice: + Itm.Type = pkgDirStream::Item::CharDevice; + break; + + case BlockDevice: + Itm.Type = pkgDirStream::Item::BlockDevice; + break; + + case Directory: + Itm.Type = pkgDirStream::Item::Directory; + break; + + case FIFO: + Itm.Type = pkgDirStream::Item::FIFO; + break; + + case GNU_LongLink: + { + unsigned long Length = Itm.Size; + unsigned char Block[512]; + while (Length > 0) + { + if (InFd.Read(Block,sizeof(Block),true) == false) + return false; + if (Length <= sizeof(Block)) + { + LastLongLink.append(Block,Block+sizeof(Block)); + break; + } + LastLongLink.append(Block,Block+sizeof(Block)); + Length -= sizeof(Block); + } + continue; + } + + case GNU_LongName: + { + unsigned long Length = Itm.Size; + unsigned char Block[512]; + while (Length > 0) + { + if (InFd.Read(Block,sizeof(Block),true) == false) + return false; + if (Length < sizeof(Block)) + { + LastLongName.append(Block,Block+sizeof(Block)); + break; + } + LastLongName.append(Block,Block+sizeof(Block)); + Length -= sizeof(Block); + } + continue; + } + + default: + BadRecord = true; + _error->Warning("Unkown TAR header type %u, member %s",(unsigned)Tar->LinkFlag,Tar->Name); + break; + } + + int Fd = -1; + if (BadRecord == false) + if (Stream.DoItem(Itm,Fd) == false) + return false; + + // Copy the file over the FD + unsigned long Size = Itm.Size; + while (Size != 0) + { + unsigned char Junk[32*1024]; + unsigned long Read = MIN(Size,sizeof(Junk)); + if (InFd.Read(Junk,((Read+511)/512)*512) == false) + return false; + + if (BadRecord == false) + { + if (Fd > 0) + { + if (write(Fd,Junk,Read) != (signed)Read) + return Stream.Fail(Itm,Fd); + } + else + { + /* An Fd of -2 means to send to a special processing + function */ + if (Fd == -2) + if (Stream.Process(Itm,Junk,Read,Itm.Size - Size) == false) + return Stream.Fail(Itm,Fd); + } + } + + Size -= Read; + } + + // And finish up + if (Itm.Size != 0 && BadRecord == false) + if (Stream.FinishedFile(Itm,Fd) == false) + return false; + + LastLongName.erase(); + LastLongLink.erase(); + } + + return Done(false); +} + /*}}}*/ diff --git a/apt-inst/contrib/extracttar.h b/apt-inst/contrib/extracttar.h new file mode 100644 index 000000000..aaca987f2 --- /dev/null +++ b/apt-inst/contrib/extracttar.h @@ -0,0 +1,54 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: extracttar.h,v 1.2 2001/02/20 07:03:17 jgg Exp $ +/* ###################################################################### + + Extract a Tar - Tar Extractor + + The tar extractor takes an ordinary gzip compressed tar stream from + the given file and explodes it, passing the individual items to the + given Directory Stream for processing. + + ##################################################################### */ + /*}}}*/ +#ifndef PKGLIB_EXTRACTTAR_H +#define PKGLIB_EXTRACTTAR_H + +#ifdef __GNUG__ +#pragma interface "apt-pkg/extracttar.h" +#endif + +#include <apt-pkg/fileutl.h> +#include <apt-pkg/dirstream.h> + +class ExtractTar +{ + protected: + + struct TarHeader; + + // The varios types items can be + enum ItemType {NormalFile0 = '\0',NormalFile = '0',HardLink = '1', + SymbolicLink = '2',CharacterDevice = '3', + BlockDevice = '4',Directory = '5',FIFO = '6', + GNU_LongLink = 'K',GNU_LongName = 'L'}; + + FileFd &File; + unsigned long MaxInSize; + int GZPid; + FileFd InFd; + bool Eof; + + // Fork and reap gzip + bool StartGzip(); + bool Done(bool Force); + + public: + + bool Go(pkgDirStream &Stream); + + ExtractTar(FileFd &Fd,unsigned long Max); + virtual ~ExtractTar(); +}; + +#endif |