#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.HeadSizePackSize); 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) { // Let's calculate the size of optional data. int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD; if (hd->Flags & LHD_SALT) DataSize-=SALT_SIZE; 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.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;JSetLocal(&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 *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