740 lines
26 KiB
C++
740 lines
26 KiB
C++
#include "rar.hpp"
|
|
|
|
#include "unrar.h"
|
|
#include "unicode.hpp"
|
|
#include "encname.hpp"
|
|
|
|
// arcread.cpp
|
|
unrar_err_t Archive::ReadHeader(size_t * ReadSize_)
|
|
{
|
|
CurBlockPos=Tell();
|
|
|
|
unrar_err_t Error;
|
|
size_t ReadSize;
|
|
switch(Format)
|
|
{
|
|
#ifndef SFX_MODULE
|
|
case RARFMT14:
|
|
Error=ReadHeader14(&ReadSize);
|
|
break;
|
|
#endif
|
|
case RARFMT15:
|
|
Error=ReadHeader15(&ReadSize);
|
|
break;
|
|
case RARFMT50:
|
|
Error=ReadHeader50(&ReadSize);
|
|
break;
|
|
|
|
default: // unreachable
|
|
Error=unrar_err_corrupt;
|
|
break;
|
|
}
|
|
|
|
if (Error!=unrar_ok)
|
|
return Error;
|
|
|
|
if (ReadSize>0 && NextBlockPos<=CurBlockPos)
|
|
return unrar_err_corrupt;
|
|
|
|
*ReadSize_ = ReadSize;
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
|
|
unrar_err_t Archive::ReadHeader15(size_t *ReadSize)
|
|
{
|
|
Raw.Reset();
|
|
|
|
Raw.Read(SIZEOF_SHORTBLOCKHEAD);
|
|
if (Raw.Size()==0)
|
|
return unrar_err_corrupt;
|
|
|
|
ShortBlock.HeadCRC=Raw.Get2();
|
|
|
|
ShortBlock.Reset();
|
|
|
|
uint HeaderType=Raw.Get1();
|
|
ShortBlock.Flags=Raw.Get2();
|
|
ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
|
|
ShortBlock.HeadSize=Raw.Get2();
|
|
|
|
ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
|
|
if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
|
|
return unrar_err_corrupt;
|
|
|
|
// For simpler further processing we map header types common
|
|
// for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include
|
|
// header types specific for RAR 1.5 - 4.x only.
|
|
switch(ShortBlock.HeaderType)
|
|
{
|
|
case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break;
|
|
case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break;
|
|
case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break;
|
|
case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break;
|
|
default: break;
|
|
}
|
|
CurHeaderType=ShortBlock.HeaderType;
|
|
|
|
if (ShortBlock.HeaderType==HEAD3_CMT)
|
|
{
|
|
// Old style (up to RAR 2.9) comment header embedded into main
|
|
// or file header. We must not read the entire ShortBlock.HeadSize here
|
|
// to not break the comment processing logic later.
|
|
Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
|
|
}
|
|
else
|
|
if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0)
|
|
{
|
|
// Old style (up to RAR 2.9) main archive comment embedded into
|
|
// the main archive header found. While we can read the entire
|
|
// ShortBlock.HeadSize here and remove this part of "if", it would be
|
|
// waste of memory, because we'll read and process this comment data
|
|
// in other function anyway and we do not need them here now.
|
|
Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD);
|
|
}
|
|
else
|
|
Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
|
|
|
|
NextBlockPos=CurBlockPos+ShortBlock.HeadSize;
|
|
|
|
switch(ShortBlock.HeaderType)
|
|
{
|
|
case HEAD_MAIN:
|
|
MainHead.Reset();
|
|
*(BaseBlock *)&MainHead=ShortBlock;
|
|
MainHead.HighPosAV=Raw.Get2();
|
|
MainHead.PosAV=Raw.Get4();
|
|
|
|
Solid=(MainHead.Flags & MHD_SOLID)!=0;
|
|
MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
|
|
break;
|
|
case HEAD_FILE:
|
|
case HEAD_SERVICE:
|
|
{
|
|
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
|
|
FileHeader *hd=FileBlock ? &FileHead:&SubHead;
|
|
hd->Reset();
|
|
|
|
*(BaseBlock *)hd=ShortBlock;
|
|
|
|
hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
|
|
hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
|
|
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
|
|
hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
|
|
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
|
|
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
|
|
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
|
|
hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
|
|
hd->Version=(hd->Flags & LHD_VERSION)!=0;
|
|
|
|
hd->DataSize=Raw.Get4();
|
|
uint LowUnpSize=Raw.Get4();
|
|
hd->HostOS=Raw.Get1();
|
|
|
|
hd->FileHash.Type=HASH_CRC32;
|
|
hd->FileHash.CRC32=Raw.Get4();
|
|
|
|
uint FileTime=Raw.Get4();
|
|
hd->UnpVer=Raw.Get1();
|
|
hd->Method=Raw.Get1()-0x30;
|
|
size_t NameSize=Raw.Get2();
|
|
hd->FileAttr=Raw.Get4();
|
|
|
|
if (hd->Encrypted)
|
|
return unrar_err_encrypted;
|
|
|
|
hd->HSType=HSYS_UNKNOWN;
|
|
if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
|
|
hd->HSType=HSYS_UNIX;
|
|
else
|
|
if (hd->HostOS<HOST_MAX)
|
|
hd->HSType=HSYS_WINDOWS;
|
|
|
|
hd->RedirType=FSREDIR_NONE;
|
|
|
|
// RAR 4.x Unix symlink.
|
|
if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
|
|
{
|
|
hd->RedirType=FSREDIR_UNIXSYMLINK;
|
|
*hd->RedirName=0;
|
|
}
|
|
|
|
hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
|
|
|
|
hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
|
|
|
|
uint HighPackSize,HighUnpSize;
|
|
if (hd->LargeFile)
|
|
{
|
|
HighPackSize=Raw.Get4();
|
|
HighUnpSize=Raw.Get4();
|
|
hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
|
|
}
|
|
else
|
|
{
|
|
HighPackSize=HighUnpSize=0;
|
|
// UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
|
|
// that we do not know the unpacked file size and must unpack it
|
|
// until we find the end of file marker in compressed data.
|
|
hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
|
|
}
|
|
hd->PackSize=int32to64(HighPackSize,hd->DataSize);
|
|
hd->UnpSize=int32to64(HighUnpSize,LowUnpSize);
|
|
if (hd->UnknownUnpSize)
|
|
hd->UnpSize=INT64NDF;
|
|
|
|
char FileName[NM*4];
|
|
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
|
|
Raw.GetB((byte *)FileName,ReadNameSize);
|
|
FileName[ReadNameSize]=0;
|
|
|
|
if (FileBlock)
|
|
{
|
|
if ((hd->Flags & LHD_UNICODE)!=0)
|
|
{
|
|
EncodeFileName NameCoder;
|
|
size_t Length=strlen(FileName);
|
|
Length++;
|
|
NameCoder.Decode(FileName,(byte *)FileName+Length,
|
|
NameSize-Length,hd->FileName,
|
|
ASIZE(hd->FileName));
|
|
}
|
|
else
|
|
*hd->FileName=0;
|
|
|
|
char AnsiName[NM];
|
|
IntToExt(FileName,AnsiName,ASIZE(AnsiName));
|
|
GetWideName(AnsiName,hd->FileName,hd->FileName,ASIZE(hd->FileName));
|
|
|
|
ConvertFileHeader(hd);
|
|
}
|
|
else
|
|
{
|
|
CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
|
|
|
|
// Calculate the size of optional data.
|
|
int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
|
|
if ((hd->Flags & LHD_SALT)!=0)
|
|
return unrar_err_encrypted;
|
|
|
|
if (DataSize>0)
|
|
{
|
|
// Here we read optional additional fields for subheaders.
|
|
// They are stored after the file name and before salt.
|
|
hd->SubData.Alloc(DataSize);
|
|
Raw.GetB(&hd->SubData[0],DataSize);
|
|
}
|
|
}
|
|
if ((hd->Flags & LHD_SALT)!=0)
|
|
return unrar_err_encrypted;
|
|
hd->mtime.SetDos(FileTime);
|
|
if ((hd->Flags & LHD_EXTTIME)!=0)
|
|
{
|
|
ushort Flags=Raw.Get2();
|
|
RarTime *tbl[4];
|
|
tbl[0]=&FileHead.mtime;
|
|
tbl[1]=&FileHead.ctime;
|
|
tbl[2]=&FileHead.atime;
|
|
tbl[3]=NULL; // Archive time is not used now.
|
|
for (int I=0;I<4;I++)
|
|
{
|
|
RarTime *CurTime=tbl[I];
|
|
uint rmode=Flags>>(3-I)*4;
|
|
if ((rmode & 8)==0 || CurTime==NULL)
|
|
continue;
|
|
if (I!=0)
|
|
{
|
|
uint DosTime=Raw.Get4();
|
|
CurTime->SetDos(DosTime);
|
|
}
|
|
RarLocalTime rlt;
|
|
CurTime->GetLocal(&rlt);
|
|
if (rmode & 4)
|
|
rlt.Second++;
|
|
rlt.Reminder=0;
|
|
int count=rmode&3;
|
|
for (int J=0;J<count;J++)
|
|
{
|
|
byte CurByte=Raw.Get1();
|
|
rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
|
|
}
|
|
CurTime->SetLocal(&rlt);
|
|
}
|
|
}
|
|
NextBlockPos+=hd->PackSize;
|
|
bool CRCProcessedOnly=hd->CommentInHeader;
|
|
ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
|
|
if (hd->HeadCRC!=HeaderCRC)
|
|
return unrar_err_corrupt;
|
|
}
|
|
break;
|
|
case HEAD_ENDARC:
|
|
*(BaseBlock *)&EndArcHead=ShortBlock;
|
|
EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
|
|
EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
|
|
EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
|
|
EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
|
|
if (EndArcHead.DataCRC)
|
|
EndArcHead.ArcDataCRC=Raw.Get4();
|
|
if (EndArcHead.StoreVolNumber)
|
|
return unrar_err_segmented;
|
|
break;
|
|
default:
|
|
if (ShortBlock.Flags & LONG_BLOCK)
|
|
NextBlockPos+=Raw.Get4();
|
|
break;
|
|
}
|
|
|
|
ushort HeaderCRC=Raw.GetCRC15(false);
|
|
|
|
// Old AV header does not have header CRC properly set.
|
|
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
|
|
ShortBlock.HeaderType!=HEAD3_AV)
|
|
return unrar_err_corrupt;
|
|
|
|
if (NextBlockPos<=CurBlockPos)
|
|
return unrar_err_corrupt;
|
|
|
|
*ReadSize=Raw.Size();
|
|
return unrar_ok;
|
|
}
|
|
|
|
|
|
unrar_err_t Archive::ReadHeader50(size_t *ReadSize)
|
|
{
|
|
Raw.Reset();
|
|
|
|
// Header size must not occupy more than 3 variable length integer bytes
|
|
// resulting in 2 MB maximum header size, so here we read 4 byte CRC32
|
|
// followed by 3 bytes or less of header size.
|
|
const size_t FirstReadSize=7; // Smallest possible block size.
|
|
if (Raw.Read(FirstReadSize)<FirstReadSize)
|
|
return unrar_err_arc_eof;
|
|
|
|
ShortBlock.Reset();
|
|
ShortBlock.HeadCRC=Raw.Get4();
|
|
uint SizeBytes=Raw.GetVSize(4);
|
|
uint64 BlockSize=Raw.GetV();
|
|
|
|
if (BlockSize==0 || SizeBytes==0)
|
|
return unrar_err_corrupt;
|
|
|
|
int SizeToRead=int(BlockSize);
|
|
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
|
|
uint HeaderSize=4+SizeBytes+(uint)BlockSize;
|
|
|
|
if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
|
|
return unrar_err_corrupt;
|
|
|
|
Raw.Read(SizeToRead);
|
|
|
|
if (Raw.Size()<HeaderSize)
|
|
return unrar_err_arc_eof;
|
|
|
|
uint HeaderCRC=Raw.GetCRC50();
|
|
|
|
ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV();
|
|
ShortBlock.Flags=(uint)Raw.GetV();
|
|
ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0;
|
|
ShortBlock.HeadSize=HeaderSize;
|
|
|
|
CurHeaderType=ShortBlock.HeaderType;
|
|
|
|
bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC);
|
|
if (BadCRC)
|
|
return unrar_err_corrupt;
|
|
|
|
uint64 ExtraSize=0;
|
|
if ((ShortBlock.Flags & HFL_EXTRA)!=0)
|
|
{
|
|
ExtraSize=Raw.GetV();
|
|
if (ExtraSize>=ShortBlock.HeadSize)
|
|
return unrar_err_corrupt;
|
|
}
|
|
|
|
uint64 DataSize=0;
|
|
if ((ShortBlock.Flags & HFL_DATA)!=0)
|
|
DataSize=Raw.GetV();
|
|
|
|
NextBlockPos=CurBlockPos+ShortBlock.HeadSize+DataSize;
|
|
|
|
switch(ShortBlock.HeaderType)
|
|
{
|
|
default: break;
|
|
|
|
case HEAD_CRYPT:
|
|
return unrar_err_encrypted;
|
|
case HEAD_MAIN:
|
|
{
|
|
MainHead.Reset();
|
|
*(BaseBlock *)&MainHead=ShortBlock;
|
|
uint ArcFlags=(uint)Raw.GetV();
|
|
|
|
Solid=(ArcFlags & MHFL_SOLID)!=0;
|
|
|
|
if (ExtraSize!=0)
|
|
{
|
|
unrar_err_t Error;
|
|
if ((Error=ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead))!=unrar_ok)
|
|
return Error;
|
|
}
|
|
}
|
|
break;
|
|
case HEAD_FILE:
|
|
case HEAD_SERVICE:
|
|
{
|
|
FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
|
|
hd->Reset();
|
|
*(BaseBlock *)hd=ShortBlock;
|
|
|
|
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
|
|
|
|
hd->LargeFile=true;
|
|
|
|
hd->PackSize=DataSize;
|
|
hd->FileFlags=(uint)Raw.GetV();
|
|
hd->UnpSize=Raw.GetV();
|
|
|
|
hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
|
|
if (hd->UnknownUnpSize)
|
|
hd->UnpSize=INT64NDF;
|
|
|
|
hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
|
|
hd->FileAttr=(uint)Raw.GetV();
|
|
if ((hd->FileFlags & FHFL_UTIME)!=0)
|
|
hd->mtime=(time_t)Raw.Get4();
|
|
|
|
hd->FileHash.Type=HASH_NONE;
|
|
if ((hd->FileFlags & FHFL_CRC32)!=0)
|
|
{
|
|
hd->FileHash.Type=HASH_CRC32;
|
|
hd->FileHash.CRC32=Raw.Get4();
|
|
}
|
|
|
|
hd->RedirType=FSREDIR_NONE;
|
|
|
|
uint CompInfo=(uint)Raw.GetV();
|
|
hd->Method=(CompInfo>>7) & 7;
|
|
hd->UnpVer=CompInfo & 0x3f;
|
|
|
|
hd->HostOS=(byte)Raw.GetV();
|
|
size_t NameSize=(size_t)Raw.GetV();
|
|
hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
|
|
|
|
hd->HSType=HSYS_UNKNOWN;
|
|
if (hd->HostOS==HOST5_UNIX)
|
|
hd->HSType=HSYS_UNIX;
|
|
else
|
|
if (hd->HostOS==HOST5_WINDOWS)
|
|
hd->HSType=HSYS_WINDOWS;
|
|
|
|
hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
|
|
hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
|
|
hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
|
|
hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
|
|
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
|
|
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
|
|
|
|
if (hd->Encrypted)
|
|
return unrar_err_encrypted;
|
|
|
|
char FileName[NM*4];
|
|
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
|
|
Raw.GetB((byte *)FileName,ReadNameSize);
|
|
FileName[ReadNameSize]=0;
|
|
|
|
UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)-1);
|
|
|
|
// Should do it before converting names, because extra fields can
|
|
// affect name processing, like in case of NTFS streams.
|
|
if (ExtraSize!=0)
|
|
ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
|
|
|
|
if (FileBlock)
|
|
ConvertFileHeader(hd);
|
|
|
|
if (BadCRC) // Add the file name to broken header message displayed above.
|
|
return unrar_err_corrupt;
|
|
}
|
|
break;
|
|
case HEAD_ENDARC:
|
|
{
|
|
*(BaseBlock *)&EndArcHead=ShortBlock;
|
|
uint ArcFlags=(uint)Raw.GetV();
|
|
EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
|
|
EndArcHead.StoreVolNumber=false;
|
|
EndArcHead.DataCRC=false;
|
|
EndArcHead.RevSpace=false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (NextBlockPos<=CurBlockPos)
|
|
return unrar_err_corrupt;
|
|
|
|
*ReadSize=Raw.Size();
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
|
|
unrar_err_t Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
|
|
{
|
|
// Read extra data from the end of block skipping any fields before it.
|
|
size_t ExtraStart=Raw->Size()-ExtraSize;
|
|
if (ExtraStart<Raw->GetPos())
|
|
return unrar_err_corrupt;
|
|
Raw->SetPos(ExtraStart);
|
|
while (Raw->DataLeft()>=2)
|
|
{
|
|
int64 FieldSize=Raw->GetV();
|
|
if (FieldSize==0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
|
|
break;
|
|
size_t NextPos=size_t(Raw->GetPos()+FieldSize);
|
|
uint64 FieldType=Raw->GetV();
|
|
|
|
FieldSize=Raw->DataLeft(); // Field size without size and type fields.
|
|
|
|
if (bb->HeaderType==HEAD_MAIN)
|
|
{
|
|
MainHeader *hd=(MainHeader *)bb;
|
|
if (FieldType==MHEXTRA_LOCATOR)
|
|
{
|
|
hd->Locator=true;
|
|
uint Flags=(uint)Raw->GetV();
|
|
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
|
|
{
|
|
uint64 Offset=Raw->GetV();
|
|
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
|
|
hd->QOpenOffset=Offset+CurBlockPos;
|
|
}
|
|
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
|
|
{
|
|
uint64 Offset=Raw->GetV();
|
|
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
|
|
hd->RROffset=Offset+CurBlockPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
|
|
{
|
|
FileHeader *hd=(FileHeader *)bb;
|
|
switch(FieldType)
|
|
{
|
|
case FHEXTRA_CRYPT:
|
|
return unrar_err_encrypted;
|
|
case FHEXTRA_HASH:
|
|
{
|
|
FileHeader *hd=(FileHeader *)bb;
|
|
uint Type=(uint)Raw->GetV();
|
|
if (Type==FHEXTRA_HASH_BLAKE2)
|
|
{
|
|
hd->FileHash.Type=HASH_BLAKE2;
|
|
Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
|
|
}
|
|
}
|
|
break;
|
|
case FHEXTRA_HTIME:
|
|
if (FieldSize>=9)
|
|
{
|
|
byte Flags=(byte)Raw->GetV();
|
|
bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
|
|
if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
|
|
{
|
|
if (UnixTime)
|
|
hd->mtime=(time_t)Raw->Get4();
|
|
else
|
|
hd->mtime.SetRaw(Raw->Get8());
|
|
}
|
|
if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
|
|
{
|
|
if (UnixTime)
|
|
hd->ctime=(time_t)Raw->Get4();
|
|
else
|
|
hd->ctime.SetRaw(Raw->Get8());
|
|
}
|
|
if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
|
|
{
|
|
if (UnixTime)
|
|
hd->atime=(time_t)Raw->Get4();
|
|
else
|
|
hd->atime.SetRaw(Raw->Get8());
|
|
}
|
|
}
|
|
break;
|
|
case FHEXTRA_REDIR:
|
|
{
|
|
hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
|
|
uint Flags=(uint)Raw->GetV();
|
|
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
|
|
size_t NameSize=(size_t)Raw->GetV();
|
|
|
|
char UtfName[NM*4];
|
|
*UtfName=0;
|
|
if (NameSize<ASIZE(UtfName)-1)
|
|
{
|
|
Raw->GetB(UtfName,NameSize);
|
|
UtfName[NameSize]=0;
|
|
}
|
|
#ifdef _WIN_ALL
|
|
UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
|
|
#endif
|
|
UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
|
|
}
|
|
break;
|
|
case FHEXTRA_UOWNER:
|
|
{
|
|
uint Flags=(uint)Raw->GetV();
|
|
hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
|
|
hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
|
|
*hd->UnixOwnerName=*hd->UnixGroupName=0;
|
|
if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
|
|
{
|
|
size_t Length=(size_t)Raw->GetV();
|
|
Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
|
|
Raw->GetB(hd->UnixOwnerName,Length);
|
|
hd->UnixOwnerName[Length]=0;
|
|
}
|
|
if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
|
|
{
|
|
size_t Length=(size_t)Raw->GetV();
|
|
Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
|
|
Raw->GetB(hd->UnixGroupName,Length);
|
|
hd->UnixGroupName[Length]=0;
|
|
}
|
|
#ifdef _UNIX
|
|
if (hd->UnixOwnerNumeric)
|
|
hd->UnixOwnerID=(uid_t)Raw->GetV();
|
|
if (hd->UnixGroupNumeric)
|
|
hd->UnixGroupID=(uid_t)Raw->GetV();
|
|
#else
|
|
// Need these fields in Windows too for 'list' command,
|
|
// but uid_t and gid_t are not defined.
|
|
if (hd->UnixOwnerNumeric)
|
|
hd->UnixOwnerID=(uint)Raw->GetV();
|
|
if (hd->UnixGroupNumeric)
|
|
hd->UnixGroupID=(uint)Raw->GetV();
|
|
#endif
|
|
hd->UnixOwnerSet=true;
|
|
}
|
|
break;
|
|
case FHEXTRA_SUBDATA:
|
|
{
|
|
hd->SubData.Alloc((size_t)FieldSize);
|
|
Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Raw->SetPos(NextPos);
|
|
}
|
|
|
|
return unrar_ok;
|
|
}
|
|
|
|
|
|
#ifndef SFX_MODULE
|
|
unrar_err_t Archive::ReadHeader14(size_t *ReadSize)
|
|
{
|
|
Raw.Reset();
|
|
|
|
if (CurBlockPos<=(int64)SFXSize)
|
|
{
|
|
Raw.Read(SIZEOF_MAINHEAD14);
|
|
MainHead.Reset();
|
|
byte Mark[4];
|
|
Raw.GetB(Mark,4);
|
|
uint HeadSize=Raw.Get2();
|
|
byte Flags=Raw.Get1();
|
|
NextBlockPos=CurBlockPos+HeadSize;
|
|
CurHeaderType=HEAD_MAIN;
|
|
|
|
Solid=(Flags & MHD_SOLID)!=0;
|
|
MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
|
|
MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
|
|
}
|
|
else
|
|
{
|
|
Raw.Read(SIZEOF_FILEHEAD14);
|
|
FileHead.Reset();
|
|
|
|
FileHead.HeaderType=HEAD_FILE;
|
|
FileHead.DataSize=Raw.Get4();
|
|
FileHead.UnpSize=Raw.Get4();
|
|
FileHead.FileHash.Type=HASH_RAR14;
|
|
FileHead.FileHash.CRC32=Raw.Get2();
|
|
FileHead.HeadSize=Raw.Get2();
|
|
uint FileTime=Raw.Get4();
|
|
FileHead.FileAttr=Raw.Get1();
|
|
FileHead.Flags=Raw.Get1()|LONG_BLOCK;
|
|
FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
|
|
size_t NameSize=Raw.Get1();
|
|
FileHead.Method=Raw.Get1();
|
|
|
|
FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
|
|
FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
|
|
FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
|
|
if (FileHead.Encrypted)
|
|
return unrar_err_encrypted;
|
|
|
|
FileHead.PackSize=FileHead.DataSize;
|
|
FileHead.WinSize=0x10000;
|
|
|
|
FileHead.mtime.SetDos(FileTime);
|
|
|
|
Raw.Read(NameSize);
|
|
|
|
char FileName[NM];
|
|
Raw.GetB((byte *)FileName,Min(NameSize,ASIZE(FileName)));
|
|
FileName[NameSize]=0;
|
|
IntToExt(FileName,FileName,ASIZE(FileName));
|
|
CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
|
|
|
|
if (Raw.Size()!=0)
|
|
NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
|
|
CurHeaderType=HEAD_FILE;
|
|
}
|
|
*ReadSize=(NextBlockPos>CurBlockPos ? Raw.Size():0);
|
|
return unrar_ok;
|
|
}
|
|
#endif
|
|
|
|
|
|
// (removed name case and attribute conversion)
|
|
|
|
bool Archive::IsArcDir()
|
|
{
|
|
return FileHead.Dir;
|
|
}
|
|
|
|
|
|
bool Archive::IsArcLabel()
|
|
{
|
|
return(FileHead.HostOS<=HOST_WIN32 && (FileHead.FileAttr & 8));
|
|
}
|
|
|
|
|
|
void Archive::ConvertFileHeader(FileHeader *hd)
|
|
{
|
|
if (Format==RARFMT15 && hd->UnpVer<20 && (hd->FileAttr & 0x10))
|
|
hd->Dir=true;
|
|
if (hd->HSType==HSYS_UNKNOWN)
|
|
{
|
|
if (hd->Dir)
|
|
hd->FileAttr=0x10;
|
|
else
|
|
hd->FileAttr=0x20;
|
|
}
|
|
}
|
|
|
|
|
|
int64 Archive::GetStartPos()
|
|
{
|
|
int64 StartPos=SFXSize+MarkHead.HeadSize;
|
|
StartPos+=MainHead.HeadSize;
|
|
return StartPos;
|
|
}
|