QuietUnrar/libunrar/arcread.cpp

713 lines
19 KiB
C++

#include "rar.hpp"
size_t Archive::SearchBlock(int BlockType)
{
size_t Size,Count=0;
while ((Size=ReadHeader())!=0 &&
(BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD))
{
if ((++Count & 127)==0)
Wait();
if (GetHeaderType()==BlockType)
return(Size);
SeekToNext();
}
return(0);
}
size_t Archive::SearchSubBlock(const char *Type)
{
size_t Size;
while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD)
{
if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type))
return(Size);
SeekToNext();
}
return(0);
}
void Archive::UnexpEndArcMsg()
{
int64 ArcSize=FileLength();
if (CurBlockPos>ArcSize || NextBlockPos>ArcSize)
{
#ifndef SHELL_EXT
Log(FileName,St(MLogUnexpEOF));
#endif
ErrHandler.SetErrorCode(WARNING);
}
}
size_t Archive::ReadHeader()
{
CurBlockPos=Tell();
#ifndef SFX_MODULE
if (OldFormat)
return(ReadOldHeader());
#endif
RawRead Raw(this);
bool Decrypt=Encrypted && CurBlockPos>=(int64)SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD;
if (Decrypt)
{
#if defined(SHELL_EXT) || defined(NOCRYPT)
return(0);
#else
if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE)
{
UnexpEndArcMsg();
return(0);
}
if (*Cmd->Password==0)
#ifdef RARDLL
if (Cmd->Callback==NULL ||
Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1)
{
Close();
ErrHandler.Exit(USER_BREAK);
}
#else
if (!GetPassword(PASSWORD_ARCHIVE,FileName,Cmd->Password,sizeof(Cmd->Password)))
{
Close();
ErrHandler.Exit(USER_BREAK);
}
#endif
HeadersCrypt.SetCryptKeys(Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36);
Raw.SetCrypt(&HeadersCrypt);
#endif
}
Raw.Read(SIZEOF_SHORTBLOCKHEAD);
if (Raw.Size()==0)
{
UnexpEndArcMsg();
return(0);
}
Raw.Get(ShortBlock.HeadCRC);
byte HeadType;
Raw.Get(HeadType);
ShortBlock.HeadType=(HEADER_TYPE)HeadType;
Raw.Get(ShortBlock.Flags);
Raw.Get(ShortBlock.HeadSize);
if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
{
#ifndef SHELL_EXT
Log(FileName,St(MLogFileHead),"???");
#endif
BrokenFileHeader=true;
ErrHandler.SetErrorCode(CRC_ERROR);
return(0);
}
if (ShortBlock.HeadType==COMM_HEAD)
Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
else
if (ShortBlock.HeadType==MAIN_HEAD && (ShortBlock.Flags & MHD_COMMENT)!=0)
Raw.Read(SIZEOF_NEWMHD-SIZEOF_SHORTBLOCKHEAD);
else
Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
NextBlockPos=CurBlockPos+ShortBlock.HeadSize;
switch(ShortBlock.HeadType)
{
case MAIN_HEAD:
*(BaseBlock *)&NewMhd=ShortBlock;
Raw.Get(NewMhd.HighPosAV);
Raw.Get(NewMhd.PosAV);
if (NewMhd.Flags & MHD_ENCRYPTVER)
Raw.Get(NewMhd.EncryptVer);
break;
case ENDARC_HEAD:
*(BaseBlock *)&EndArcHead=ShortBlock;
if (EndArcHead.Flags & EARC_DATACRC)
Raw.Get(EndArcHead.ArcDataCRC);
if (EndArcHead.Flags & EARC_VOLNUMBER)
Raw.Get(EndArcHead.VolNumber);
break;
case FILE_HEAD:
case NEWSUB_HEAD:
{
FileHeader *hd=ShortBlock.HeadType==FILE_HEAD ? &NewLhd:&SubHead;
*(BaseBlock *)hd=ShortBlock;
Raw.Get(hd->PackSize);
Raw.Get(hd->UnpSize);
Raw.Get(hd->HostOS);
Raw.Get(hd->FileCRC);
Raw.Get(hd->FileTime);
Raw.Get(hd->UnpVer);
Raw.Get(hd->Method);
Raw.Get(hd->NameSize);
Raw.Get(hd->FileAttr);
if (hd->Flags & LHD_LARGE)
{
Raw.Get(hd->HighPackSize);
Raw.Get(hd->HighUnpSize);
}
else
{
hd->HighPackSize=hd->HighUnpSize=0;
if (hd->UnpSize==0xffffffff)
{
// 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->UnpSize=(uint)(INT64NDF);
hd->HighUnpSize=(uint)(INT64NDF>>32);
}
}
hd->FullPackSize=INT32TO64(hd->HighPackSize,hd->PackSize);
hd->FullUnpSize=INT32TO64(hd->HighUnpSize,hd->UnpSize);
char FileName[NM*4];
int NameSize=Min(hd->NameSize,sizeof(FileName)-1);
Raw.Get((byte *)FileName,NameSize);
FileName[NameSize]=0;
strncpyz(hd->FileName,FileName,ASIZE(hd->FileName));
if (hd->HeadType==NEWSUB_HEAD)
{
int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD;
if (hd->Flags & LHD_SALT)
DataSize-=SALT_SIZE;
if (DataSize>0)
{
hd->SubData.Alloc(DataSize);
Raw.Get(&hd->SubData[0],DataSize);
if (hd->CmpName(SUBHEAD_TYPE_RR))
{
byte *D=&hd->SubData[8];
RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24);
}
}
}
else
if (hd->HeadType==FILE_HEAD)
{
if (hd->Flags & LHD_UNICODE)
{
EncodeFileName NameCoder;
size_t Length=strlen(FileName);
if (Length==hd->NameSize)
{
UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1);
WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1);
ExtToInt(hd->FileName,hd->FileName);
}
else
{
Length++;
NameCoder.Decode(FileName,(byte *)FileName+Length,
hd->NameSize-Length,hd->FileNameW,
sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0]));
}
if (*hd->FileNameW==0)
hd->Flags &= ~LHD_UNICODE;
}
else
*hd->FileNameW=0;
#ifndef SFX_MODULE
ConvertNameCase(hd->FileName);
ConvertNameCase(hd->FileNameW);
#endif
ConvertUnknownHeader();
}
if (hd->Flags & LHD_SALT)
Raw.Get(hd->Salt,SALT_SIZE);
hd->mtime.SetDos(hd->FileTime);
hd->ctime.Reset();
hd->atime.Reset();
hd->arctime.Reset();
if (hd->Flags & LHD_EXTTIME)
{
ushort Flags;
Raw.Get(Flags);
RarTime *tbl[4];
tbl[0]=&NewLhd.mtime;
tbl[1]=&NewLhd.ctime;
tbl[2]=&NewLhd.atime;
tbl[3]=&NewLhd.arctime;
for (int I=0;I<4;I++)
{
RarTime *CurTime=tbl[I];
uint rmode=Flags>>(3-I)*4;
if ((rmode & 8)==0)
continue;
if (I!=0)
{
uint DosTime;
Raw.Get(DosTime);
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.Get(CurByte);
rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
}
CurTime->SetLocal(&rlt);
}
}
NextBlockPos+=hd->FullPackSize;
bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0;
HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff;
if (hd->HeadCRC!=HeaderCRC)
{
if (hd->HeadType==NEWSUB_HEAD)
strcat(hd->FileName,"- ???");
BrokenFileHeader=true;
ErrHandler.SetErrorCode(WARNING);
#ifndef SHELL_EXT
Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName));
Alarm();
#endif
}
}
break;
#ifndef SFX_MODULE
case COMM_HEAD:
*(BaseBlock *)&CommHead=ShortBlock;
Raw.Get(CommHead.UnpSize);
Raw.Get(CommHead.UnpVer);
Raw.Get(CommHead.Method);
Raw.Get(CommHead.CommCRC);
break;
case SIGN_HEAD:
*(BaseBlock *)&SignHead=ShortBlock;
Raw.Get(SignHead.CreationTime);
Raw.Get(SignHead.ArcNameSize);
Raw.Get(SignHead.UserNameSize);
break;
case AV_HEAD:
*(BaseBlock *)&AVHead=ShortBlock;
Raw.Get(AVHead.UnpVer);
Raw.Get(AVHead.Method);
Raw.Get(AVHead.AVVer);
Raw.Get(AVHead.AVInfoCRC);
break;
case PROTECT_HEAD:
*(BaseBlock *)&ProtectHead=ShortBlock;
Raw.Get(ProtectHead.DataSize);
Raw.Get(ProtectHead.Version);
Raw.Get(ProtectHead.RecSectors);
Raw.Get(ProtectHead.TotalBlocks);
Raw.Get(ProtectHead.Mark,8);
NextBlockPos+=ProtectHead.DataSize;
RecoverySectors=ProtectHead.RecSectors;
break;
case SUB_HEAD:
*(BaseBlock *)&SubBlockHead=ShortBlock;
Raw.Get(SubBlockHead.DataSize);
NextBlockPos+=SubBlockHead.DataSize;
Raw.Get(SubBlockHead.SubType);
Raw.Get(SubBlockHead.Level);
switch(SubBlockHead.SubType)
{
case UO_HEAD:
*(SubBlockHeader *)&UOHead=SubBlockHead;
Raw.Get(UOHead.OwnerNameSize);
Raw.Get(UOHead.GroupNameSize);
if (UOHead.OwnerNameSize>NM-1)
UOHead.OwnerNameSize=NM-1;
if (UOHead.GroupNameSize>NM-1)
UOHead.GroupNameSize=NM-1;
Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize);
Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize);
UOHead.OwnerName[UOHead.OwnerNameSize]=0;
UOHead.GroupName[UOHead.GroupNameSize]=0;
break;
case MAC_HEAD:
*(SubBlockHeader *)&MACHead=SubBlockHead;
Raw.Get(MACHead.fileType);
Raw.Get(MACHead.fileCreator);
break;
case EA_HEAD:
case BEEA_HEAD:
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
Raw.Get(EAHead.UnpSize);
Raw.Get(EAHead.UnpVer);
Raw.Get(EAHead.Method);
Raw.Get(EAHead.EACRC);
break;
case STREAM_HEAD:
*(SubBlockHeader *)&StreamHead=SubBlockHead;
Raw.Get(StreamHead.UnpSize);
Raw.Get(StreamHead.UnpVer);
Raw.Get(StreamHead.Method);
Raw.Get(StreamHead.StreamCRC);
Raw.Get(StreamHead.StreamNameSize);
if (StreamHead.StreamNameSize>NM-1)
StreamHead.StreamNameSize=NM-1;
Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize);
StreamHead.StreamName[StreamHead.StreamNameSize]=0;
break;
}
break;
#endif
default:
if (ShortBlock.Flags & LONG_BLOCK)
{
uint DataSize;
Raw.Get(DataSize);
NextBlockPos+=DataSize;
}
break;
}
HeaderCRC=~Raw.GetCRC(false)&0xffff;
CurHeaderType=ShortBlock.HeadType;
if (Decrypt)
{
NextBlockPos+=Raw.PaddedSize()+SALT_SIZE;
if (ShortBlock.HeadCRC!=HeaderCRC)
{
bool Recovered=false;
if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0)
{
// Last 7 bytes of recovered volume can contain zeroes, because
// REV files store its own information (volume number, etc.) here.
SaveFilePos SavePos(*this);
int64 Length=Tell();
Seek(Length-7,SEEK_SET);
Recovered=true;
for (int J=0;J<7;J++)
if (GetByte()!=0)
Recovered=false;
}
if (!Recovered)
{
#ifndef SILENT
Log(FileName,St(MEncrBadCRC),FileName);
#endif
Close();
BrokenFileHeader=true;
ErrHandler.SetErrorCode(CRC_ERROR);
return(0);
// ErrHandler.Exit(CRC_ERROR);
}
}
}
if (NextBlockPos<=CurBlockPos)
{
#ifndef SHELL_EXT
Log(FileName,St(MLogFileHead),"???");
#endif
BrokenFileHeader=true;
ErrHandler.SetErrorCode(CRC_ERROR);
return(0);
}
return(Raw.Size());
}
#ifndef SFX_MODULE
size_t Archive::ReadOldHeader()
{
RawRead Raw(this);
if (CurBlockPos<=(int64)SFXSize)
{
Raw.Read(SIZEOF_OLDMHD);
Raw.Get(OldMhd.Mark,4);
Raw.Get(OldMhd.HeadSize);
Raw.Get(OldMhd.Flags);
NextBlockPos=CurBlockPos+OldMhd.HeadSize;
CurHeaderType=MAIN_HEAD;
}
else
{
OldFileHeader OldLhd;
Raw.Read(SIZEOF_OLDLHD);
NewLhd.HeadType=FILE_HEAD;
Raw.Get(NewLhd.PackSize);
Raw.Get(NewLhd.UnpSize);
Raw.Get(OldLhd.FileCRC);
Raw.Get(NewLhd.HeadSize);
Raw.Get(NewLhd.FileTime);
Raw.Get(OldLhd.FileAttr);
Raw.Get(OldLhd.Flags);
Raw.Get(OldLhd.UnpVer);
Raw.Get(OldLhd.NameSize);
Raw.Get(OldLhd.Method);
NewLhd.Flags=OldLhd.Flags|LONG_BLOCK;
NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10;
NewLhd.Method=OldLhd.Method+0x30;
NewLhd.NameSize=OldLhd.NameSize;
NewLhd.FileAttr=OldLhd.FileAttr;
NewLhd.FileCRC=OldLhd.FileCRC;
NewLhd.FullPackSize=NewLhd.PackSize;
NewLhd.FullUnpSize=NewLhd.UnpSize;
NewLhd.mtime.SetDos(NewLhd.FileTime);
NewLhd.ctime.Reset();
NewLhd.atime.Reset();
NewLhd.arctime.Reset();
Raw.Read(OldLhd.NameSize);
Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize);
NewLhd.FileName[OldLhd.NameSize]=0;
ConvertNameCase(NewLhd.FileName);
*NewLhd.FileNameW=0;
if (Raw.Size()!=0)
NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize;
CurHeaderType=FILE_HEAD;
}
return(NextBlockPos>CurBlockPos ? Raw.Size():0);
}
#endif
void Archive::ConvertNameCase(char *Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
{
IntToExt(Name,Name);
strupper(Name);
ExtToInt(Name,Name);
}
if (Cmd->ConvertNames==NAMES_LOWERCASE)
{
IntToExt(Name,Name);
strlower(Name);
ExtToInt(Name,Name);
}
}
#ifndef SFX_MODULE
void Archive::ConvertNameCase(wchar *Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
strupperw(Name);
if (Cmd->ConvertNames==NAMES_LOWERCASE)
strlowerw(Name);
}
#endif
bool Archive::IsArcDir()
{
return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY);
}
bool Archive::IsArcLabel()
{
return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8));
}
void Archive::ConvertAttributes()
{
#if defined(_WIN_32) || defined(_EMX)
switch(NewLhd.HostOS)
{
case HOST_MSDOS:
case HOST_OS2:
case HOST_WIN32:
break;
case HOST_UNIX:
case HOST_BEOS:
if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
NewLhd.FileAttr=0x10;
else
NewLhd.FileAttr=0x20;
break;
default:
if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
NewLhd.FileAttr=0x10;
else
NewLhd.FileAttr=0x20;
break;
}
#endif
#ifdef _UNIX
// umask defines which permission bits must not be set by default
// when creating a file or directory.
static mode_t mask = (mode_t) -1;
if (mask == (mode_t) -1)
{
// umask call returns the current umask value. Argument (022) is not
// important here.
mask = umask(022);
// Restore the original umask value, which was changed to 022 above.
umask(mask);
}
switch(NewLhd.HostOS)
{
case HOST_MSDOS:
case HOST_OS2:
case HOST_WIN32:
{
// Mapping MSDOS, OS/2 and Windows file attributes to Unix.
if (NewLhd.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
{
// For directories we use 0777 mask.
NewLhd.FileAttr=0777 & ~mask;
}
else
if (NewLhd.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
{
// For read only files we use 0444 mask with 'w' bits turned off.
NewLhd.FileAttr=0444 & ~mask;
}
else
{
// umask does not set +x for regular files, so we use 0666
// instead of 0777 as for directories.
NewLhd.FileAttr=0666 & ~mask;
}
}
break;
case HOST_UNIX:
case HOST_BEOS:
break;
default:
if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
NewLhd.FileAttr=0x41ff & ~mask;
else
NewLhd.FileAttr=0x81b6 & ~mask;
break;
}
#endif
}
void Archive::ConvertUnknownHeader()
{
if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10))
NewLhd.Flags|=LHD_DIRECTORY;
if (NewLhd.HostOS>=HOST_MAX)
{
if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
NewLhd.FileAttr=0x10;
else
NewLhd.FileAttr=0x20;
}
for (char *s=NewLhd.FileName;*s!=0;s=charnext(s))
{
if (*s=='/' || *s=='\\')
*s=CPATHDIVIDER;
#if defined(_APPLE) && !defined(UNICODE_SUPPORTED)
if ((byte)*s<32 || (byte)*s>127)
*s='_';
#endif
#if defined(_WIN_32) || defined(_EMX)
// ':' in file names is allowed in Unix, but not in Windows.
// Even worse, file data will be written to NTFS stream on NTFS,
// so automatic name correction on file create error in extraction
// routine does not work. In Windows and DOS versions we better
// replace ':' now.
if (*s==':')
*s='_';
#endif
}
for (wchar *s=NewLhd.FileNameW;*s!=0;s++)
{
if (*s=='/' || *s=='\\')
*s=CPATHDIVIDER;
#if defined(_WIN_32) || defined(_EMX)
// ':' in file names is allowed in Unix, but not in Windows.
// Even worse, file data will be written to NTFS stream on NTFS,
// so automatic name correction on file create error in extraction
// routine does not work. In Windows and DOS versions we better
// replace ':' now.
if (*s==':')
*s='_';
#endif
}
}
#ifndef SHELL_EXT
bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile)
{
if (HeaderCRC!=SubHead.HeadCRC)
{
#ifndef SHELL_EXT
Log(FileName,St(MSubHeadCorrupt));
#endif
ErrHandler.SetErrorCode(CRC_ERROR);
return(false);
}
if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36)
{
#ifndef SHELL_EXT
Log(FileName,St(MSubHeadUnknown));
#endif
return(false);
}
if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0)
return(true);
SubDataIO.Init();
Unpack Unpack(&SubDataIO);
Unpack.Init();
if (DestFile==NULL)
{
UnpData->Alloc(SubHead.UnpSize);
SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize);
}
if (SubHead.Flags & LHD_PASSWORD)
if (*Cmd->Password)
SubDataIO.SetEncryption(SubHead.UnpVer,Cmd->Password,
(SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false,
SubHead.UnpVer>=36);
else
return(false);
SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
SubDataIO.EnableShowProgress(false);
SubDataIO.SetFiles(this,DestFile);
SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER)!=0;
SubDataIO.SetSubHeader(&SubHead,NULL);
Unpack.SetDestSize(SubHead.UnpSize);
if (SubHead.Method==0x30)
CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
else
Unpack.DoUnpack(SubHead.UnpVer,false);
if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC)
{
#ifndef SHELL_EXT
Log(FileName,St(MSubHeadDataCRC),SubHead.FileName);
#endif
ErrHandler.SetErrorCode(CRC_ERROR);
if (UnpData!=NULL)
UnpData->Reset();
return(false);
}
return(true);
}
#endif