179 lines
4.6 KiB
C++
179 lines
4.6 KiB
C++
// -*- 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 <cstring>
|
|
#include <string>
|
|
#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;
|
|
}
|
|
/*}}}*/
|