#include "TZIPFile.h"
#include "TFile.h"
#include "TObjArray.h"
ClassImp(TZIPFile)
TZIPFile::TZIPFile() : TArchiveFile()
{
fDirPos = 0;
fDirSize = 0;
fDirOffset = 0;
}
TZIPFile::TZIPFile(const char *archive, const char *member, TFile *file)
: TArchiveFile(archive, member, file)
{
fDirPos = 0;
fDirSize = 0;
fDirOffset = 0;
}
Int_t TZIPFile::OpenArchive()
{
if (ReadEndHeader(FindEndHeader()) == -1)
return -1;
return ReadDirectory();
}
Long64_t TZIPFile::FindEndHeader()
{
const Int_t kBUFSIZE = 1024;
Long64_t size = fFile->GetSize();
Long64_t limit = TMath::Min(size, Long64_t(kMAX_VAR_LEN));
char buf[kBUFSIZE+4];
for (Long64_t offset = 4; offset < limit; ) {
offset = TMath::Min(offset + kBUFSIZE, limit);
Long64_t pos = size - offset;
Int_t n = TMath::Min(kBUFSIZE+4, Int_t(offset));
fFile->Seek(pos);
if (fFile->ReadBuffer(buf, n)) {
Error("FindEndHeader", "error reading %d bytes at %lld", n, pos);
return 0;
}
for (Int_t i = n - 4; i > 0; i--)
if (buf[i] == 0x50 && buf[i+1] == 0x4b &&
buf[i+2] == 0x05 && buf[i+3] == 0x06) {
return pos + i;
}
}
Error("FindEndHeader", "did not find end header in %s", fArchiveName.Data());
return 0;
}
Int_t TZIPFile::ReadEndHeader(Long64_t pos)
{
char buf[kEND_HEADER_SIZE];
fFile->Seek(pos);
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
Get(buf, kZIP_MAGIC_LEN) != kEND_HEADER_MAGIC) {
Error("ReadEndHeader", "wrong end header magic in %s", fArchiveName.Data());
return -1;
}
if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kEND_HEADER_SIZE - kZIP_MAGIC_LEN)) {
Error("ReadEndHeader", "error reading %d end header bytes from %s",
kEND_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
UInt_t disk = Get(buf + kEND_DISK_OFF, kEND_DISK_LEN);
UInt_t dirdisk = Get(buf + kEND_DIR_DISK_OFF, kEND_DIR_DISK_LEN);
UInt_t dhdrs = Get(buf + kEND_DISK_HDRS_OFF, kEND_DISK_HDRS_LEN);
UInt_t thdrs = Get(buf + kEND_TOTAL_HDRS_OFF, kEND_TOTAL_HDRS_LEN);
Long64_t dirsz = Get(buf + kEND_DIR_SIZE_OFF, kEND_DIR_SIZE_LEN);
Long64_t diroff = Get(buf + kEND_DIR_OFFSET_OFF, kEND_DIR_OFFSET_LEN);
Int_t commlen = Get(buf + kEND_COMMENTLEN_OFF, kEND_COMMENTLEN_LEN);
if (disk != 0 || dirdisk != 0) {
Error("ReadHeader", "only single disk archives are supported in %s",
fArchiveName.Data());
return -1;
}
if (dhdrs != thdrs) {
Error("ReadEndHeader", "inconsistency in end header data in %s",
fArchiveName.Data());
return -1;
}
char *comment = new char[commlen+1];
if (fFile->ReadBuffer(comment, commlen)) {
Error("ReadEndHeader", "error reading %d end header comment bytes from %s",
commlen, fArchiveName.Data());
delete [] comment;
return -1;
}
comment[commlen] = '\0';
fComment = comment;
fDirOffset = fDirPos = diroff;
fDirSize = dirsz;
delete [] comment;
Long64_t recoff = ReadZip64EndLocator(pos - kZIP64_EDL_HEADER_SIZE);
if (recoff < 0) {
if (recoff == -1)
return -1;
return 0;
}
if (ReadZip64EndRecord(recoff) < 0)
return -1;
return 0;
}
Long64_t TZIPFile::ReadZip64EndLocator(Long64_t pos)
{
char buf[kZIP64_EDL_HEADER_SIZE];
fFile->Seek(pos);
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
Get(buf, kZIP_MAGIC_LEN) != kZIP64_EDL_HEADER_MAGIC) {
return -2;
}
if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kZIP64_EDL_HEADER_SIZE - kZIP_MAGIC_LEN)) {
Error("ReadZip64EndLocator", "error reading %d Zip64 end locator header bytes from %s",
kZIP64_EDL_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
UInt_t dirdisk = Get( buf + kZIP64_EDL_DISK_OFF, kZIP64_EDL_DISK_LEN);
Long64_t recoff = Get64(buf + kZIP64_EDL_REC_OFFSET_OFF, kZIP64_EDL_REC_OFFSET_LEN);
UInt_t totdisk = Get( buf + kZIP64_EDL_TOTAL_DISK_OFF, kZIP64_EDL_TOTAL_DISK_LEN);
if (dirdisk != 0 || totdisk != 1) {
Error("ReadZip64EndLocator", "only single disk archives are supported in %s",
fArchiveName.Data());
return -1;
}
return recoff;
}
Int_t TZIPFile::ReadZip64EndRecord(Long64_t pos)
{
char buf[kZIP64_EDR_HEADER_SIZE];
fFile->Seek(pos);
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
Get(buf, kZIP_MAGIC_LEN) != kZIP64_EDR_HEADER_MAGIC) {
Error("ReadZip64EndRecord", "no Zip64 end of directory record\n");
return -1;
}
if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kZIP64_EDR_HEADER_SIZE - kZIP_MAGIC_LEN)) {
Error("ReadZip64EndRecord", "error reading %d Zip64 end record header bytes from %s",
kZIP64_EDR_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
Long64_t dirsz = Get64(buf + kZIP64_EDR_DIR_SIZE_OFF, kZIP64_EDR_DIR_SIZE_LEN);
Long64_t diroff = Get64(buf + kZIP64_EDR_DIR_OFFSET_OFF, kZIP64_EDR_DIR_OFFSET_LEN);
fDirOffset = fDirPos = diroff;
fDirSize = dirsz;
return 0;
}
Int_t TZIPFile::ReadDirectory()
{
char buf[kDIR_HEADER_SIZE];
UInt_t n, i;
fFile->Seek(fDirPos);
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
(n = Get(buf, kZIP_MAGIC_LEN)) != kDIR_HEADER_MAGIC) {
Error("ReadDirectory", "wrong directory header magic in %s",
fArchiveName.Data());
return -1;
}
for (i = 0; n == kDIR_HEADER_MAGIC; i++) {
if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kDIR_HEADER_SIZE - kZIP_MAGIC_LEN)) {
Error("ReadDirectory", "error reading %d directory bytes from %s",
kDIR_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
UInt_t version = Get(buf + kDIR_VREQD_OFF, kDIR_VREQD_LEN);
UInt_t flags = Get(buf + kDIR_FLAG_OFF, kDIR_FLAG_LEN);
UInt_t method = Get(buf + kDIR_METHOD_OFF, kDIR_METHOD_LEN);
UInt_t time = Get(buf + kDIR_DATE_OFF, kDIR_DATE_LEN);
UInt_t crc32 = Get(buf + kDIR_CRC32_OFF, kDIR_CRC32_LEN);
Long64_t csize = Get(buf + kDIR_CSIZE_OFF, kDIR_CSIZE_LEN);
Long64_t usize = Get(buf + kDIR_USIZE_OFF, kDIR_USIZE_LEN);
Int_t namelen = Get(buf + kDIR_NAMELEN_OFF, kDIR_NAMELEN_LEN);
Int_t extlen = Get(buf + kDIR_EXTRALEN_OFF, kDIR_EXTRALEN_LEN);
Int_t commlen = Get(buf + kDIR_COMMENTLEN_OFF, kDIR_COMMENTLEN_LEN);
UInt_t disk = Get(buf + kDIR_DISK_START_OFF, kDIR_DISK_START_LEN);
UInt_t iattr = Get(buf + kDIR_INT_ATTR_OFF, kDIR_INT_ATTR_LEN);
UInt_t xattr = Get(buf + kDIR_EXT_ATTR_OFF, kDIR_EXT_ATTR_LEN);
Long64_t offset = Get(buf + kDIR_ENTRY_POS_OFF, kDIR_ENTRY_POS_LEN);
if (Get(buf + kDIR_MAGIC_OFF, kZIP_MAGIC_LEN) != kDIR_HEADER_MAGIC ||
version > kARCHIVE_VERSION ||
flags & 8 ||
(method != kSTORED && method != kDEFLATED) ||
disk != 0 ||
csize < 0 ||
usize < 0 ||
csize > kMaxUInt ||
usize > kMaxUInt) {
Error("ReadDirectory", "inconsistency in directory data in %s",
fArchiveName.Data());
return -1;
}
char *name = new char[namelen+1];
char *extra = new char[extlen];
char *comment = new char[commlen+1];
if (fFile->ReadBuffer(name, namelen) ||
fFile->ReadBuffer(extra, extlen) ||
fFile->ReadBuffer(comment, commlen)) {
Error("ReadDirectory", "error reading additional directory data from %s",
fArchiveName.Data());
delete [] name;
delete [] extra;
delete [] comment;
return -1;
}
name[namelen] = '\0';
comment[commlen] = '\0';
TZIPMember *m = new TZIPMember(name);
fMembers->Add(m);
m->fMethod = method;
m->fLevel = method == kSTORED ? 0
: (flags & 6)/2 == 0 ? 3
: (flags & 6)/2 == 1 ? 9
: (flags & 6)/2 == 2 ? 2
: (flags & 6)/2 == 3 ? 1
: 3;
m->fCsize = csize;
m->fDsize = usize;
m->fCRC32 = crc32;
m->fModTime.Set(time, kTRUE);
m->fGlobalLen = extlen;
m->fGlobal = extra;
m->fComment = comment;
m->fAttrInt = iattr;
m->fAttrExt = xattr;
m->fPosition = offset;
delete [] name;
delete [] comment;
if (DecodeZip64ExtendedExtraField(m) == -1)
return -1;
if (gDebug)
Info("ReadDirectory", "%lld %lld %s %s",
m->GetDecompressedSize(), m->GetCompressedSize(),
m->GetModTime().AsSQLString(), m->GetName());
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN)) {
Error("ReadDirectory", "error reading %d directory bytes from %s",
kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
n = Get(buf, kZIP_MAGIC_LEN);
}
if (n != kEND_HEADER_MAGIC && n != kZIP64_EDR_HEADER_MAGIC) {
Error("ReadDirectory", "wrong end header magic in %s", fArchiveName.Data());
return -1;
}
return 0;
}
Int_t TZIPFile::ReadMemberHeader(TZIPMember *member)
{
char buf[kENTRY_HEADER_SIZE];
fFile->Seek(member->fPosition);
if (fFile->ReadBuffer(buf, kZIP_MAGIC_LEN) ||
Get(buf, kZIP_MAGIC_LEN) != kENTRY_HEADER_MAGIC) {
Error("ReadMemberHeader", "wrong entry header magic in %s",
fArchiveName.Data());
return -1;
}
if (fFile->ReadBuffer(buf + kZIP_MAGIC_LEN, kENTRY_HEADER_SIZE - kZIP_MAGIC_LEN)) {
Error("ReadMemberHeader", "error reading %d member header bytes from %s",
kENTRY_HEADER_SIZE - kZIP_MAGIC_LEN, fArchiveName.Data());
return -1;
}
Int_t namelen = Get(buf + kENTRY_NAMELEN_OFF, kENTRY_NAMELEN_LEN);
Int_t extlen = Get(buf + kENTRY_EXTRALEN_OFF, kENTRY_EXTRALEN_LEN);
member->fFilePosition = member->fPosition + kENTRY_HEADER_SIZE +
namelen + extlen;
return 0;
}
Int_t TZIPFile::DecodeZip64ExtendedExtraField(TZIPMember *m, Bool_t global)
{
char *buf;
Int_t len;
Int_t ret = -2;
if (global) {
buf = (char *) m->fGlobal;
len = m->fGlobalLen;
} else {
buf = (char *) m->fLocal;
len = m->fLocalLen;
}
if (!buf || !len) {
return ret;
}
Int_t off = 0;
while (len > 0) {
UInt_t tag = Get(buf + off + kZIP64_EXTENDED_MAGIC_OFF, kZIP64_EXTENDED_MAGIC_LEN);
UInt_t size = Get(buf + off + kZIP64_EXTENDED_SIZE_OFF, kZIP64_EXTENDED_SIZE_LEN);
if (tag == kZIP64_EXTENDED_MAGIC) {
Long64_t usize = Get64(buf + off + kZIP64_EXTENDED_USIZE_OFF, kZIP64_EXTENDED_USIZE_LEN);
Long64_t csize = Get64(buf + off + kZIP64_EXTENTED_CSIZE_OFF, kZIP64_EXTENDED_CSIZE_LEN);
m->fDsize = usize;
m->fCsize = csize;
if (size >= 24) {
Long64_t offset = Get64(buf + off + kZIP64_EXTENDED_HDR_OFFSET_OFF, kZIP64_EXTENDED_HDR_OFFSET_LEN);
m->fPosition = offset;
}
ret = 0;
}
len -= (Int_t)size + kZIP64_EXTENDED_MAGIC_LEN + kZIP64_EXTENDED_MAGIC_LEN;
off += (Int_t)size + kZIP64_EXTENDED_MAGIC_LEN + kZIP64_EXTENDED_MAGIC_LEN;
}
return ret;
}
Int_t TZIPFile::SetCurrentMember()
{
fCurMember = 0;
if (fMemberIndex > -1) {
fCurMember = (TZIPMember *) fMembers->At(fMemberIndex);
if (!fCurMember)
return -1;
fMemberName = fCurMember->GetName();
} else {
for (int i = 0; i < fMembers->GetEntriesFast(); i++) {
TZIPMember *m = (TZIPMember *) fMembers->At(i);
if (fMemberName == m->fName) {
fCurMember = m;
fMemberIndex = i;
break;
}
}
if (!fCurMember)
return -1;
}
return ReadMemberHeader((TZIPMember *)fCurMember);
}
UInt_t TZIPFile::Get(const void *buffer, Int_t bytes)
{
UInt_t value = 0;
if (bytes > 4) {
Error("Get", "can not read > 4 byte integers, use Get64");
return value;
}
#ifdef R__BYTESWAP
memcpy(&value, buffer, bytes);
#else
const UChar_t *buf = static_cast<const unsigned char *>(buffer);
for (UInt_t shift = 0; bytes; shift += 8, --bytes, ++buf)
value += *buf << shift;
#endif
return value;
}
ULong64_t TZIPFile::Get64(const void *buffer, Int_t bytes)
{
ULong64_t value = 0;
if (bytes != 8) {
Error("Get64", "bytes must be 8 (asked for %d)", bytes);
return value;
}
#ifdef R__BYTESWAP
memcpy(&value, buffer, bytes);
#else
const UChar_t *buf = static_cast<const unsigned char *>(buffer);
for (UInt_t shift = 0; bytes; shift += 8, --bytes, ++buf)
value += *buf << shift;
#endif
return value;
}
void TZIPFile::Print(Option_t *) const
{
if (fMembers)
fMembers->Print();
}
ClassImp(TZIPMember)
TZIPMember::TZIPMember()
{
fLocal = 0;
fLocalLen = 0;
fGlobal = 0;
fGlobalLen = 0;
fCRC32 = 0;
fAttrInt = 0;
fAttrExt = 0;
fMethod = 0;
fLevel = 0;
}
TZIPMember::TZIPMember(const char *name)
: TArchiveMember(name)
{
fLocal = 0;
fLocalLen = 0;
fGlobal = 0;
fGlobalLen = 0;
fCRC32 = 0;
fAttrInt = 0;
fAttrExt = 0;
fMethod = 0;
fLevel = 0;
}
TZIPMember::TZIPMember(const TZIPMember &member)
: TArchiveMember(member)
{
fLocal = 0;
fLocalLen = member.fLocalLen;
fGlobal = 0;
fGlobalLen = member.fGlobalLen;
fCRC32 = member.fCRC32;
fAttrInt = member.fAttrInt;
fAttrExt = member.fAttrExt;
fMethod = member.fMethod;
fLevel = member.fLevel;
if (member.fLocal) {
fLocal = new char [fLocalLen];
memcpy(fLocal, member.fLocal, fLocalLen);
}
if (member.fGlobal) {
fGlobal = new char [fGlobalLen];
memcpy(fGlobal, member.fGlobal, fGlobalLen);
}
}
TZIPMember &TZIPMember::operator=(const TZIPMember &rhs)
{
if (this != &rhs) {
TArchiveMember::operator=(rhs);
delete [] (char*) fLocal;
delete [] (char*) fGlobal;
fLocal = 0;
fLocalLen = rhs.fLocalLen;
fGlobal = 0;
fGlobalLen = rhs.fGlobalLen;
fCRC32 = rhs.fCRC32;
fAttrInt = rhs.fAttrInt;
fAttrExt = rhs.fAttrExt;
fMethod = rhs.fMethod;
fLevel = rhs.fLevel;
if (rhs.fLocal) {
fLocal = new char [fLocalLen];
memcpy(fLocal, rhs.fLocal, fLocalLen);
}
if (rhs.fGlobal) {
fGlobal = new char [fGlobalLen];
memcpy(fGlobal, rhs.fGlobal, fGlobalLen);
}
}
return *this;
}
TZIPMember::~TZIPMember()
{
delete [] (char*) fLocal;
delete [] (char*) fGlobal;
}
void TZIPMember::Print(Option_t *) const
{
printf("%-20lld", fDsize);
printf(" %s %s\n", fModTime.AsSQLString(), fName.Data());
}