summaryrefslogtreecommitdiffstats
path: root/apt-inst/contrib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--apt-inst/contrib/arfile.cc179
-rw-r--r--apt-inst/contrib/arfile.h69
-rw-r--r--apt-inst/contrib/extracttar.cc329
-rw-r--r--apt-inst/contrib/extracttar.h61
4 files changed, 638 insertions, 0 deletions
diff --git a/apt-inst/contrib/arfile.cc b/apt-inst/contrib/arfile.cc
new file mode 100644
index 0000000..6d4a1f1
--- /dev/null
+++ b/apt-inst/contrib/arfile.cc
@@ -0,0 +1,179 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ 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 boundary.
+
+ Information about the structure of AR files can be found in ar(5)
+ on a BSD system, or in the binutils source.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/arfile.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <string>
+#include <string.h>
+#include <sys/types.h>
+
+#include <apti18n.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()
+{
+ off_t 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"));
+ }
+
+ if (Left < 0 || Memb->Size > static_cast<unsigned long long>(Left))
+ {
+ 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 >= sizeof(S))
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+
+ if (Len > Memb->Size)
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+
+ if (File.Read(S,Len) == false)
+ {
+ delete Memb;
+ 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] == ' ' || Head.Name[I] == '/'; I--)
+ {
+ if (I == 0)
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+ }
+ Memb->Name = std::string(Head.Name,I+1);
+ }
+
+ // Account for the AR header alignment
+ off_t 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 < (off_t)(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 0000000..8124208
--- /dev/null
+++ b/apt-inst/contrib/arfile.h
@@ -0,0 +1,69 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ 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
+
+#include <apt-pkg/macros.h>
+#include <string>
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+#endif
+
+class FileFd;
+
+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;
+ inline Member *Members() { return List; }
+
+ ARArchive(FileFd &File);
+ ~ARArchive();
+};
+
+// A member of the archive
+struct ARArchive::Member
+{
+ // Fields from the header
+ std::string Name;
+ unsigned long MTime;
+ unsigned long UID;
+ unsigned long GID;
+ unsigned long Mode;
+ unsigned long long Size;
+
+ // Location of the data.
+ unsigned long 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 0000000..cbee4d1
--- /dev/null
+++ b/apt-inst/contrib/extracttar.cc
@@ -0,0 +1,329 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Extract a Tar - Tar Extractor
+
+ Some performance measurements showed that zlib performed quite poorly
+ in comparison 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 /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/extracttar.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// 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];
+};
+
+// We need to read long names (names and link targets) into memory, so let's
+// have a limit (shamelessly stolen from libarchive) to avoid people OOMing
+// us with large streams.
+static const unsigned long long APT_LONGNAME_LIMIT = 1048576llu;
+
+// A file size limit that we allow extracting. Currently, that's 128 GB.
+// We also should leave some wiggle room for code adding files to it, and
+// possibly conversion for signed, so this should not be larger than like 2**62.
+static const unsigned long long APT_FILESIZE_LIMIT = 1llu << 37;
+
+// ExtractTar::ExtractTar - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ExtractTar::ExtractTar(FileFd &Fd,unsigned long long Max,string DecompressionProgram)
+ : File(Fd), MaxInSize(Max), DecompressProg(DecompressionProgram)
+{
+ GZPid = -1;
+ Eof = false;
+}
+ /*}}}*/
+// ExtractTar::ExtractTar - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ExtractTar::~ExtractTar()
+{
+ // Error close
+ Done();
+}
+ /*}}}*/
+// ExtractTar::Done - Reap the gzip sub process /*{{{*/
+bool ExtractTar::Done(bool)
+{
+ return Done();
+}
+bool ExtractTar::Done()
+{
+ return InFd.Close();
+}
+ /*}}}*/
+// 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()
+{
+ if (DecompressProg.empty())
+ {
+ InFd.OpenDescriptor(File.Fd(), FileFd::ReadOnly, FileFd::None, false);
+ return true;
+ }
+
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ for (; compressor != compressors.end(); ++compressor) {
+ if (compressor->Name == DecompressProg) {
+ return InFd.OpenDescriptor(File.Fd(), FileFd::ReadOnly, *compressor, false);
+ }
+ }
+
+ return _error->Error(_("Cannot find a configured compressor for '%s'"),
+ DecompressProg.c_str());
+
+}
+ /*}}}*/
+// 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, ItemLink;
+ string LastLongName, ItemName;
+ 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();
+
+ if (NewSum != CheckSum)
+ return _error->Error(_("Tar checksum failed, archive corrupted"));
+
+ // Decode all of the fields
+ pkgDirStream::Item Itm;
+ if (StrToNum(Tar->Mode,Itm.Mode,sizeof(Tar->Mode),8) == false ||
+ (Base256ToNum(Tar->UserID,Itm.UID,8) == false &&
+ StrToNum(Tar->UserID,Itm.UID,sizeof(Tar->UserID),8) == false) ||
+ (Base256ToNum(Tar->GroupID,Itm.GID,8) == false &&
+ StrToNum(Tar->GroupID,Itm.GID,sizeof(Tar->GroupID),8) == false) ||
+ (Base256ToNum(Tar->Size,Itm.Size,12) == false &&
+ StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false) ||
+ (Base256ToNum(Tar->MTime,Itm.MTime,12) == 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"));
+
+ // Security check. Prevents overflows below the code when rounding up in skip/copy code,
+ // and provides modest protection against decompression bombs.
+ if (Itm.Size > APT_FILESIZE_LIMIT)
+ return _error->Error("Tar member too large: %llu > %llu bytes", Itm.Size, APT_FILESIZE_LIMIT);
+
+ // Grab the filename and link target: use last long name if one was
+ // set, otherwise use the header value as-is, but remember that it may
+ // fill the entire 100-byte block and needs to be zero-terminated.
+ // See Debian Bug #689582.
+ if (LastLongName.empty() == false)
+ Itm.Name = (char *)LastLongName.c_str();
+ else
+ Itm.Name = (char *)ItemName.assign(Tar->Name, sizeof(Tar->Name)).c_str();
+ if (Itm.Name[0] == '.' && Itm.Name[1] == '/' && Itm.Name[2] != 0)
+ Itm.Name += 2;
+
+ if (LastLongLink.empty() == false)
+ Itm.LinkTarget = (char *)LastLongLink.c_str();
+ else
+ Itm.LinkTarget = (char *)ItemLink.assign(Tar->LinkName, sizeof(Tar->LinkName)).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 long Length = Itm.Size;
+ unsigned char Block[512];
+ if (Length > APT_LONGNAME_LIMIT)
+ return _error->Error("Long name to large: %llu bytes > %llu bytes", Length, APT_LONGNAME_LIMIT);
+ 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 long Length = Itm.Size;
+ unsigned char Block[512];
+ if (Length > APT_LONGNAME_LIMIT)
+ return _error->Error("Long name to large: %llu bytes > %llu bytes", Length, APT_LONGNAME_LIMIT);
+ 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(_("Unknown TAR header type %u"), (unsigned)Tar->LinkFlag);
+ break;
+ }
+
+ int Fd = -1;
+ if (BadRecord == false)
+ if (Stream.DoItem(Itm,Fd) == false)
+ return false;
+
+ // Copy the file over the FD
+ unsigned long long Size = Itm.Size;
+ while (Size != 0)
+ {
+ unsigned char Junk[32*1024];
+ unsigned long Read = min(Size, (unsigned long long)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 (BadRecord == false)
+ if (Stream.FinishedFile(Itm,Fd) == false)
+ return false;
+
+ LastLongName.erase();
+ LastLongLink.erase();
+ }
+
+ return Done();
+}
+ /*}}}*/
diff --git a/apt-inst/contrib/extracttar.h b/apt-inst/contrib/extracttar.h
new file mode 100644
index 0000000..c0b340e
--- /dev/null
+++ b/apt-inst/contrib/extracttar.h
@@ -0,0 +1,61 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ 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
+
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+
+#include <string>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/dirstream.h>
+#include <algorithm>
+using std::min;
+#endif
+
+class pkgDirStream;
+
+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 long MaxInSize;
+ int GZPid;
+ FileFd InFd;
+ bool Eof;
+ std::string DecompressProg;
+
+ // Fork and reap gzip
+ bool StartGzip();
+ bool Done();
+ APT_DEPRECATED_MSG("Parameter Force is ignored, use Done() instead.") bool Done(bool Force);
+
+ public:
+
+ bool Go(pkgDirStream &Stream);
+
+ ExtractTar(FileFd &Fd,unsigned long long Max,std::string DecompressionProgram);
+ virtual ~ExtractTar();
+};
+
+#endif