unrar/arccmt.cpp000666 000000 000000 00000011147 13343205463 012343 0ustar00000000 000000 static bool IsAnsiEscComment(const wchar *Data,size_t Size); bool Archive::GetComment(Array *CmtData) { if (!MainComment) return false; SaveFilePos SavePos(*this); #ifndef SFX_MODULE uint CmtLength; if (Format==RARFMT14) { Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET); CmtLength=GetByte(); CmtLength+=(GetByte()<<8); } else #endif { if (MainHead.CommentInHeader) { // Old style (RAR 2.9) archive comment embedded into the main // archive header. Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET); if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT) return false; } else { // Current (RAR 3.0+) version of archive comment. Seek(GetStartPos(),SEEK_SET); return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData); } #ifndef SFX_MODULE // Old style (RAR 2.9) comment header embedded into the main // archive header. if (BrokenHeader) { uiMsg(UIERROR_CMTBROKEN,FileName); return false; } CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD; #endif } #ifndef SFX_MODULE if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30) { if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35)) return false; ComprDataIO DataIO; DataIO.SetTestMode(true); uint UnpCmtLength; if (Format==RARFMT14) { #ifdef RAR_NOCRYPT return false; #else UnpCmtLength=GetByte(); UnpCmtLength+=(GetByte()<<8); CmtLength-=2; DataIO.SetCmt13Encryption(); CommHead.UnpVer=15; #endif } else UnpCmtLength=CommHead.UnpSize; DataIO.SetFiles(this,NULL); DataIO.EnableShowProgress(false); DataIO.SetPackedSizeToRead(CmtLength); DataIO.UnpHash.Init(HASH_CRC32,1); DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet. Unpack CmtUnpack(&DataIO); CmtUnpack.Init(0x10000,false); CmtUnpack.SetDestSize(UnpCmtLength); CmtUnpack.DoUnpack(CommHead.UnpVer,false); if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC) { uiMsg(UIERROR_CMTBROKEN,FileName); return false; } else { byte *UnpData; size_t UnpDataSize; DataIO.GetUnpackedData(&UnpData,&UnpDataSize); #ifdef _WIN_ALL // If we ever decide to extend it to Android, we'll need to alloc // 4x memory for OEM to UTF-8 output here. OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); #endif CmtData->Alloc(UnpDataSize+1); memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); CmtData->Alloc(wcslen(CmtData->Addr(0))); } } else { if (CmtLength==0) return false; Array CmtRaw(CmtLength); int ReadSize=Read(&CmtRaw[0],CmtLength); if (ReadSize>=0 && (uint)ReadSizeAlloc(CmtLength+1); CmtRaw.Push(0); #ifdef _WIN_ALL // If we ever decide to extend it to Android, we'll need to alloc // 4x memory for OEM to UTF-8 output here. OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); #endif CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); CmtData->Alloc(wcslen(CmtData->Addr(0))); } #endif return CmtData->Size() > 0; } bool Archive::ReadCommentData(Array *CmtData) { Array CmtRaw; if (!ReadSubData(&CmtRaw,NULL)) return false; size_t CmtSize=CmtRaw.Size(); CmtRaw.Push(0); CmtData->Alloc(CmtSize+1); if (Format==RARFMT50) UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); else if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) { RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); (*CmtData)[CmtSize/2]=0; } else { CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); } CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. return true; } void Archive::ViewComment() { if (Cmd->DisableComment) return; Array CmtBuf; if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. { size_t CmtSize=CmtBuf.Size(); wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); if (ChPtr!=NULL) CmtSize=ChPtr-&CmtBuf[0]; mprintf(L"\n"); OutComment(&CmtBuf[0],CmtSize); } } unrar/archive.cpp000666 000000 000000 00000017672 13343205463 012524 0ustar00000000 000000 #include "rar.hpp" #include "arccmt.cpp" Archive::Archive(RAROptions *InitCmd) { Cmd=NULL; // Just in case we'll have an exception in 'new' below. DummyCmd=(InitCmd==NULL); Cmd=DummyCmd ? (new RAROptions):InitCmd; OpenShared=Cmd->OpenShared; Format=RARFMT15; Solid=false; Volume=false; MainComment=false; Locked=false; Signed=false; FirstVolume=false; NewNumbering=false; SFXSize=0; LatestTime.Reset(); Protected=false; Encrypted=false; FailedHeaderDecryption=false; BrokenHeader=false; LastReadBlock=0; CurBlockPos=0; NextBlockPos=0; memset(&MainHead,0,sizeof(MainHead)); memset(&CryptHead,0,sizeof(CryptHead)); memset(&EndArcHead,0,sizeof(EndArcHead)); VolNumber=0; VolWrite=0; AddingFilesSize=0; AddingHeadersSize=0; *FirstVolumeName=0; Splitting=false; NewArchive=false; SilentOpen=false; #ifdef USE_QOPEN ProhibitQOpen=false; #endif } Archive::~Archive() { if (DummyCmd) delete Cmd; } void Archive::CheckArc(bool EnableBroken) { if (!IsArchive(EnableBroken)) { // If FailedHeaderDecryption is set, we already reported that archive // password is incorrect. if (!FailedHeaderDecryption) uiMsg(UIERROR_BADARCHIVE,FileName); ErrHandler.Exit(RARX_FATAL); } } #if !defined(SFX_MODULE) void Archive::CheckOpen(const wchar *Name) { TOpen(Name); CheckArc(false); } #endif bool Archive::WCheckOpen(const wchar *Name) { if (!WOpen(Name)) return false; if (!IsArchive(false)) { uiMsg(UIERROR_BADARCHIVE,FileName); Close(); return false; } return true; } RARFORMAT Archive::IsSignature(const byte *D,size_t Size) { RARFORMAT Type=RARFMT_NONE; if (Size>=1 && D[0]==0x52) #ifndef SFX_MODULE if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) Type=RARFMT14; else #endif if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) { // We check the last signature byte, so we can return a sensible // warning in case we'll want to change the archive format // sometimes in the future. if (D[6]==0) Type=RARFMT15; else if (D[6]==1) Type=RARFMT50; else if (D[6]>1 && D[6]<5) Type=RARFMT_FUTURE; } return Type; } bool Archive::IsArchive(bool EnableBroken) { Encrypted=false; BrokenHeader=false; // Might be left from previous volume. #ifndef SFX_MODULE if (IsDevice()) { uiMsg(UIERROR_INVALIDNAME,FileName,FileName); return false; } #endif if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) return false; SFXSize=0; RARFORMAT Type; if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) { Format=Type; if (Format==RARFMT14) Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); } else { Array Buffer(MAXSFXSIZE); long CurPos=(long)Tell(); int ReadSize=Read(&Buffer[0],Buffer.Size()-16); for (int I=0;I0 && CurPos<28 && ReadSize>31) { char *D=&Buffer[28-CurPos]; if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) continue; } SFXSize=CurPos+I; Seek(SFXSize,SEEK_SET); if (Format==RARFMT15 || Format==RARFMT50) Read(MarkHead.Mark,SIZEOF_MARKHEAD3); break; } if (SFXSize==0) return false; } if (Format==RARFMT_FUTURE) { uiMsg(UIERROR_NEWRARFORMAT,FileName); return false; } if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. { if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) return false; MarkHead.HeadSize=SIZEOF_MARKHEAD5; } else MarkHead.HeadSize=SIZEOF_MARKHEAD3; #ifdef RARDLL // If callback function is not set, we cannot get the password, // so we skip the initial header processing for encrypted header archive. // It leads to skipped archive comment, but the rest of archive data // is processed correctly. if (Cmd->Callback==NULL) SilentOpen=true; #endif bool HeadersLeft; // Any headers left to read. bool StartFound=false; // Main or encryption headers found. // Skip the archive encryption header if any and read the main header. while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang. { SeekToNext(); HEADER_TYPE Type=GetHeaderType(); // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to // avoid the password prompt. StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT; if (StartFound) break; } // This check allows to make RS based recovery even if password is incorrect. // But we should not do it for EnableBroken or we'll get 'not RAR archive' // messages when extracting encrypted archives with wrong password. if (FailedHeaderDecryption && !EnableBroken) return false; if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing. { if (!FailedHeaderDecryption) // If not reported a wrong password already. uiMsg(UIERROR_MHEADERBROKEN,FileName); if (!EnableBroken) return false; } MainComment=MainHead.CommentInHeader; // If we process non-encrypted archive or can request a password, // we set 'first volume' flag based on file attributes below. // It is necessary for RAR 2.x archives, which did not have 'first volume' // flag in main header. Also for all RAR formats we need to scan until // first file header to set "comment" flag when reading service header. // Unless we are in silent mode, we need to know about presence of comment // immediately after IsArchive call. if (HeadersLeft && (!SilentOpen || !Encrypted)) { SaveFilePos SavePos(*this); int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; HEADER_TYPE SaveCurHeaderType=CurHeaderType; while (ReadHeader()!=0) { HEADER_TYPE HeaderType=GetHeaderType(); if (HeaderType==HEAD_SERVICE) { // If we have a split service headers, it surely indicates non-first // volume. But not split service header does not guarantee the first // volume, because we can have split file after non-split archive // comment. So we do not quit from loop here. FirstVolume=Volume && !SubHead.SplitBefore; } else if (HeaderType==HEAD_FILE) { FirstVolume=Volume && !FileHead.SplitBefore; break; } else if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. break; SeekToNext(); } CurBlockPos=SaveCurBlockPos; NextBlockPos=SaveNextBlockPos; CurHeaderType=SaveCurHeaderType; } if (!Volume || FirstVolume) wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName)); return true; } void Archive::SeekToNext() { Seek(NextBlockPos,SEEK_SET); } // Calculate the block size including encryption fields and padding if any. uint Archive::FullHeaderSize(size_t Size) { if (Encrypted) { Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. if (Format == RARFMT50) Size += SIZE_INITV; else Size += SIZE_SALT30; } return uint(Size); } #ifdef USE_QOPEN bool Archive::Open(const wchar *Name,uint Mode) { // Important if we reuse Archive object and it has virtual QOpen // file position not matching real. For example, for 'l -v volname'. QOpen.Unload(); return File::Open(Name,Mode); } int Archive::Read(void *Data,size_t Size) { size_t Result; if (QOpen.Read(Data,Size,Result)) return (int)Result; return File::Read(Data,Size); } void Archive::Seek(int64 Offset,int Method) { if (!QOpen.Seek(Offset,Method)) File::Seek(Offset,Method); } int64 Archive::Tell() { int64 QPos; if (QOpen.Tell(&QPos)) return QPos; return File::Tell(); } #endif unrar/arcmem.cpp000666 000000 000000 00000002103 13251145606 012326 0ustar00000000 000000 ArcMemory::ArcMemory() { Loaded=false; SeekPos=0; } void ArcMemory::Load(const byte *Data,size_t Size) { ArcData.Alloc(Size); memcpy(&ArcData[0],Data,Size); Loaded=true; SeekPos=0; } bool ArcMemory::Unload() { if (!Loaded) return false; Loaded=false; return true; } bool ArcMemory::Read(void *Data,size_t Size,size_t &Result) { if (!Loaded) return false; Result=(size_t)Min(Size,ArcData.Size()-SeekPos); memcpy(Data,&ArcData[(size_t)SeekPos],Result); SeekPos+=Result; return true; } bool ArcMemory::Seek(int64 Offset,int Method) { if (!Loaded) return false; if (Method==SEEK_SET) { if (Offset<0) SeekPos=0; else SeekPos=Min((uint64)Offset,ArcData.Size()); } else if (Method==SEEK_CUR || Method==SEEK_END) { if (Method==SEEK_END) SeekPos=ArcData.Size(); SeekPos+=(uint64)Offset; if (SeekPos>ArcData.Size()) SeekPos=Offset<0 ? 0 : ArcData.Size(); } return true; } bool ArcMemory::Tell(int64 *Pos) { if (!Loaded) return false; *Pos=SeekPos; return true; } unrar/arcread.cpp000666 000000 000000 00000131564 13343205463 012501 0ustar00000000 000000 #include "rar.hpp" size_t Archive::ReadHeader() { // Once we failed to decrypt an encrypted block, there is no reason to // attempt to do it further. We'll never be successful and only generate // endless errors. if (FailedHeaderDecryption) return 0; CurBlockPos=Tell(); size_t ReadSize; switch(Format) { #ifndef SFX_MODULE case RARFMT14: ReadSize=ReadHeader14(); break; #endif case RARFMT15: ReadSize=ReadHeader15(); break; case RARFMT50: ReadSize=ReadHeader50(); break; } // It is important to check ReadSize>0 here, because it is normal // for RAR2 and RAR3 archives without end of archive block to have // NextBlockPos==CurBlockPos after the end of archive has reached. if (ReadSize>0 && NextBlockPos<=CurBlockPos) { BrokenHeaderMsg(); ReadSize=0; } if (ReadSize==0) CurHeaderType=HEAD_UNKNOWN; return ReadSize; } size_t Archive::SearchBlock(HEADER_TYPE HeaderType) { size_t Size,Count=0; while ((Size=ReadHeader())!=0 && (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) { if ((++Count & 127)==0) Wait(); if (GetHeaderType()==HeaderType) return Size; SeekToNext(); } return 0; } size_t Archive::SearchSubBlock(const wchar *Type) { size_t Size,Count=0; while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) { if ((++Count & 127)==0) Wait(); if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) return Size; SeekToNext(); } return 0; } size_t Archive::SearchRR() { // If locator extra field is available for recovery record, let's utilize it. if (MainHead.Locator && MainHead.RROffset!=0) { uint64 CurPos=Tell(); Seek(MainHead.RROffset,SEEK_SET); size_t Size=ReadHeader(); if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) return Size; Seek(CurPos,SEEK_SET); } // Otherwise scan the entire archive to find the recovery record. return SearchSubBlock(SUBHEAD_TYPE_RR); } void Archive::UnexpEndArcMsg() { int64 ArcSize=FileLength(); // If block positions are equal to file size, this is not an error. // It can happen when we reached the end of older RAR 1.5 archive, // which did not have the end of archive block. if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) { uiMsg(UIERROR_UNEXPEOF,FileName); ErrHandler.SetErrorCode(RARX_WARNING); } } void Archive::BrokenHeaderMsg() { uiMsg(UIERROR_HEADERBROKEN,FileName); BrokenHeader=true; ErrHandler.SetErrorCode(RARX_CRC); } void Archive::UnkEncVerMsg(const wchar *Name) { uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name); ErrHandler.SetErrorCode(RARX_WARNING); } // Return f in case of signed integer overflow or negative parameters // or v1+v2 otherwise. We use it for file offsets, which are signed // for compatibility with off_t in POSIX file functions and third party code. // Signed integer overflow is the undefined behavior according to // C++ standard and it causes fuzzers to complain. inline int64 SafeAdd(int64 v1,int64 v2,int64 f) { return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f; } size_t Archive::ReadHeader15() { RawRead Raw(this); bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; if (Decrypt) { #ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. return 0; #else RequestArcPassword(); byte Salt[SIZE_SALT30]; if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) { UnexpEndArcMsg(); return 0; } HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); Raw.SetCrypt(&HeadersCrypt); #endif } Raw.Read(SIZEOF_SHORTBLOCKHEAD); if (Raw.Size()==0) { UnexpEndArcMsg(); return 0; } 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.HeadSizeReset(); *(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->SaltSet=(hd->Flags & LHD_SALT)!=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(); // RAR15 did not use the special dictionary size to mark dirs. if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) hd->Dir=true; hd->Method=Raw.Get1()-0x30; size_t NameSize=Raw.Get2(); hd->FileAttr=Raw.Get4(); hd->CryptMethod=CRYPT_NONE; if (hd->Encrypted) switch(hd->UnpVer) { case 13: hd->CryptMethod=CRYPT_RAR13; break; case 15: hd->CryptMethod=CRYPT_RAR15; break; case 20: case 26: hd->CryptMethod=CRYPT_RAR20; break; default: hd->CryptMethod=CRYPT_RAR30; break; } hd->HSType=HSYS_UNKNOWN; if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) hd->HSType=HSYS_UNIX; else if (hd->HostOSHSType=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) { *hd->FileName=0; if ((hd->Flags & LHD_UNICODE)!=0) { EncodeFileName NameCoder; size_t Length=strlen(FileName); Length++; if (ReadNameSize>Length) NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length, ReadNameSize-Length,hd->FileName, ASIZE(hd->FileName)); } if (*hd->FileName==0) ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); #ifndef SFX_MODULE ConvertNameCase(hd->FileName); #endif 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) DataSize-=SIZE_SALT30; 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->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; } if ((hd->Flags & LHD_SALT)!=0) Raw.GetB(hd->Salt,SIZE_SALT30); 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;JSetLocal(&rlt); } } // Set to 0 in case of overflow, so end of ReadHeader cares about it. NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); bool CRCProcessedOnly=hd->CommentInHeader; ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); if (hd->HeadCRC!=HeaderCRC) { BrokenHeader=true; ErrHandler.SetErrorCode(RARX_WARNING); // If we have a broken encrypted header, we do not need to display // the error message here, because it will be displayed for such // headers later in this function. Also such headers are unlikely // to have anything sensible in file name field, so it is useless // to display the file name. if (!Decrypt) uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } } 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) VolNumber=EndArcHead.VolNumber=Raw.Get2(); break; #ifndef SFX_MODULE case HEAD3_CMT: *(BaseBlock *)&CommHead=ShortBlock; CommHead.UnpSize=Raw.Get2(); CommHead.UnpVer=Raw.Get1(); CommHead.Method=Raw.Get1(); CommHead.CommCRC=Raw.Get2(); break; case HEAD3_PROTECT: *(BaseBlock *)&ProtectHead=ShortBlock; ProtectHead.DataSize=Raw.Get4(); ProtectHead.Version=Raw.Get1(); ProtectHead.RecSectors=Raw.Get2(); ProtectHead.TotalBlocks=Raw.Get4(); Raw.GetB(ProtectHead.Mark,8); NextBlockPos+=ProtectHead.DataSize; break; case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. *(BaseBlock *)&SubBlockHead=ShortBlock; SubBlockHead.DataSize=Raw.Get4(); NextBlockPos+=SubBlockHead.DataSize; SubBlockHead.SubType=Raw.Get2(); SubBlockHead.Level=Raw.Get1(); switch(SubBlockHead.SubType) { case UO_HEAD: *(SubBlockHeader *)&UOHead=SubBlockHead; UOHead.OwnerNameSize=Raw.Get2(); UOHead.GroupNameSize=Raw.Get2(); if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName)) UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1; if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName)) UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1; Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize); Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize); UOHead.OwnerName[UOHead.OwnerNameSize]=0; UOHead.GroupName[UOHead.GroupNameSize]=0; break; case NTACL_HEAD: *(SubBlockHeader *)&EAHead=SubBlockHead; EAHead.UnpSize=Raw.Get4(); EAHead.UnpVer=Raw.Get1(); EAHead.Method=Raw.Get1(); EAHead.EACRC=Raw.Get4(); break; case STREAM_HEAD: *(SubBlockHeader *)&StreamHead=SubBlockHead; StreamHead.UnpSize=Raw.Get4(); StreamHead.UnpVer=Raw.Get1(); StreamHead.Method=Raw.Get1(); StreamHead.StreamCRC=Raw.Get4(); StreamHead.StreamNameSize=Raw.Get2(); if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); StreamHead.StreamName[StreamHead.StreamNameSize]=0; break; } break; #endif 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) { bool Recovered=false; if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) { // 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) { BrokenHeader=true; ErrHandler.SetErrorCode(RARX_CRC); if (Decrypt) { uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); FailedHeaderDecryption=true; return 0; } } } return Raw.Size(); } size_t Archive::ReadHeader50() { RawRead Raw(this); bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; if (Decrypt) { #if defined(RAR_NOCRYPT) return 0; #else byte HeadersInitV[SIZE_INITV]; if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) { UnexpEndArcMsg(); return 0; } // We repeat the password request only for manually entered passwords // and not for -p. Wrong password can be intentionally provided // in -p to not stop batch processing for encrypted archives. bool GlobalPassword=Cmd->Password.IsSet(); while (true) // Repeat the password prompt for wrong passwords. { RequestArcPassword(); byte PswCheck[SIZE_PSWCHECK]; HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); // Verify password validity. if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P. { // This message is used by Android GUI to reset cached passwords. // Update appropriate code if changed. uiMsg(UIERROR_BADPSW,FileName); FailedHeaderDecryption=true; ErrHandler.SetErrorCode(RARX_BADPWD); return 0; } else // For passwords entered manually. { // This message is used by Android GUI and Windows GUI and SFX to // reset cached passwords. Update appropriate code if changed. uiMsg(UIWAIT_BADPSW,FileName); Cmd->Password.Clean(); } #ifdef RARDLL // Avoid new requests for unrar.dll to prevent the infinite loop // if app always returns the same password. ErrHandler.SetErrorCode(RARX_BADPWD); Cmd->DllError=ERAR_BAD_PASSWORD; ErrHandler.Exit(RARX_BADPWD); #else continue; // Request a password again. #endif } break; } Raw.SetCrypt(&HeadersCrypt); #endif } // Header size must not occupy more than 3 variable length integer bytes // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5), // 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)=ShortBlock.HeadSize) { BrokenHeaderMsg(); return 0; } } uint64 DataSize=0; if ((ShortBlock.Flags & HFL_DATA)!=0) DataSize=Raw.GetV(); NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); // Set to 0 in case of overflow, so end of ReadHeader cares about it. NextBlockPos=SafeAdd(NextBlockPos,DataSize,0); switch(ShortBlock.HeaderType) { case HEAD_CRYPT: { *(BaseBlock *)&CryptHead=ShortBlock; uint CryptVersion=(uint)Raw.GetV(); if (CryptVersion>CRYPT_VERSION) { UnkEncVerMsg(FileName); return 0; } uint EncFlags=(uint)Raw.GetV(); CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; CryptHead.Lg2Count=Raw.Get1(); if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) { UnkEncVerMsg(FileName); return 0; } Raw.GetB(CryptHead.Salt,SIZE_SALT50); if (CryptHead.UsePswCheck) { Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); byte csum[SIZE_PSWCHECK_CSUM]; Raw.GetB(csum,SIZE_PSWCHECK_CSUM); sha256_context ctx; sha256_init(&ctx); sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); byte Digest[SHA256_DIGEST_SIZE]; sha256_done(&ctx, Digest); CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; } Encrypted=true; } break; case HEAD_MAIN: { MainHead.Reset(); *(BaseBlock *)&MainHead=ShortBlock; uint ArcFlags=(uint)Raw.GetV(); Volume=(ArcFlags & MHFL_VOLUME)!=0; Solid=(ArcFlags & MHFL_SOLID)!=0; Locked=(ArcFlags & MHFL_LOCK)!=0; Protected=(ArcFlags & MHFL_PROTECT)!=0; Signed=false; NewNumbering=true; if ((ArcFlags & MHFL_VOLNUMBER)!=0) VolNumber=(uint)Raw.GetV(); else VolNumber=0; FirstVolume=Volume && VolNumber==0; if (ExtraSize!=0) ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); #ifdef USE_QOPEN if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) { // We seek to QO block in the end of archive when processing // QOpen.Load, so we need to preserve current block positions // to not break normal archive processing by calling function. int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; HEADER_TYPE SaveCurHeaderType=CurHeaderType; QOpen.Init(this,false); QOpen.Load(MainHead.QOpenOffset); CurBlockPos=SaveCurBlockPos; NextBlockPos=SaveNextBlockPos; CurHeaderType=SaveCurHeaderType; } #endif } 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.SetUnix((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; // "+ 50" to not mix with old RAR format algorithms. For example, // we may need to use the compression algorithm 15 in the future, // but it was already used in RAR 1.5 and Unpack needs to distinguish // them. hd->UnpVer=(CompInfo & 0x3f) + 50; 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); hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; 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)); // 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) { #ifndef SFX_MODULE ConvertNameCase(hd->FileName); #endif ConvertFileHeader(hd); } if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; #if 0 // For RAR5 format we read the user specified recovery percent here. // It would be useful to do it for shell extension too, so we display // the correct recovery record size in archive properties. But then // we would need to include the entire recovery record processing // code to shell extension, which is not done now. if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) { RecoveryPercent=hd->SubData[0]; RSBlockHeader Header; GetRRInfo(this,&Header); RecoverySize=Header.RecSectionSize*Header.RecCount; } #endif if (BadCRC) // Add the file name to broken header message displayed above. uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } 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; } return Raw.Size(); } #if !defined(RAR_NOCRYPT) void Archive::RequestArcPassword() { if (!Cmd->Password.IsSet()) { #ifdef RARDLL if (Cmd->Callback!=NULL) { wchar PasswordW[MAXPASSWORD]; *PasswordW=0; if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) *PasswordW=0; if (*PasswordW==0) { char PasswordA[MAXPASSWORD]; *PasswordA=0; if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) *PasswordA=0; GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); cleandata(PasswordA,sizeof(PasswordA)); } Cmd->Password.Set(PasswordW); cleandata(PasswordW,sizeof(PasswordW)); } if (!Cmd->Password.IsSet()) { Close(); Cmd->DllError=ERAR_MISSING_PASSWORD; ErrHandler.Exit(RARX_USERBREAK); } #else if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) { Close(); uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. ErrHandler.Exit(RARX_USERBREAK); } #endif Cmd->ManualPassword=true; } } #endif void 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 (ExtraStartGetPos()) return; Raw->SetPos(ExtraStart); while (Raw->DataLeft()>=2) { int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative. if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) break; size_t NextPos=size_t(Raw->GetPos()+FieldSize); uint64 FieldType=Raw->GetV(); FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. if (FieldSize<0) // FieldType is longer than expected extra field size. break; 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: { FileHeader *hd=(FileHeader *)bb; uint EncVersion=(uint)Raw->GetV(); if (EncVersion > CRYPT_VERSION) UnkEncVerMsg(hd->FileName); else { uint Flags=(uint)Raw->GetV(); hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; hd->Lg2Count=Raw->Get1(); if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) UnkEncVerMsg(hd->FileName); Raw->GetB(hd->Salt,SIZE_SALT50); Raw->GetB(hd->InitV,SIZE_INITV); if (hd->UsePswCheck) { Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); // It is important to know if password check data is valid. // If it is damaged and header CRC32 fails to detect it, // archiver would refuse to decompress a possibly valid file. // Since we want to be sure distinguishing a wrong password // or corrupt file data, we use 64-bit password check data // and to control its validity we use 32 bits of password // check data SHA-256 additionally to 32-bit header CRC32. byte csum[SIZE_PSWCHECK_CSUM]; Raw->GetB(csum,SIZE_PSWCHECK_CSUM); sha256_context ctx; sha256_init(&ctx); sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); byte Digest[SHA256_DIGEST_SIZE]; sha256_done(&ctx, Digest); hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; // RAR 5.21 and earlier set PswCheck field in service records to 0 // even if UsePswCheck was present. if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) hd->UsePswCheck=0; } hd->SaltSet=true; hd->CryptMethod=CRYPT_RAR50; hd->Encrypted=true; } } break; 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>=5) { byte Flags=(byte)Raw->GetV(); bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; if ((Flags & FHEXTRA_HTIME_MTIME)!=0) if (UnixTime) hd->mtime.SetUnix(Raw->Get4()); else hd->mtime.SetWin(Raw->Get8()); if ((Flags & FHEXTRA_HTIME_CTIME)!=0) if (UnixTime) hd->ctime.SetUnix(Raw->Get4()); else hd->ctime.SetWin(Raw->Get8()); if ((Flags & FHEXTRA_HTIME_ATIME)!=0) if (UnixTime) hd->atime.SetUnix((time_t)Raw->Get4()); else hd->atime.SetWin(Raw->Get8()); if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. { uint ns; if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) hd->mtime.Adjust(ns); if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) hd->ctime.Adjust(ns); if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) hd->atime.Adjust(ns); } } break; case FHEXTRA_VERSION: if (FieldSize>=1) { Raw->GetV(); // Skip flags field. uint Version=(uint)Raw->GetV(); if (Version!=0) { hd->Version=true; wchar VerText[20]; swprintf(VerText,ASIZE(VerText),L";%u",Version); wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); } } 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 (NameSizeGetB(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=(gid_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: { // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than // required. It did not hurt extraction, because UnRAR 5.21 // and earlier ignored this field and set FieldSize as data left // in entire extra area. But now we set the correct field size // and set FieldSize based on the actual extra record size, // so we need to adjust it for those older archives here. // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE // and always is last in extra area. So since its size is by 1 // less than needed, we always have 1 byte left in extra area, // which fact we use here to detect such archives. if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) FieldSize++; // We cannot allocate too much memory here, because above // we check FieldSize againt Raw size and we control that Raw size // is sensible when reading headers. hd->SubData.Alloc((size_t)FieldSize); Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); } break; } } Raw->SetPos(NextPos); } } #ifndef SFX_MODULE size_t Archive::ReadHeader14() { RawRead Raw(this); if (CurBlockPos<=(int64)SFXSize) { Raw.Read(SIZEOF_MAINHEAD14); MainHead.Reset(); byte Mark[4]; Raw.GetB(Mark,4); uint HeadSize=Raw.Get2(); if (HeadSize<7) return false; byte Flags=Raw.Get1(); NextBlockPos=CurBlockPos+HeadSize; CurHeaderType=HEAD_MAIN; Volume=(Flags & MHD_VOLUME)!=0; Solid=(Flags & MHD_SOLID)!=0; Locked=(Flags & MHD_LOCK)!=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(); if (FileHead.HeadSize<21) return false; 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; FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; FileHead.PackSize=FileHead.DataSize; FileHead.WinSize=0x10000; FileHead.Dir=(FileHead.FileAttr & 0x10)!=0; FileHead.HostOS=HOST_MSDOS; FileHead.HSType=HSYS_WINDOWS; 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)); ConvertNameCase(FileHead.FileName); if (Raw.Size()!=0) NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; CurHeaderType=HEAD_FILE; } return NextBlockPos>CurBlockPos ? Raw.Size() : 0; } #endif #ifndef SFX_MODULE void Archive::ConvertNameCase(wchar *Name) { if (Cmd->ConvertNames==NAMES_UPPERCASE) wcsupper(Name); if (Cmd->ConvertNames==NAMES_LOWERCASE) wcslower(Name); } #endif bool Archive::IsArcDir() { return FileHead.Dir; } void Archive::ConvertAttributes() { #if defined(_WIN_ALL) || defined(_EMX) if (FileHead.HSType!=HSYS_WINDOWS) FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; #endif #ifdef _UNIX // umask defines which permission bits must not be set by default // when creating a file or directory. The typical default value // for the process umask is S_IWGRP | S_IWOTH (octal 022), // resulting in 0644 mode for new files. // Normally umask is applied automatically when creating a file, // but we set attributes with chmod later, so we need to calculate // resulting attributes here. We do it only for non-Unix archives. // We restore native Unix attributes as is, because it can be backup. static mode_t mask = (mode_t) -1; if (mask == (mode_t) -1) { // umask call returns the current umask value. Argument (022) is not // really important here. mask = umask(022); // Restore the original umask value, which was changed to 022 above. umask(mask); } switch(FileHead.HSType) { case HSYS_WINDOWS: { // Mapping MSDOS, OS/2 and Windows file attributes to Unix. if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY { // For directories we use 0777 mask. FileHead.FileAttr=0777 & ~mask; } else if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY { // For read only files we use 0444 mask with 'w' bits turned off. FileHead.FileAttr=0444 & ~mask; } else { // umask does not set +x for regular files, so we use 0666 // instead of 0777 as for directories. FileHead.FileAttr=0666 & ~mask; } } break; case HSYS_UNIX: break; default: if (FileHead.Dir) FileHead.FileAttr=0x41ff & ~mask; else FileHead.FileAttr=0x81b6 & ~mask; break; } #endif } void Archive::ConvertFileHeader(FileHeader *hd) { if (hd->HSType==HSYS_UNKNOWN) if (hd->Dir) hd->FileAttr=0x10; else hd->FileAttr=0x20; #ifdef _WIN_ALL if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); #endif for (wchar *s=hd->FileName;*s!=0;s++) { #ifdef _UNIX // Backslash is the invalid character for Windows file headers, // but it can present in Unix file names extracted in Unix. if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) *s='_'; #endif #if defined(_WIN_ALL) || defined(_EMX) // RAR 5.0 archives do not use '\' as path separator, so if we see it, // it means that it is a part of Unix file name, which we cannot // extract in Windows. if (*s=='\\' && Format==RARFMT50) *s='_'; // ':' 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 // This code must be performed only after other path separator checks, // because it produces backslashes illegal for some of checks above. // Backslash is allowed in file names in Unix, but not in Windows. // Still, RAR 4.x uses backslashes as path separator even in Unix. // Forward slash is not allowed in both systems. In RAR 5.0 we use // the forward slash as universal path separator. if (*s=='/' || *s=='\\' && Format!=RARFMT50) *s=CPATHDIVIDER; } } int64 Archive::GetStartPos() { int64 StartPos=SFXSize+MarkHead.HeadSize; if (Format==RARFMT15) StartPos+=MainHead.HeadSize; else // RAR 5.0. StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); return StartPos; } bool Archive::ReadSubData(Array *UnpData,File *DestFile) { if (BrokenHeader) { uiMsg(UIERROR_SUBHEADERBROKEN,FileName); ErrHandler.SetErrorCode(RARX_CRC); return false; } if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) { uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); return false; } if (SubHead.PackSize==0 && !SubHead.SplitAfter) return true; SubDataIO.Init(); Unpack Unpack(&SubDataIO); Unpack.Init(SubHead.WinSize,false); if (DestFile==NULL) { if (SubHead.UnpSize>0x1000000) { // So huge allocation must never happen in valid archives. uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); return false; } if (UnpData==NULL) SubDataIO.SetTestMode(true); else { UnpData->Alloc((size_t)SubHead.UnpSize); SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); } } if (SubHead.Encrypted) if (Cmd->Password.IsSet()) SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); else return false; SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); SubDataIO.SetPackedSizeToRead(SubHead.PackSize); SubDataIO.EnableShowProgress(false); SubDataIO.SetFiles(this,DestFile); SubDataIO.UnpVolume=SubHead.SplitAfter; SubDataIO.SetSubHeader(&SubHead,NULL); Unpack.SetDestSize(SubHead.UnpSize); if (SubHead.Method==0) CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); else Unpack.DoUnpack(SubHead.UnpVer,false); if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) { uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); ErrHandler.SetErrorCode(RARX_CRC); if (UnpData!=NULL) UnpData->Reset(); return false; } return true; } unrar/blake2s.cpp000666 000000 000000 00000011621 13343205463 012412 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves #include "rar.hpp" #ifdef USE_SSE #include "blake2s_sse.cpp" #endif static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth); static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ); static void blake2s_final( blake2s_state *S, byte *digest ); #include "blake2sp.cpp" static const uint32 blake2s_IV[8] = { 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL }; static const byte blake2s_sigma[10][16] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , }; static inline void blake2s_set_lastnode( blake2s_state *S ) { S->f[1] = ~0U; } /* Some helper functions, not necessarily useful */ static inline void blake2s_set_lastblock( blake2s_state *S ) { if( S->last_node ) blake2s_set_lastnode( S ); S->f[0] = ~0U; } static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc ) { S->t[0] += inc; S->t[1] += ( S->t[0] < inc ); } /* init2 xors IV with input parameter block */ void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth) { #ifdef USE_SSE if (_SSE_Version>=SSE_SSE2) blake2s_init_sse(); #endif S->init(); // Clean data. for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block. S->h[2] ^= node_offset; S->h[3] ^= (node_depth<<16)|0x20000000; } #define G(r,i,m,a,b,c,d) \ a = a + b + m[blake2s_sigma[r][2*i+0]]; \ d = rotr32(d ^ a, 16); \ c = c + d; \ b = rotr32(b ^ c, 12); \ a = a + b + m[blake2s_sigma[r][2*i+1]]; \ d = rotr32(d ^ a, 8); \ c = c + d; \ b = rotr32(b ^ c, 7); static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) { uint32 m[16]; uint32 v[16]; for( size_t i = 0; i < 16; ++i ) m[i] = RawGet4( block + i * 4 ); for( size_t i = 0; i < 8; ++i ) v[i] = S->h[i]; v[ 8] = blake2s_IV[0]; v[ 9] = blake2s_IV[1]; v[10] = blake2s_IV[2]; v[11] = blake2s_IV[3]; v[12] = S->t[0] ^ blake2s_IV[4]; v[13] = S->t[1] ^ blake2s_IV[5]; v[14] = S->f[0] ^ blake2s_IV[6]; v[15] = S->f[1] ^ blake2s_IV[7]; for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows. { G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]); G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]); G(r,2,m,v[ 2],v[ 6],v[10],v[14]); G(r,3,m,v[ 3],v[ 7],v[11],v[15]); G(r,4,m,v[ 0],v[ 5],v[10],v[15]); G(r,5,m,v[ 1],v[ 6],v[11],v[12]); G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]); G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]); } for( size_t i = 0; i < 8; ++i ) S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; } void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) { while( inlen > 0 ) { size_t left = S->buflen; size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; if( inlen > fill ) { memcpy( S->buf + left, in, fill ); // Fill buffer S->buflen += fill; blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); #ifdef USE_SSE #ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. if (_SSE_Version>=SSE_SSE2) #else if (_SSE_Version>=SSE_SSSE3) #endif blake2s_compress_sse( S, S->buf ); else blake2s_compress( S, S->buf ); // Compress #else blake2s_compress( S, S->buf ); // Compress #endif memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left S->buflen -= BLAKE2S_BLOCKBYTES; in += fill; inlen -= fill; } else // inlen <= fill { memcpy( S->buf + left, in, (size_t)inlen ); S->buflen += (size_t)inlen; // Be lazy, do not compress in += inlen; inlen = 0; } } } void blake2s_final( blake2s_state *S, byte *digest ) { if( S->buflen > BLAKE2S_BLOCKBYTES ) { blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); blake2s_compress( S, S->buf ); S->buflen -= BLAKE2S_BLOCKBYTES; memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); } blake2s_increment_counter( S, ( uint32 )S->buflen ); blake2s_set_lastblock( S ); memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ blake2s_compress( S, S->buf ); for( int i = 0; i < 8; ++i ) /* Output full hash */ RawPut4( S->h[i], digest + 4 * i ); } unrar/blake2sp.cpp000666 000000 000000 00000007346 13343205463 012603 0ustar00000000 000000 /* BLAKE2 reference source code package - reference C implementations Written in 2012 by Samuel Neves To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . */ #define PARALLELISM_DEGREE 8 void blake2sp_init( blake2sp_state *S ) { memset( S->buf, 0, sizeof( S->buf ) ); S->buflen = 0; blake2s_init_param( &S->R, 0, 1 ); // Init root. for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. S->R.last_node = 1; S->S[PARALLELISM_DEGREE - 1].last_node = 1; } struct Blake2ThreadData { void Update(); blake2s_state *S; const byte *in; size_t inlen; }; void Blake2ThreadData::Update() { size_t inlen__ = inlen; const byte *in__ = ( const byte * )in; while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) { #ifdef USE_SSE // We gain 5% in i7 SSE mode by prefetching next data block. if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); #endif blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; } } #ifdef RAR_SMP THREAD_PROC(Blake2Thread) { Blake2ThreadData *td=(Blake2ThreadData *)Data; td->Update(); } #endif void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ) { size_t left = S->buflen; size_t fill = sizeof( S->buf ) - left; if( left && inlen >= fill ) { memcpy( S->buf + left, in, fill ); for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); in += fill; inlen -= fill; left = 0; } Blake2ThreadData btd_array[PARALLELISM_DEGREE]; #ifdef RAR_SMP uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads; if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here. ThreadNumber=4; #else uint ThreadNumber=1; #endif for (size_t id__=0;id__inlen = inlen; btd->in = in + id__ * BLAKE2S_BLOCKBYTES; btd->S = &S->S[id__]; #ifdef RAR_SMP if (ThreadNumber>1) S->ThPool->AddTask(Blake2Thread,(void*)btd); else btd->Update(); #else btd->Update(); #endif id__++; } #ifdef RAR_SMP if (S->ThPool!=NULL) // Can be NULL in -mt1 mode. S->ThPool->WaitDone(); #endif // RAR_SMP } in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; if( inlen > 0 ) memcpy( S->buf + left, in, (size_t)inlen ); S->buflen = left + (size_t)inlen; } void blake2sp_final( blake2sp_state *S, byte *digest ) { byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) { if( S->buflen > i * BLAKE2S_BLOCKBYTES ) { size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); } blake2s_final( &S->S[i], hash[i] ); } for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES ); blake2s_final( &S->R, digest ); } unrar/blake2s_sse.cpp000666 000000 000000 00000011773 13343205463 013274 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves extern const byte blake2s_sigma[10][16]; // Initialization vector. static __m128i blake2s_IV_0_3, blake2s_IV_4_7; #ifdef _WIN_64 // Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. static __m128i crotr8, crotr16; #endif static void blake2s_init_sse() { // We cannot initialize these 128 bit variables in place when declaring // them globally, because global scope initialization is performed before // our SSE check and it would make code incompatible with older non-SSE2 // CPUs. Also we cannot initialize them as static inside of function // using these variables, because SSE static initialization is not thread // safe: first thread starts initialization and sets "init done" flag even // if it is not done yet, second thread can attempt to access half-init // SSE data. So we moved init code here. blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); #ifdef _WIN_64 crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); #endif } #define LOAD(p) _mm_load_si128( (__m128i *)(p) ) #define STORE(p,r) _mm_store_si128((__m128i *)(p), r) #ifdef _WIN_32 // 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient // to not use _mm_shuffle_epi8 here. #define mm_rotr_epi32(r, c) ( \ _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) #else #define mm_rotr_epi32(r, c) ( \ c==8 ? _mm_shuffle_epi8(r,crotr8) \ : c==16 ? _mm_shuffle_epi8(r,crotr16) \ : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) #endif #define G1(row1,row2,row3,row4,buf) \ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ row4 = _mm_xor_si128( row4, row1 ); \ row4 = mm_rotr_epi32(row4, 16); \ row3 = _mm_add_epi32( row3, row4 ); \ row2 = _mm_xor_si128( row2, row3 ); \ row2 = mm_rotr_epi32(row2, 12); #define G2(row1,row2,row3,row4,buf) \ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ row4 = _mm_xor_si128( row4, row1 ); \ row4 = mm_rotr_epi32(row4, 8); \ row3 = _mm_add_epi32( row3, row4 ); \ row2 = _mm_xor_si128( row2, row3 ); \ row2 = mm_rotr_epi32(row2, 7); #define DIAGONALIZE(row1,row2,row3,row4) \ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) ); #define UNDIAGONALIZE(row1,row2,row3,row4) \ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); #ifdef _WIN_64 // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load // from stack operations, which are slower than this code. #define _mm_set_epi32(i3,i2,i1,i0) \ _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) #endif // Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode // and about the same in x64 mode in our test. Perhaps depends on compiler. // We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather // instructions here, but they did not show any speed gain on i7-6700K. #define SSE_ROUND(m,row,r) \ { \ __m128i buf; \ buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \ G1(row[0],row[1],row[2],row[3],buf); \ buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \ G2(row[0],row[1],row[2],row[3],buf); \ DIAGONALIZE(row[0],row[1],row[2],row[3]); \ buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \ G1(row[0],row[1],row[2],row[3],buf); \ buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \ G2(row[0],row[1],row[2],row[3],buf); \ UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \ } static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) { __m128i row[4]; __m128i ff0, ff1; const uint32 *m = ( uint32 * )block; row[0] = ff0 = LOAD( &S->h[0] ); row[1] = ff1 = LOAD( &S->h[4] ); row[2] = blake2s_IV_0_3; row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) ); SSE_ROUND( m, row, 0 ); SSE_ROUND( m, row, 1 ); SSE_ROUND( m, row, 2 ); SSE_ROUND( m, row, 3 ); SSE_ROUND( m, row, 4 ); SSE_ROUND( m, row, 5 ); SSE_ROUND( m, row, 6 ); SSE_ROUND( m, row, 7 ); SSE_ROUND( m, row, 8 ); SSE_ROUND( m, row, 9 ); STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) ); STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) ); return 0; } unrar/cmddata.cpp000666 000000 000000 00000105734 13343205463 012475 0ustar00000000 000000 #include "rar.hpp" CommandData::CommandData() { Init(); } void CommandData::Init() { RAROptions::Init(); *Command=0; *ArcName=0; FileLists=false; NoMoreSwitches=false; ListMode=RCLM_AUTO; BareOutput=false; FileArgs.Reset(); ExclArgs.Reset(); InclArgs.Reset(); StoreArgs.Reset(); ArcNames.Reset(); NextVolSizes.Reset(); } // Return the pointer to next position in the string and store dynamically // allocated command line parameter in Par. static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) { const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); if (NextCmd==NULL) return NULL; size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. *Par=(wchar *)malloc(ParSize*sizeof(wchar)); if (*Par==NULL) return NULL; return GetCmdParam(CmdLine,*Par,ParSize); } #if !defined(SFX_MODULE) void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) { *Command=0; NoMoreSwitches=false; #ifdef CUSTOM_CMDLINE_PARSER // In Windows we may prefer to implement our own command line parser // to avoid replacing \" by " in standard parser. Such replacing corrupts // destination paths like "dest path\" in extraction commands. // Also our own parser is Unicode compatible. const wchar *CmdLine=GetCommandLine(); wchar *Par; for (bool FirstParam=true;;FirstParam=false) { if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) break; if (!FirstParam) // First parameter is the executable name. if (Preprocess) PreprocessArg(Par); else ParseArg(Par); free(Par); } #else Array Arg; for (int I=1;I EnvStrW(strlen(EnvStr)+1); CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size()); ProcessSwitchesString(&EnvStrW[0]); } } #endif #if !defined(SFX_MODULE) // Preprocess those parameters, which must be processed before the rest of // command line. Return 'false' to stop further processing. void CommandData::PreprocessArg(const wchar *Arg) { if (IsSwitch(Arg[0]) && !NoMoreSwitches) { Arg++; if (Arg[0]=='-' && Arg[1]==0) // Switch "--". NoMoreSwitches=true; if (wcsicomp(Arg,L"cfg-")==0) ConfigDisabled=true; if (wcsnicomp(Arg,L"ilog",4)==0) { // Ensure that correct log file name is already set // if we need to report an error when processing the command line. ProcessSwitch(Arg); InitLogOptions(LogName,ErrlogCharset); } if (wcsnicomp(Arg,L"sc",2)==0) { // Process -sc before reading any file lists. ProcessSwitch(Arg); if (*LogName!=0) InitLogOptions(LogName,ErrlogCharset); } } else if (*Command==0) wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini. } #endif #if !defined(SFX_MODULE) void CommandData::ReadConfig() { StringList List; if (ReadTextFile(DefConfigName,&List,true)) { wchar *Str; while ((Str=List.GetString())!=NULL) { while (IsSpace(*Str)) Str++; if (wcsnicomp(Str,L"switches=",9)==0) ProcessSwitchesString(Str+9); if (*Command!=0) { wchar Cmd[16]; wcsncpyz(Cmd,Command,ASIZE(Cmd)); wchar C0=toupperw(Cmd[0]); wchar C1=toupperw(Cmd[1]); if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V') Cmd[1]=0; if (C0=='R' && (C1=='R' || C1=='V')) Cmd[2]=0; wchar SwName[16+ASIZE(Cmd)]; swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd); size_t Length=wcslen(SwName); if (wcsnicomp(Str,SwName,Length)==0) ProcessSwitchesString(Str+Length); } } } } #endif #if !defined(SFX_MODULE) void CommandData::ProcessSwitchesString(const wchar *Str) { wchar *Par; while ((Str=AllocCmdParam(Str,&Par))!=NULL) { if (IsSwitch(*Par)) ProcessSwitch(Par+1); free(Par); } } #endif #if !defined(SFX_MODULE) void CommandData::ProcessSwitch(const wchar *Switch) { switch(toupperw(Switch[0])) { case '@': ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; break; case 'A': switch(toupperw(Switch[1])) { case 'C': ClearArc=true; break; case 'D': AppendArcNameToPath=true; break; #ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else { GenerateArcName=true; wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); } break; #endif case 'I': IgnoreGeneralAttr=true; break; case 'N': // Reserved for archive name. break; case 'O': AddArcOnly=true; break; case 'P': wcscpy(ArcPath,Switch+2); break; case 'S': SyncFiles=true; break; default: BadSwitch(Switch); break; } break; case 'C': if (Switch[2]==0) switch(toupperw(Switch[1])) { case '-': DisableComment=true; break; case 'U': ConvertNames=NAMES_UPPERCASE; break; case 'L': ConvertNames=NAMES_LOWERCASE; break; } break; case 'D': if (Switch[2]==0) switch(toupperw(Switch[1])) { case 'S': DisableSortSolid=true; break; case 'H': OpenShared=true; break; case 'F': DeleteFiles=true; break; } break; case 'E': switch(toupperw(Switch[1])) { case 'P': switch(Switch[2]) { case 0: ExclPath=EXCL_SKIPWHOLEPATH; break; case '1': ExclPath=EXCL_BASEPATH; break; case '2': ExclPath=EXCL_SAVEFULLPATH; break; case '3': ExclPath=EXCL_ABSPATH; break; } break; default: if (Switch[1]=='+') { InclFileAttr|=GetExclAttr(Switch+2); InclAttrSet=true; } else ExclFileAttr|=GetExclAttr(Switch+1); break; } break; case 'F': if (Switch[1]==0) FreshFiles=true; else BadSwitch(Switch); break; case 'H': switch (toupperw(Switch[1])) { case 'P': EncryptHeaders=true; if (Switch[2]!=0) { Password.Set(Switch+2); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } else if (!Password.IsSet()) { uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); eprintf(L"\n"); } break; default : BadSwitch(Switch); break; } break; case 'I': if (wcsnicomp(Switch+1,L"LOG",3)==0) { wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); break; } if (wcsicomp(Switch+1,L"SND")==0) { Sound=true; break; } if (wcsicomp(Switch+1,L"ERR")==0) { MsgStream=MSG_STDERR; // Set it immediately when parsing the command line, so it also // affects messages issued while parsing the command line. SetConsoleMsgStream(MSG_STDERR); break; } if (wcsnicomp(Switch+1,L"EML",3)==0) { wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); break; } if (wcsicomp(Switch+1,L"M")==0) { MoreInfo=true; break; } if (wcsicomp(Switch+1,L"NUL")==0) { MsgStream=MSG_NULL; SetConsoleMsgStream(MSG_NULL); break; } if (toupperw(Switch[1])=='D') { for (uint I=2;Switch[I]!=0;I++) switch(toupperw(Switch[I])) { case 'Q': MsgStream=MSG_ERRONLY; SetConsoleMsgStream(MSG_ERRONLY); break; case 'C': DisableCopyright=true; break; case 'D': DisableDone=true; break; case 'P': DisablePercentage=true; break; } break; } if (wcsnicomp(Switch+1,L"OFF",3)==0) { switch(Switch[4]) { case 0: case '1': Shutdown=POWERMODE_OFF; break; case '2': Shutdown=POWERMODE_HIBERNATE; break; case '3': Shutdown=POWERMODE_SLEEP; break; case '4': Shutdown=POWERMODE_RESTART; break; } break; } if (wcsicomp(Switch+1,L"VER")==0) { PrintVersion=true; break; } break; case 'K': switch(toupperw(Switch[1])) { case 'B': KeepBroken=true; break; case 0: Lock=true; break; } break; case 'M': switch(toupperw(Switch[1])) { case 'C': { const wchar *Str=Switch+2; if (*Str=='-') for (uint I=0;IMaxPoolThreads || Threads<1) BadSwitch(Switch); else { } break; #endif default: Method=Switch[1]-'0'; if (Method>5 || Method<0) BadSwitch(Switch); break; } break; case 'N': case 'X': if (Switch[1]!=0) { StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; if (Switch[1]=='@' && !IsWildcard(Switch)) ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true); else Args->AddString(Switch+1); } break; case 'O': switch(toupperw(Switch[1])) { case '+': Overwrite=OVERWRITE_ALL; break; case '-': Overwrite=OVERWRITE_NONE; break; case 0: Overwrite=OVERWRITE_FORCE_ASK; break; #ifdef _WIN_ALL case 'C': SetCompressedAttr=true; break; #endif case 'H': SaveHardLinks=true; break; #ifdef SAVE_LINKS case 'L': SaveSymLinks=true; if (toupperw(Switch[2])=='A') AbsoluteLinks=true; break; #endif #ifdef _WIN_ALL case 'N': if (toupperw(Switch[2])=='I') AllowIncompatNames=true; break; #endif case 'R': Overwrite=OVERWRITE_AUTORENAME; break; #ifdef _WIN_ALL case 'S': SaveStreams=true; break; #endif case 'W': ProcessOwners=true; break; default : BadSwitch(Switch); break; } break; case 'P': if (Switch[1]==0) { uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); eprintf(L"\n"); } else { Password.Set(Switch+1); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } break; #ifndef SFX_MODULE case 'Q': if (toupperw(Switch[1])=='O') switch(toupperw(Switch[2])) { case 0: QOpenMode=QOPEN_AUTO; break; case '-': QOpenMode=QOPEN_NONE; break; case '+': QOpenMode=QOPEN_ALWAYS; break; default: BadSwitch(Switch); break; } else BadSwitch(Switch); break; #endif case 'R': switch(toupperw(Switch[1])) { case 0: Recurse=RECURSE_ALWAYS; break; case '-': Recurse=RECURSE_DISABLE; break; case '0': Recurse=RECURSE_WILDCARDS; break; case 'I': { Priority=atoiw(Switch+2); if (Priority<0 || Priority>15) BadSwitch(Switch); const wchar *ChPtr=wcschr(Switch+2,':'); if (ChPtr!=NULL) { SleepTime=atoiw(ChPtr+1); if (SleepTime>1000) BadSwitch(Switch); InitSystemOptions(SleepTime); } SetPriority(Priority); } break; } break; case 'S': if (IsDigit(Switch[1])) { Solid|=SOLID_COUNT; SolidCount=atoiw(&Switch[1]); } else switch(toupperw(Switch[1])) { case 0: Solid|=SOLID_NORMAL; break; case '-': Solid=SOLID_NONE; break; case 'E': Solid|=SOLID_FILEEXT; break; case 'V': Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT; break; case 'D': Solid|=SOLID_VOLUME_DEPENDENT; break; case 'L': if (IsDigit(Switch[2])) FileSizeLess=atoilw(Switch+2); break; case 'M': if (IsDigit(Switch[2])) FileSizeMore=atoilw(Switch+2); break; case 'C': { bool AlreadyBad=false; // Avoid reporting "bad switch" several times. RAR_CHARSET rch=RCH_DEFAULT; switch(toupperw(Switch[2])) { case 'A': rch=RCH_ANSI; break; case 'O': rch=RCH_OEM; break; case 'U': rch=RCH_UNICODE; break; case 'F': rch=RCH_UTF8; break; default : BadSwitch(Switch); AlreadyBad=true; break; }; if (!AlreadyBad) if (Switch[3]==0) CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; else for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) switch(toupperw(Switch[I])) { case 'C': CommentCharset=rch; break; case 'L': FilelistCharset=rch; break; case 'R': RedirectCharset=rch; break; default: BadSwitch(Switch); AlreadyBad=true; break; } // Set it immediately when parsing the command line, so it also // affects messages issued while parsing the command line. SetConsoleRedirectCharset(RedirectCharset); } break; } break; case 'T': switch(toupperw(Switch[1])) { case 'K': ArcTime=ARCTIME_KEEP; break; case 'L': ArcTime=ARCTIME_LATEST; break; case 'O': FileTimeBefore.SetAgeText(Switch+2); break; case 'N': FileTimeAfter.SetAgeText(Switch+2); break; case 'B': FileTimeBefore.SetIsoText(Switch+2); break; case 'A': FileTimeAfter.SetIsoText(Switch+2); break; case 'S': { EXTTIME_MODE Mode=EXTTIME_HIGH3; bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; if (CommonMode) Mode=(EXTTIME_MODE)(Switch[2]-'0'); if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. Mode=EXTTIME_HIGH3; if (Switch[2]=='-') Mode=EXTTIME_NONE; if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) xmtime=xctime=xatime=Mode; else { if (Switch[3]>='0' && Switch[3]<='4') Mode=(EXTTIME_MODE)(Switch[3]-'0'); if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. Mode=EXTTIME_HIGH3; if (Switch[3]=='-') Mode=EXTTIME_NONE; switch(toupperw(Switch[2])) { case 'M': xmtime=Mode; break; case 'C': xctime=Mode; break; case 'A': xatime=Mode; break; } } } break; case '-': Test=false; break; case 0: Test=true; break; default: BadSwitch(Switch); break; } break; case 'U': if (Switch[1]==0) UpdateFiles=true; else BadSwitch(Switch); break; case 'V': switch(toupperw(Switch[1])) { case 'P': VolumePause=true; break; case 'E': if (toupperw(Switch[2])=='R') VersionControl=atoiw(Switch+3)+1; break; case '-': VolSize=0; break; default: VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. break; } break; case 'W': wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); AddEndSlash(TempPath,ASIZE(TempPath)); break; case 'Y': AllYes=true; break; case 'Z': if (Switch[1]==0) { // If comment file is not specified, we read data from stdin. wcscpy(CommentFile,L"stdin"); } else wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); break; case '?' : OutHelp(RARX_SUCCESS); break; default : BadSwitch(Switch); break; } } #endif #if !defined(SFX_MODULE) void CommandData::BadSwitch(const wchar *Switch) { mprintf(St(MUnknownOption),Switch); ErrHandler.Exit(RARX_USERERROR); } #endif void CommandData::OutTitle() { if (BareOutput || DisableCopyright) return; #if defined(__GNUC__) && defined(SFX_MODULE) mprintf(St(MCopyrightS)); #else #ifndef SILENT static bool TitleShown=false; if (TitleShown) return; TitleShown=true; wchar Version[80]; if (RARVER_BETA!=0) swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); else swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); #if defined(_WIN_32) || defined(_WIN_64) wcsncatz(Version,L" ",ASIZE(Version)); #endif #ifdef _WIN_32 wcsncatz(Version,St(Mx86),ASIZE(Version)); #endif #ifdef _WIN_64 wcsncatz(Version,St(Mx64),ASIZE(Version)); #endif if (PrintVersion) { mprintf(L"%s",Version); exit(0); } mprintf(St(MUCopyright),Version,RARVER_YEAR); #endif #endif } inline bool CmpMSGID(MSGID i1,MSGID i2) { #ifdef MSGID_INT return i1==i2; #else // If MSGID is const char*, we cannot compare pointers only. // Pointers to different instances of same string can differ, // so we need to compare complete strings. return wcscmp(i1,i2)==0; #endif } void CommandData::OutHelp(RAR_EXIT ExitCode) { #if !defined(SILENT) OutTitle(); static MSGID Help[]={ #ifdef SFX_MODULE // Console SFX switches definition. MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV #else // UnRAR switches definition. MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM, MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU, MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal, MCHelpSwY #endif }; for (uint I=0;IRewind(); while (Args->GetString(CurMask,ASIZE(CurMask))) { wchar *LastMaskChar=PointToLastChar(CurMask); bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. if (Dir) { // CheckName is a directory. if (DirMask) { // We process the directory and have the directory exclusion mask. // So let's convert "mask\" to "mask" and process it normally. *LastMaskChar=0; } else { // REMOVED, we want -npath\* to match empty folders too. // If mask has wildcards in name part and does not have the trailing // '\' character, we cannot use it for directories. // if (IsWildcard(PointToName(CurMask))) // continue; } } else { // If we process a file inside of directory excluded by "dirmask\". // we want to exclude such file too. So we convert "dirmask\" to // "dirmask\*". It is important for operations other than archiving // with -x. When archiving with -x, directory matched by "dirmask\" // is excluded from further scanning. if (DirMask) wcsncatz(CurMask,L"*",ASIZE(CurMask)); } #ifndef SFX_MODULE if (CheckFullPath && IsFullPath(CurMask)) { // We do not need to do the special "*\" processing here, because // unlike the "else" part of this "if", now we convert names to full // format, so they all include the path, which is matched by "*\" // correctly. Moreover, removing "*\" from mask would break // the comparison, because now all names have the path. if (*FullName==0) ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); if (CmpName(CurMask,FullName,MatchMode)) return true; } else #endif { wchar NewName[NM+2],*CurName=Name; // Important to convert before "*\" check below, so masks like // d:*\something are processed properly. wchar *CmpMask=ConvertPath(CurMask,NULL); if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) { // We want "*\name" to match 'name' not only in subdirectories, // but also in the current directory. We convert the name // from 'name' to '.\name' to be matched by "*\" part even if it is // in current directory. NewName[0]='.'; NewName[1]=CPATHDIVIDER; wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); CurName=NewName; } if (CmpName(CmpMask,CurName,MatchMode)) return true; } } return false; } #ifndef SFX_MODULE // Now this function performs only one task and only in Windows version: // it skips symlinks to directories if -e1024 switch is specified. // Symlinks are skipped in ScanTree class, so their entire contents // is skipped too. Without this function we would check the attribute // only directly before archiving, so we would skip the symlink record, // but not the contents of symlinked directory. bool CommandData::ExclDirByAttr(uint FileAttr) { #ifdef _WIN_ALL if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) return true; #endif return false; } #endif #ifndef SFX_MODULE // Return 'true' if we need to exclude the file from processing. bool CommandData::TimeCheck(RarTime &ft) { if (FileTimeBefore.IsSet() && ft>=FileTimeBefore) return true; if (FileTimeAfter.IsSet() && ft<=FileTimeAfter) return true; return false; } #endif #ifndef SFX_MODULE // Return 'true' if we need to exclude the file from processing. bool CommandData::SizeCheck(int64 Size) { if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) return(true); if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) return(true); return(false); } #endif int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, wchar *MatchedArg,uint MatchedArgSize) { if (MatchedArg!=NULL && MatchedArgSize>0) *MatchedArg=0; // if (wcslen(FileHead.FileName)>=NM) // return 0; bool Dir=FileHead.Dir; if (ExclCheck(FileHead.FileName,Dir,false,true)) return 0; #ifndef SFX_MODULE if (TimeCheck(FileHead.mtime)) return 0; if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0) return 0; if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; #endif wchar *ArgName; FileArgs.Rewind(); for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) if (CmpName(ArgName,FileHead.FileName,MatchType)) { if (ExactMatch!=NULL) *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; if (MatchedArg!=NULL) wcsncpyz(MatchedArg,ArgName,MatchedArgSize); return StringCount; } return 0; } void CommandData::ProcessCommand() { #ifndef SFX_MODULE const wchar *SingleCharCommands=L"FUADPXETK"; if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. const wchar *ArcExt=GetExt(ArcName); #ifdef _UNIX if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #else if (ArcExt==NULL) wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #endif // Treat arcname.part1 as arcname.part1.rar. if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) && !FileExist(ArcName)) { wchar Name[NM]; wcsncpyz(Name,ArcName,ASIZE(Name)); wcsncatz(Name,L".rar",ASIZE(Name)); if (FileExist(Name)) wcsncpyz(ArcName,Name,ASIZE(ArcName)); } if (wcschr(L"AFUMD",*Command)==NULL) { if (GenerateArcName) GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false); StringList ArcMasks; ArcMasks.AddString(ArcName); ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); FindData FindData; while (Scan.GetNext(&FindData)==SCAN_SUCCESS) AddArcName(FindData.Name); } else AddArcName(ArcName); #endif switch(Command[0]) { case 'P': case 'X': case 'E': case 'T': case 'I': { CmdExtract Extract(this); Extract.DoExtract(); } break; #ifndef SILENT case 'V': case 'L': ListArchive(this); break; default: OutHelp(RARX_USERERROR); #endif } if (!BareOutput) mprintf(L"\n"); } void CommandData::AddArcName(const wchar *Name) { ArcNames.AddString(Name); } bool CommandData::GetArcName(wchar *Name,int MaxSize) { return ArcNames.GetString(Name,MaxSize); } bool CommandData::IsSwitch(int Ch) { #if defined(_WIN_ALL) || defined(_EMX) return Ch=='-' || Ch=='/'; #else return Ch=='-'; #endif } #ifndef SFX_MODULE uint CommandData::GetExclAttr(const wchar *Str) { if (IsDigit(*Str)) return wcstol(Str,NULL,0); uint Attr=0; while (*Str!=0) { switch(toupperw(*Str)) { #ifdef _UNIX case 'D': Attr|=S_IFDIR; break; case 'V': Attr|=S_IFCHR; break; #elif defined(_WIN_ALL) || defined(_EMX) case 'R': Attr|=0x1; break; case 'H': Attr|=0x2; break; case 'S': Attr|=0x4; break; case 'D': Attr|=0x10; break; case 'A': Attr|=0x20; break; #endif } Str++; } return Attr; } #endif #ifndef SFX_MODULE bool CommandData::CheckWinSize() { // Define 0x100000000 as macro to avoid troubles with older compilers. const uint64 MaxDictSize=INT32TO64(1,0); // Limit the dictionary size to 4 GB. for (uint64 I=0x10000;I<=MaxDictSize;I*=2) if (WinSize==I) return true; WinSize=0x400000; return false; } #endif #ifndef SFX_MODULE void CommandData::ReportWrongSwitches(RARFORMAT Format) { if (Format==RARFMT15) { if (HashType!=HASH_CRC32) uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4); #ifdef _WIN_ALL if (SaveSymLinks) uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4); #endif if (SaveHardLinks) uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4); #ifdef _WIN_ALL // Do not report a wrong dictionary size here, because we are not sure // yet about archive format. We can switch to RAR5 mode later // if we update RAR5 archive. #endif if (QOpenMode!=QOPEN_AUTO) uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4); } if (Format==RARFMT50) { } } #endif unrar/coder.cpp000666 000000 000000 00000002360 13343205463 012163 0ustar00000000 000000 inline unsigned int RangeCoder::GetChar() { return(UnpackRead->GetChar()); } void RangeCoder::InitDecoder(Unpack *UnpackRead) { RangeCoder::UnpackRead=UnpackRead; low=code=0; range=uint(-1); for (int i=0;i < 4;i++) code=(code << 8) | GetChar(); } // (int) cast before "low" added only to suppress compiler warnings. #define ARI_DEC_NORMALIZE(code,low,range,read) \ { \ while ((low^(low+range))GetChar(); \ range <<= 8; \ low <<= 8; \ } \ } inline int RangeCoder::GetCurrentCount() { return (code-low)/(range /= SubRange.scale); } inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT) { return (code-low)/(range >>= SHIFT); } inline void RangeCoder::Decode() { low += range*SubRange.LowCount; range *= SubRange.HighCount-SubRange.LowCount; } unrar/consio.cpp000666 000000 000000 00000022703 13343205463 012364 0ustar00000000 000000 #include "rar.hpp" #include "log.cpp" static MESSAGE_TYPE MsgStream=MSG_STDOUT; static RAR_CHARSET RedirectCharset=RCH_DEFAULT; const int MaxMsgSize=2*NM+2048; static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; #ifdef _WIN_ALL static bool IsRedirected(DWORD nStdHandle) { HANDLE hStd=GetStdHandle(nStdHandle); DWORD Mode; return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0; } #endif void InitConsole() { #ifdef _WIN_ALL // We want messages like file names or progress percent to be printed // immediately. Use only in Windows, in Unix they can cause wprintf %ls // to fail with non-English strings. setbuf(stdout,NULL); setbuf(stderr,NULL); // Detect if output is redirected and set output mode properly. // We do not want to send Unicode output to files and especially to pipes // like '|more', which cannot handle them correctly in Windows. // In Unix console output is UTF-8 and it is handled correctly // when redirecting, so no need to perform any adjustments. StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE); StderrRedirected=IsRedirected(STD_ERROR_HANDLE); StdinRedirected=IsRedirected(STD_INPUT_HANDLE); #ifdef _MSC_VER if (!StdoutRedirected) _setmode(_fileno(stdout), _O_U16TEXT); if (!StderrRedirected) _setmode(_fileno(stderr), _O_U16TEXT); #endif #elif defined(_UNIX) StdoutRedirected=!isatty(fileno(stdout)); StderrRedirected=!isatty(fileno(stderr)); StdinRedirected=!isatty(fileno(stdin)); #endif } void SetConsoleMsgStream(MESSAGE_TYPE MsgStream) { ::MsgStream=MsgStream; } void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset) { ::RedirectCharset=RedirectCharset; } #ifndef SILENT static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { // This buffer is for format string only, not for entire output, // so it can be short enough. wchar fmtw[1024]; PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); #ifdef _WIN_ALL safebuf wchar Msg[MaxMsgSize]; if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) { HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); vswprintf(Msg,ASIZE(Msg),fmtw,arglist); DWORD Written; if (RedirectCharset==RCH_UNICODE) WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL); else { // Avoid Unicode for redirect in Windows, it does not work with pipes. safebuf char MsgA[MaxMsgSize]; if (RedirectCharset==RCH_UTF8) WideToUtf(Msg,MsgA,ASIZE(MsgA)); else WideToChar(Msg,MsgA,ASIZE(MsgA)); if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding. // We already converted \n to \r\n above, so we use WriteFile instead // of C library to avoid unnecessary additional conversion. WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL); } return; } // MSVC2008 vfwprintf writes every character to console separately // and it is too slow. We use direct WriteConsole call instead. vswprintf(Msg,ASIZE(Msg),fmtw,arglist); HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); DWORD Written; WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL); #else vfwprintf(dest,fmtw,arglist); // We do not use setbuf(NULL) in Unix (see comments in InitConsole). fflush(dest); #endif } void mprintf(const wchar *fmt,...) { if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) return; fflush(stderr); // Ensure proper message order. va_list arglist; va_start(arglist,fmt); FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout; cvt_wprintf(dest,fmt,arglist); va_end(arglist); } #endif #ifndef SILENT void eprintf(const wchar *fmt,...) { if (MsgStream==MSG_NULL) return; fflush(stdout); // Ensure proper message order. va_list arglist; va_start(arglist,fmt); cvt_wprintf(stderr,fmt,arglist); va_end(arglist); } #endif #ifndef SILENT static void GetPasswordText(wchar *Str,uint MaxLength) { if (MaxLength==0) return; if (StdinRedirected) getwstr(Str,MaxLength); // Read from pipe or redirected file. else { #ifdef _WIN_ALL HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE); DWORD ConInMode,ConOutMode; DWORD Read=0; GetConsoleMode(hConIn,&ConInMode); GetConsoleMode(hConOut,&ConOutMode); SetConsoleMode(hConIn,ENABLE_LINE_INPUT); SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT); ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); Str[Read]=0; SetConsoleMode(hConIn,ConInMode); SetConsoleMode(hConOut,ConOutMode); #else char StrA[MAXPASSWORD]; #if defined(_EMX) || defined (__VMS) fgets(StrA,ASIZE(StrA)-1,stdin); #elif defined(__sun) strncpyz(StrA,getpassphrase(""),ASIZE(StrA)); #else strncpyz(StrA,getpass(""),ASIZE(StrA)); #endif CharToWide(StrA,Str,MaxLength); cleandata(StrA,sizeof(StrA)); #endif } Str[MaxLength-1]=0; RemoveLF(Str); } #endif #ifndef SILENT bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) { if (!StdinRedirected) uiAlarm(UIALARM_QUESTION); while (true) { if (!StdinRedirected) if (Type==UIPASSWORD_GLOBAL) eprintf(L"\n%s: ",St(MAskPsw)); else eprintf(St(MAskPswFor),FileName); wchar PlainPsw[MAXPASSWORD]; GetPasswordText(PlainPsw,ASIZE(PlainPsw)); if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) return false; if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) { eprintf(St(MReAskPsw)); wchar CmpStr[MAXPASSWORD]; GetPasswordText(CmpStr,ASIZE(CmpStr)); if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0) { eprintf(St(MNotMatchPsw)); cleandata(PlainPsw,sizeof(PlainPsw)); cleandata(CmpStr,sizeof(CmpStr)); continue; } cleandata(CmpStr,sizeof(CmpStr)); } Password->Set(PlainPsw); cleandata(PlainPsw,sizeof(PlainPsw)); break; } return true; } #endif #ifndef SILENT bool getwstr(wchar *str,size_t n) { // Print buffered prompt title function before waiting for input. fflush(stderr); *str=0; #if defined(_WIN_ALL) // fgetws does not work well with non-English text in Windows, // so we do not use it. if (StdinRedirected) // ReadConsole does not work if redirected. { // fgets does not work well with pipes in Windows in our test. // Let's use files. Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. File SrcFile; SrcFile.SetHandleType(FILE_HANDLESTD); int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); if (ReadSize<=0) { // Looks like stdin is a null device. We can enter to infinite loop // calling Ask(), so let's better exit. ErrHandler.Exit(RARX_USERBREAK); } StrA[ReadSize]=0; CharToWide(&StrA[0],str,n); cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords. } else { DWORD ReadSize=0; if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0) return false; str[ReadSize]=0; } #else if (fgetws(str,n,stdin)==NULL) ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop. #endif RemoveLF(str); return true; } #endif #ifndef SILENT // We allow this function to return 0 in case of invalid input, // because it might be convenient to press Enter to some not dangerous // prompts like "insert disk with next volume". We should call this function // again in case of 0 in dangerous prompt such as overwriting file. int Ask(const wchar *AskStr) { uiAlarm(UIALARM_QUESTION); const int MaxItems=10; wchar Item[MaxItems][40]; int ItemKeyPos[MaxItems],NumItems=0; for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_')) { wchar *CurItem=Item[NumItems]; wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); wchar *EndItem=wcschr(CurItem,'_'); if (EndItem!=NULL) *EndItem=0; int KeyPos=0,CurKey; while ((CurKey=CurItem[KeyPos])!=0) { bool Found=false; for (int I=0;I4 ? L"\n":L" "):L", "); int KeyPos=ItemKeyPos[I]; for (int J=0;J[{key};"{string}"p used to redefine // a keyboard key on some terminals. if (Data[J]=='\"') return true; if (!IsDigit(Data[J]) && Data[J]!=';') break; } return false; } void OutComment(const wchar *Comment,size_t Size) { if (IsCommentUnsafe(Comment,Size)) return; const size_t MaxOutSize=0x400; for (size_t I=0;I>1)^0xEDB88320 : (C>>1); CRCTab[I]=C; } } static void InitTables() { InitCRC32(crc_tables[0]); for (uint I=0;I<256;I++) // Build additional lookup tables. { uint C=crc_tables[0][I]; for (uint J=1;J<8;J++) { C=crc_tables[0][(byte)C]^(C>>8); crc_tables[J][I]=C; } } } struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; uint CRC32(uint StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; // Align Data to 8 for better performance. for (;Size>0 && ((size_t)Data & 7);Size--,Data++) StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); for (;Size>=8;Size-=8,Data+=8) { #ifdef BIG_ENDIAN StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); #else StartCRC ^= *(uint32 *) Data; uint NextData = *(uint32 *) (Data+4); #endif StartCRC = crc_tables[7][(byte) StartCRC ] ^ crc_tables[6][(byte)(StartCRC >> 8) ] ^ crc_tables[5][(byte)(StartCRC >> 16)] ^ crc_tables[4][(byte)(StartCRC >> 24)] ^ crc_tables[3][(byte) NextData ] ^ crc_tables[2][(byte)(NextData >>8 ) ] ^ crc_tables[1][(byte)(NextData >> 16)] ^ crc_tables[0][(byte)(NextData >> 24)]; } for (;Size>0;Size--,Data++) // Process left data. StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); return StartCRC; } #ifndef SFX_MODULE // For RAR 1.4 archives in case somebody still has them. ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; for (size_t I=0;I>15))&0xffff; } return StartCRC; } #endif unrar/crypt.cpp000666 000000 000000 00000005403 13343205463 012231 0ustar00000000 000000 #include "rar.hpp" #ifndef SFX_MODULE #include "crypt1.cpp" #include "crypt2.cpp" #endif #include "crypt3.cpp" #include "crypt5.cpp" CryptData::CryptData() { Method=CRYPT_NONE; memset(KDF3Cache,0,sizeof(KDF3Cache)); memset(KDF5Cache,0,sizeof(KDF5Cache)); KDF3CachePos=0; KDF5CachePos=0; memset(CRCTab,0,sizeof(CRCTab)); } CryptData::~CryptData() { cleandata(KDF3Cache,sizeof(KDF3Cache)); cleandata(KDF5Cache,sizeof(KDF5Cache)); } void CryptData::DecryptBlock(byte *Buf,size_t Size) { switch(Method) { #ifndef SFX_MODULE case CRYPT_RAR13: Decrypt13(Buf,Size); break; case CRYPT_RAR15: Crypt15(Buf,Size); break; case CRYPT_RAR20: for (size_t I=0;IIsSet() || Method==CRYPT_NONE) return false; CryptData::Method=Method; wchar PwdW[MAXPASSWORD]; Password->Get(PwdW,ASIZE(PwdW)); char PwdA[MAXPASSWORD]; WideToChar(PwdW,PwdA,ASIZE(PwdA)); switch(Method) { #ifndef SFX_MODULE case CRYPT_RAR13: SetKey13(PwdA); break; case CRYPT_RAR15: SetKey15(PwdA); break; case CRYPT_RAR20: SetKey20(PwdA); break; #endif case CRYPT_RAR30: SetKey30(Encrypt,Password,PwdW,Salt); break; case CRYPT_RAR50: SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); break; } cleandata(PwdA,sizeof(PwdA)); cleandata(PwdW,sizeof(PwdW)); return true; } // Use the current system time to additionally randomize data. static void TimeRandomize(byte *RndBuf,size_t BufSize) { static uint Count=0; RarTime CurTime; CurTime.SetCurrentTime(); uint64 Random=CurTime.GetWin()+clock(); for (size_t I=0;I> ( (I & 7) * 8 )); RndBuf[I]=byte( (RndByte ^ I) + Count++); } } // Fill buffer with random data. void GetRnd(byte *RndBuf,size_t BufSize) { bool Success=false; #if defined(_WIN_ALL) HCRYPTPROV hProvider = 0; if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; CryptReleaseContext(hProvider, 0); } #elif defined(_UNIX) FILE *rndf = fopen("/dev/urandom", "r"); if (rndf!=NULL) { Success=fread(RndBuf, BufSize, 1, rndf) == BufSize; fclose(rndf); } #endif // We use this code only as the last resort if code above failed. if (!Success) TimeRandomize(RndBuf,BufSize); } unrar/crypt1.cpp000666 000000 000000 00000002625 13343205463 012315 0ustar00000000 000000 extern uint CRCTab[256]; void CryptData::SetKey13(const char *Password) { Key13[0]=Key13[1]=Key13[2]=0; for (size_t I=0;Password[I]!=0;I++) { byte P=Password[I]; Key13[0]+=P; Key13[1]^=P; Key13[2]+=P; Key13[2]=(byte)rotls(Key13[2],1,8); } } void CryptData::SetKey15(const char *Password) { InitCRC32(CRCTab); uint PswCRC=CRC32(0xffffffff,Password,strlen(Password)); Key15[0]=PswCRC&0xffff; Key15[1]=(PswCRC>>16)&0xffff; Key15[2]=Key15[3]=0; for (size_t I=0;Password[I]!=0;I++) { byte P=Password[I]; Key15[2]^=P^CRCTab[P]; Key15[3]+=P+(CRCTab[P]>>16); } } void CryptData::SetAV15Encryption() { InitCRC32(CRCTab); Method=CRYPT_RAR15; Key15[0]=0x4765; Key15[1]=0x9021; Key15[2]=0x7382; Key15[3]=0x5215; } void CryptData::SetCmt13Encryption() { Method=CRYPT_RAR13; Key13[0]=0; Key13[1]=7; Key13[2]=77; } void CryptData::Decrypt13(byte *Data,size_t Count) { while (Count--) { Key13[1]+=Key13[2]; Key13[0]+=Key13[1]; *Data-=Key13[0]; Data++; } } void CryptData::Crypt15(byte *Data,size_t Count) { while (Count--) { Key15[0]+=0x1234; Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; Key15[0]^=Key15[2]; Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; Key15[3]=rotrs(Key15[3]&0xffff,1,16); Key15[0]^=Key15[3]; *Data^=(byte)(Key15[0]>>8); Data++; } } unrar/crypt2.cpp000666 000000 000000 00000007175 13343205463 012323 0ustar00000000 000000 #define NROUNDS 32 #define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \ ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \ ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \ ((uint)SubstTable20[(int)(t>>24)&255]<<24) ) static byte InitSubstTable20[256]={ 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 }; void CryptData::SetKey20(const char *Password) { InitCRC32(CRCTab); char Psw[MAXPASSWORD]; strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below. size_t PswLength=strlen(Psw); Key20[0]=0xD3A3B879L; Key20[1]=0x3F6D12F7L; Key20[2]=0x7515A235L; Key20[3]=0xA4E7F123L; memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20)); for (uint J=0;J<256;J++) for (size_t I=0;I=0;I--) { T=((C+rotls(D,11,32))^Key20[I&3]); TA=A^substLong(T); T=((D^rotls(C,17,32))+Key20[I&3]); TB=B^substLong(T); A=C; B=D; C=TA; D=TB; } RawPut4(C^Key20[0],Buf+0); RawPut4(D^Key20[1],Buf+4); RawPut4(A^Key20[2],Buf+8); RawPut4(B^Key20[3],Buf+12); UpdKeys20(InBuf); } void CryptData::UpdKeys20(byte *Buf) { for (int I=0;I<16;I+=4) { Key20[0]^=CRCTab[Buf[I]]; Key20[1]^=CRCTab[Buf[I+1]]; Key20[2]^=CRCTab[Buf[I+2]]; Key20[3]^=CRCTab[Buf[I+3]]; } } void CryptData::Swap20(byte *Ch1,byte *Ch2) { byte Ch=*Ch1; *Ch1=*Ch2; *Ch2=Ch; } unrar/crypt3.cpp000666 000000 000000 00000004100 13343205463 012305 0ustar00000000 000000 void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt) { byte AESKey[16],AESInit[16]; bool Cached=false; for (uint I=0;I>8); PswNum[2]=(byte)(I>>16); sha1_process(&c, PswNum, 3); if (I%(HashRounds/16)==0) { sha1_context tempc=c; uint32 digest[5]; sha1_done( &tempc, digest ); AESInit[I/(HashRounds/16)]=(byte)digest[4]; } } uint32 digest[5]; sha1_done( &c, digest ); for (int I=0;I<4;I++) for (int J=0;J<4;J++) AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); KDF3Cache[KDF3CachePos].Pwd=*Password; if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true) memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30); memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey)); SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false); memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit)); KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache); cleandata(RawPsw,sizeof(RawPsw)); } rin.Init(Encrypt, AESKey, 128, AESInit); cleandata(AESKey,sizeof(AESKey)); cleandata(AESInit,sizeof(AESInit)); } unrar/crypt5.cpp000666 000000 000000 00000017525 13343205463 012326 0ustar00000000 000000 static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, size_t DataLength,byte *ResDigest, sha256_context *ICtxOpt,bool *SetIOpt, sha256_context *RCtxOpt,bool *SetROpt) { const size_t Sha256BlockSize=64; // As defined in RFC 4868. byte KeyHash[SHA256_DIGEST_SIZE]; if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash. { sha256_context KCtx; sha256_init(&KCtx); sha256_process(&KCtx, Key, KeyLength); sha256_done(&KCtx, KeyHash); Key = KeyHash; KeyLength = SHA256_DIGEST_SIZE; } byte KeyBuf[Sha256BlockSize]; // Store the padded key here. sha256_context ICtx; if (ICtxOpt!=NULL && *SetIOpt) ICtx=*ICtxOpt; // Use already calculated first block context. else { // This calculation is the same for all iterations with same password. // So for PBKDF2 we can calculate it only for first block and then reuse // to improve performance. for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest. KeyBuf[I] = Key[I] ^ 0x36; for (size_t I = KeyLength; I < Sha256BlockSize; I++) KeyBuf[I] = 0x36; sha256_init(&ICtx); sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key. } if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse. { *ICtxOpt=ICtx; *SetIOpt=true; } sha256_process(&ICtx, Data, DataLength); // Hash data. byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data. sha256_done(&ICtx, IDig); sha256_context RCtx; if (RCtxOpt!=NULL && *SetROpt) RCtx=*RCtxOpt; // Use already calculated first block context. else { // This calculation is the same for all iterations with same password. // So for PBKDF2 we can calculate it only for first block and then reuse // to improve performance. for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding. KeyBuf[I] = Key[I] ^ 0x5c; for (size_t I = KeyLength; I < Sha256BlockSize; I++) KeyBuf[I] = 0x5c; sha256_init(&RCtx); sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key. } if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse. { *RCtxOpt=RCtx; *SetROpt=true; } sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest. sha256_done(&RCtx, ResDigest); } // PBKDF2 for 32 byte key length. We generate the key for specified number // of iteration count also as two supplementary values (key for checksums // and password verification) for iterations+16 and iterations+32. void pbkdf2(const byte *Pwd, size_t PwdLength, const byte *Salt, size_t SaltLength, byte *Key, byte *V1, byte *V2, uint Count) { const size_t MaxSalt=64; byte SaltData[MaxSalt+4]; memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. SaltData[SaltLength + 1] = 0; SaltData[SaltLength + 2] = 0; SaltData[SaltLength + 3] = 1; // First iteration: HMAC of password, salt and block index (1). byte U1[SHA256_DIGEST_SIZE]; hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL); byte Fn[SHA256_DIGEST_SIZE]; // Current function value. memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration. uint CurCount[] = { Count-1, 16, 16 }; byte *CurValue[] = { Key , V1, V2 }; sha256_context ICtxOpt,RCtxOpt; bool SetIOpt=false,SetROpt=false; byte U2[SHA256_DIGEST_SIZE]; for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values. { for (uint J = 0; J < CurCount[I]; J++) { // U2 = PRF (P, U1). hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt); memcpy(U1, U2, sizeof(U1)); for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U. Fn[K] ^= U1[K]; } memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE); } cleandata(SaltData, sizeof(SaltData)); cleandata(Fn, sizeof(Fn)); cleandata(U1, sizeof(U1)); cleandata(U2, sizeof(U2)); } void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, byte *PswCheck) { if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) return; byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; bool Found=false; for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password && memcmp(Item->Salt,Salt,SIZE_SALT50)==0) { memcpy(Key,Item->Key,sizeof(Key)); SecHideData(Key,sizeof(Key),false,false); memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue)); memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue)); Found=true; break; } } if (!Found) { char PwdUtf[MAXPASSWORD*4]; WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf)); pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<Lg2Count=Lg2Cnt; Item->Pwd=*Password; memcpy(Item->Salt,Salt,SIZE_SALT50); memcpy(Item->Key,Key,sizeof(Item->Key)); memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue)); memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue)); SecHideData(Item->Key,sizeof(Item->Key),true,false); } if (HashKey!=NULL) memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE); if (PswCheck!=NULL) { memset(PswCheck,0,SIZE_PSWCHECK); for (uint I=0;IType==HASH_CRC32) { byte RawCRC[4]; RawPut4(Value->CRC32,RawCRC); byte Digest[SHA256_DIGEST_SIZE]; hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL); Value->CRC32=0; for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8); } if (Value->Type==HASH_BLAKE2) { byte Digest[BLAKE2_DIGEST_SIZE]; hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL); memcpy(Value->Digest,Digest,sizeof(Value->Digest)); } } #if 0 static void TestPBKDF2(); struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF; void TestPBKDF2() // Test PBKDF2 HMAC-SHA256 { byte Key[32],V1[32],V2[32]; pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1); byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b }; mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed"); pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096); byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a }; mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed"); pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536); byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf}; mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed"); } #endif unrar/dll.cpp000666 000000 000000 00000031355 13343205464 011651 0ustar00000000 000000 #include "rar.hpp" static int RarErrorToDll(RAR_EXIT ErrCode); struct DataSet { CommandData Cmd; Archive Arc; CmdExtract Extract; int OpenMode; int HeaderSize; DataSet():Arc(&Cmd),Extract(&Cmd) {}; }; HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) { RAROpenArchiveDataEx rx; memset(&rx,0,sizeof(rx)); rx.ArcName=r->ArcName; rx.OpenMode=r->OpenMode; rx.CmtBuf=r->CmtBuf; rx.CmtBufSize=r->CmtBufSize; HANDLE hArc=RAROpenArchiveEx(&rx); r->OpenResult=rx.OpenResult; r->CmtSize=rx.CmtSize; r->CmtState=rx.CmtState; return hArc; } HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) { DataSet *Data=NULL; try { ErrHandler.Clean(); r->OpenResult=0; Data=new DataSet; Data->Cmd.DllError=0; Data->OpenMode=r->OpenMode; Data->Cmd.FileArgs.AddString(L"*"); char AnsiArcName[NM]; *AnsiArcName=0; if (r->ArcName!=NULL) { strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); #ifdef _WIN_ALL if (!AreFileApisANSI()) { OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); AnsiArcName[ASIZE(AnsiArcName)-1]=0; } #endif } wchar ArcName[NM]; GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); Data->Cmd.AddArcName(ArcName); Data->Cmd.Overwrite=OVERWRITE_ALL; Data->Cmd.VersionControl=1; Data->Cmd.Callback=r->Callback; Data->Cmd.UserData=r->UserData; // Open shared mode is added by request of dll users, who need to // browse and unpack archives while downloading. Data->Cmd.OpenShared = true; if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) { r->OpenResult=ERAR_EOPEN; delete Data; return NULL; } if (!Data->Arc.IsArchive(true)) { if (Data->Cmd.DllError!=0) r->OpenResult=Data->Cmd.DllError; else { RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) r->OpenResult=RarErrorToDll(ErrCode); else r->OpenResult=ERAR_BAD_ARCHIVE; } delete Data; return NULL; } r->Flags=0; if (Data->Arc.Volume) r->Flags|=0x01; if (Data->Arc.Locked) r->Flags|=0x04; if (Data->Arc.Solid) r->Flags|=0x08; if (Data->Arc.NewNumbering) r->Flags|=0x10; if (Data->Arc.Signed) r->Flags|=0x20; if (Data->Arc.Protected) r->Flags|=0x40; if (Data->Arc.Encrypted) r->Flags|=0x80; if (Data->Arc.FirstVolume) r->Flags|=0x100; Array CmtDataW; if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) { Array CmtData(CmtDataW.Size()*4+1); memset(&CmtData[0],0,CmtData.Size()); WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); size_t Size=strlen(&CmtData[0])+1; r->Flags|=2; r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; r->CmtSize=(uint)Min(Size,r->CmtBufSize); memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); if (Size<=r->CmtBufSize) r->CmtBuf[r->CmtSize-1]=0; } else r->CmtState=r->CmtSize=0; Data->Extract.ExtractArchiveInit(Data->Arc); return (HANDLE)Data; } catch (RAR_EXIT ErrCode) { if (Data!=NULL && Data->Cmd.DllError!=0) r->OpenResult=Data->Cmd.DllError; else r->OpenResult=RarErrorToDll(ErrCode); if (Data != NULL) delete Data; return NULL; } catch (std::bad_alloc&) // Catch 'new' exception. { r->OpenResult=ERAR_NO_MEMORY; if (Data != NULL) delete Data; } return NULL; // To make compilers happy. } int PASCAL RARCloseArchive(HANDLE hArcData) { DataSet *Data=(DataSet *)hArcData; try { bool Success=Data==NULL ? false:Data->Arc.Close(); delete Data; return Success ? ERAR_SUCCESS : ERAR_ECLOSE; } catch (RAR_EXIT ErrCode) { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } } int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) { struct RARHeaderDataEx X; memset(&X,0,sizeof(X)); int Code=RARReadHeaderEx(hArcData,&X); strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); D->Flags=X.Flags; D->PackSize=X.PackSize; D->UnpSize=X.UnpSize; D->HostOS=X.HostOS; D->FileCRC=X.FileCRC; D->FileTime=X.FileTime; D->UnpVer=X.UnpVer; D->Method=X.Method; D->FileAttr=X.FileAttr; D->CmtSize=0; D->CmtState=0; return Code; } int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) { DataSet *Data=(DataSet *)hArcData; try { if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) { if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && Data->Arc.EndArcHead.NextVolume) if (MergeArchive(Data->Arc,NULL,false,'L')) { Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); return RARReadHeaderEx(hArcData,D); } else return ERAR_EOPEN; if (Data->Arc.BrokenHeader) return ERAR_BAD_DATA; // Might be necessary if RARSetPassword is still called instead of // open callback for RAR5 archives and if password is invalid. if (Data->Arc.FailedHeaderDecryption) return ERAR_BAD_PASSWORD; return ERAR_END_ARCHIVE; } FileHeader *hd=&Data->Arc.FileHead; if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) { int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); if (Code==0) return RARReadHeaderEx(hArcData,D); else return Code; } wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); #ifdef _WIN_ALL CharToOemA(D->FileName,D->FileName); #endif D->Flags=0; if (hd->SplitBefore) D->Flags|=RHDF_SPLITBEFORE; if (hd->SplitAfter) D->Flags|=RHDF_SPLITAFTER; if (hd->Encrypted) D->Flags|=RHDF_ENCRYPTED; if (hd->Solid) D->Flags|=RHDF_SOLID; if (hd->Dir) D->Flags|=RHDF_DIRECTORY; D->PackSize=uint(hd->PackSize & 0xffffffff); D->PackSizeHigh=uint(hd->PackSize>>32); D->UnpSize=uint(hd->UnpSize & 0xffffffff); D->UnpSizeHigh=uint(hd->UnpSize>>32); D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; if (Data->Arc.Format==RARFMT50) D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big. else D->UnpVer=Data->Arc.FileHead.UnpVer; D->FileCRC=hd->FileHash.CRC32; D->FileTime=hd->mtime.GetDos(); uint64 MRaw=hd->mtime.GetWin(); D->MtimeLow=(uint)MRaw; D->MtimeHigh=(uint)(MRaw>>32); uint64 CRaw=hd->ctime.GetWin(); D->CtimeLow=(uint)CRaw; D->CtimeHigh=(uint)(CRaw>>32); uint64 ARaw=hd->atime.GetWin(); D->AtimeLow=(uint)ARaw; D->AtimeHigh=(uint)(ARaw>>32); D->Method=hd->Method+0x30; D->FileAttr=hd->FileAttr; D->CmtSize=0; D->CmtState=0; D->DictSize=uint(hd->WinSize/1024); switch (hd->FileHash.Type) { case HASH_RAR14: case HASH_CRC32: D->HashType=RAR_HASH_CRC32; break; case HASH_BLAKE2: D->HashType=RAR_HASH_BLAKE2; memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); break; default: D->HashType=RAR_HASH_NONE; break; } D->RedirType=hd->RedirType; // RedirNameSize sanity check is useful in case some developer // did not initialize Reserved area with 0 as required in docs. // We have taken 'Redir*' fields from Reserved area. We may remove // this RedirNameSize check sometimes later. if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && D->RedirNameSize>0 && D->RedirNameSize<100000) wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); D->DirTarget=hd->DirTarget; } catch (RAR_EXIT ErrCode) { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } return ERAR_SUCCESS; } int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW) { DataSet *Data=(DataSet *)hArcData; try { Data->Cmd.DllError=0; if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || Operation==RAR_SKIP && !Data->Arc.Solid) { if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && Data->Arc.FileHead.SplitAfter) if (MergeArchive(Data->Arc,NULL,false,'L')) { Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); return ERAR_SUCCESS; } else return ERAR_EOPEN; Data->Arc.SeekToNext(); } else { Data->Cmd.DllOpMode=Operation; *Data->Cmd.ExtrPath=0; *Data->Cmd.DllDestName=0; if (DestPath!=NULL) { char ExtrPathA[NM]; strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); #ifdef _WIN_ALL // We must not apply OemToCharBuffA directly to DestPath, // because we do not know DestPath length and OemToCharBuffA // does not stop at 0. OemToCharA(ExtrPathA,ExtrPathA); #endif CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); } if (DestName!=NULL) { char DestNameA[NM]; strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); #ifdef _WIN_ALL // We must not apply OemToCharBuffA directly to DestName, // because we do not know DestName length and OemToCharBuffA // does not stop at 0. OemToCharA(DestNameA,DestNameA); #endif CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); } if (DestPathW!=NULL) { wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); } if (DestNameW!=NULL) wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T"); Data->Cmd.Test=Operation!=RAR_EXTRACT; bool Repeat=false; Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); // Now we process extra file information if any. // // Archive can be closed if we process volumes, next volume is missing // and current one is already removed or deleted. So we need to check // if archive is still open to avoid calling file operations on // the invalid file handle. Some of our file operations like Seek() // process such invalid handle correctly, some not. while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==HEAD_SERVICE) { Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); Data->Arc.SeekToNext(); } Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); } } catch (std::bad_alloc&) { return ERAR_NO_MEMORY; } catch (RAR_EXIT ErrCode) { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } return Data->Cmd.DllError; } int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) { return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL); } int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) { return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName); } void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc) { DataSet *Data=(DataSet *)hArcData; Data->Cmd.ChangeVolProc=ChangeVolProc; } void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData) { DataSet *Data=(DataSet *)hArcData; Data->Cmd.Callback=Callback; Data->Cmd.UserData=UserData; } void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc) { DataSet *Data=(DataSet *)hArcData; Data->Cmd.ProcessDataProc=ProcessDataProc; } #ifndef RAR_NOCRYPT void PASCAL RARSetPassword(HANDLE hArcData,char *Password) { DataSet *Data=(DataSet *)hArcData; wchar PasswordW[MAXPASSWORD]; GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); Data->Cmd.Password.Set(PasswordW); cleandata(PasswordW,sizeof(PasswordW)); } #endif int PASCAL RARGetDllVersion() { return RAR_DLL_VERSION; } static int RarErrorToDll(RAR_EXIT ErrCode) { switch(ErrCode) { case RARX_FATAL: return ERAR_EREAD; case RARX_CRC: return ERAR_BAD_DATA; case RARX_WRITE: return ERAR_EWRITE; case RARX_OPEN: return ERAR_EOPEN; case RARX_CREATE: return ERAR_ECREATE; case RARX_MEMORY: return ERAR_NO_MEMORY; case RARX_BADPWD: return ERAR_BAD_PASSWORD; case RARX_SUCCESS: return ERAR_SUCCESS; // 0. default: return ERAR_UNKNOWN; } } unrar/encname.cpp000666 000000 000000 00000003226 13343205464 012500 0ustar00000000 000000 #include "rar.hpp" EncodeFileName::EncodeFileName() { Flags=0; FlagBits=0; FlagsPos=0; DestSize=0; } void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize, wchar *NameW,size_t MaxDecSize) { size_t EncPos=0,DecPos=0; byte HighByte=EncPos=EncSize) break; Flags=EncName[EncPos++]; FlagBits=8; } switch(Flags>>6) { case 0: if (EncPos>=EncSize) break; NameW[DecPos++]=EncName[EncPos++]; break; case 1: if (EncPos>=EncSize) break; NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); break; case 2: if (EncPos+1>=EncSize) break; NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); EncPos+=2; break; case 3: { if (EncPos>=EncSize) break; int Length=EncName[EncPos++]; if ((Length & 0x80)!=0) { if (EncPos>=EncSize) break; byte Correction=EncName[EncPos++]; for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos1) exit(RARX_USERBREAK); // Otherwise return from signal handler and let Wait() function to close // files and quit. We cannot use the same approach as in Windows, // because Unix signal handler can block execution of our main code. #endif #if defined(_WIN_ALL) && !defined(_MSC_VER) // Never reached, just to avoid a compiler warning return TRUE; #endif } void ErrorHandler::SetSignalHandlers(bool Enable) { EnableBreak=Enable; #ifdef _WIN_ALL SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); #else signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); #endif } void ErrorHandler::Throw(RAR_EXIT Code) { if (Code==RARX_USERBREAK && !EnableBreak) return; #if !defined(SILENT) // Do not write "aborted" when just displaying online help. if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) mprintf(L"\n%s\n",St(MProgAborted)); #endif SetErrorCode(Code); throw Code; } bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) { #if !defined(SFX_MODULE) && !defined(SILENT) #ifdef _WIN_ALL int ErrType=GetLastError(); if (ErrType!=0) return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), Msg,(DWORD)Size,NULL)!=0; #endif #if defined(_UNIX) || defined(_EMX) if (errno!=0) { char *err=strerror(errno); if (err!=NULL) { CharToWide(err,Msg,Size); return true; } } #endif #endif return false; } void ErrorHandler::SysErrMsg() { #if !defined(SFX_MODULE) && !defined(SILENT) wchar Msg[1024]; if (!GetSysErrMsg(Msg,ASIZE(Msg))) return; #ifdef _WIN_ALL wchar *CurMsg=Msg; while (CurMsg!=NULL) { while (*CurMsg=='\r' || *CurMsg=='\n') CurMsg++; if (*CurMsg==0) break; wchar *EndMsg=wcschr(CurMsg,'\r'); if (EndMsg==NULL) EndMsg=wcschr(CurMsg,'\n'); if (EndMsg!=NULL) { *EndMsg=0; EndMsg++; } uiMsg(UIERROR_SYSERRMSG,CurMsg); CurMsg=EndMsg; } #endif #if defined(_UNIX) || defined(_EMX) uiMsg(UIERROR_SYSERRMSG,Msg); #endif #endif } int ErrorHandler::GetSystemErrorCode() { #ifdef _WIN_ALL return GetLastError(); #else return errno; #endif } void ErrorHandler::SetSystemErrorCode(int Code) { #ifdef _WIN_ALL SetLastError(Code); #else errno=Code; #endif } unrar/extinfo.cpp000666 000000 000000 00000011545 13343205464 012551 0ustar00000000 000000 #include "rar.hpp" #include "hardlinks.cpp" #include "win32stm.cpp" #ifdef _WIN_ALL #include "win32acl.cpp" #include "win32lnk.cpp" #endif #ifdef _UNIX #include "uowners.cpp" #ifdef SAVE_LINKS #include "ulinks.cpp" #endif #endif // RAR2 service header extra records. #ifndef SFX_MODULE void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) { if (Cmd->Test) return; switch(Arc.SubBlockHead.SubType) { #ifdef _UNIX case UO_HEAD: if (Cmd->ProcessOwners) ExtractUnixOwner20(Arc,Name); break; #endif #ifdef _WIN_ALL case NTACL_HEAD: if (Cmd->ProcessOwners) ExtractACL20(Arc,Name); break; case STREAM_HEAD: ExtractStreams20(Arc,Name); break; #endif } } #endif // RAR3 and RAR5 service header extra records. void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) { #ifdef _UNIX if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) ExtractUnixOwner30(Arc,Name); #endif #ifdef _WIN_ALL if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) ExtractACL(Arc,Name); if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) ExtractStreams(Arc,Name,Cmd->Test); #endif } // Extra data stored directly in file header. void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) { #ifdef _UNIX if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) SetUnixOwner(Arc,Name); #endif } // Calculate a number of path components except \. and \.. static int CalcAllowedDepth(const wchar *Name) { int AllowedDepth=0; while (*Name!=0) { if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) { bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); if (!Dot && !Dot2) AllowedDepth++; } Name++; } return AllowedDepth; } // Check if all existing path components are directories and not links. static bool LinkInPath(const wchar *Name) { wchar Path[NM]; if (wcslen(Name)>=ASIZE(Path)) return true; // It should not be that long, skip. wcsncpyz(Path,Name,ASIZE(Path)); for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) if (IsPathDiv(*s)) { *s=0; FindData FD; if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) return true; } return false; } bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) { // Catch root dir based /path/file paths also as stuff like \\?\. // Do not check PrepSrcName here, it can be root based if destination path // is a root based. if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) return false; // Number of ".." in link target. int UpLevels=0; for (int Pos=0;*TargetName!=0;Pos++) { bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && (IsPathDiv(TargetName[2]) || TargetName[2]==0) && (Pos==0 || IsPathDiv(*(TargetName-1))); if (Dot2) UpLevels++; TargetName++; } // If link target includes "..", it must not have another links // in the path, because they can bypass our safety check. For example, // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. if (UpLevels>0 && LinkInPath(PrepSrcName)) return false; // We could check just prepared src name, but for extra safety // we check both original (as from archive header) and prepared // (after applying the destination path and -ep switches) names. int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth. // Remove the destination path from prepared name if any. We should not // count the destination path depth, because the link target must point // inside of this path, not outside of it. size_t ExtrPathLength=wcslen(Cmd->ExtrPath); if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) { PrepSrcName+=ExtrPathLength; while (IsPathDiv(*PrepSrcName)) PrepSrcName++; } int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; } bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) { #if defined(SAVE_LINKS) && defined(_UNIX) // For RAR 3.x archives we process links even in test mode to skip link data. if (Arc.Format==RARFMT15) return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); if (Arc.Format==RARFMT50) return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); #elif defined _WIN_ALL // RAR 5.0 archives store link information in file header, so there is // no need to additionally test it if we do not create a file. if (Arc.Format==RARFMT50) return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); #endif return false; } unrar/extract.cpp000666 000000 000000 00000105112 13343205464 012541 0ustar00000000 000000 #include "rar.hpp" CmdExtract::CmdExtract(CommandData *Cmd) { CmdExtract::Cmd=Cmd; *ArcName=0; *DestFileName=0; TotalFileCount=0; Unp=new Unpack(&DataIO); #ifdef RAR_SMP Unp->SetThreads(Cmd->Threads); #endif } CmdExtract::~CmdExtract() { delete Unp; } void CmdExtract::DoExtract() { #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) Fat32=NotFat32=false; #endif PasswordCancelled=false; DataIO.SetCurrentCommand(Cmd->Command[0]); FindData FD; while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) if (FindFile::FastFind(ArcName,&FD)) DataIO.TotalArcSize+=FD.Size; Cmd->ArcNames.Rewind(); while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. while (true) { EXTRACT_ARC_CODE Code=ExtractArchive(); if (Code!=EXTRACT_ARC_REPEAT) break; } if (FindFile::FastFind(ArcName,&FD)) DataIO.ProcessedArcSize+=FD.Size; } // Clean user entered password. Not really required, just for extra safety. if (Cmd->ManualPassword) Cmd->Password.Clean(); if (TotalFileCount==0 && Cmd->Command[0]!='I' && ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. { if (!PasswordCancelled) uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); // Other error codes may explain a reason of "no files extracted" clearer, // so set it only if no other errors found (wrong mask set by user). if (ErrHandler.GetErrorCode()==RARX_SUCCESS) ErrHandler.SetErrorCode(RARX_NOFILES); } else if (!Cmd->DisableDone) if (Cmd->Command[0]=='I') mprintf(St(MDone)); else if (ErrHandler.GetErrorCount()==0) mprintf(St(MExtrAllOk)); else mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); } void CmdExtract::ExtractArchiveInit(Archive &Arc) { DataIO.UnpArcSize=Arc.FileLength(); FileCount=0; MatchedArgs=0; #ifndef SFX_MODULE FirstFile=true; #endif GlobalPassword=Cmd->Password.IsSet(); DataIO.UnpVolume=false; PrevProcessed=false; AllMatchesExact=true; ReconstructDone=false; AnySolidDataUnpackedWell=false; StartTime.SetCurrentTime(); } EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); if (!Arc.WOpen(ArcName)) return EXTRACT_ARC_NEXT; if (!Arc.IsArchive(true)) { #if !defined(SFX_MODULE) && !defined(RARDLL) if (CmpExt(ArcName,L"rev")) { wchar FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames.Search(FirstVolName,false)) return EXTRACT_ARC_NEXT; RecVolumesTest(Cmd,NULL,ArcName); TotalFileCount++; // Suppress "No files to extract" message. return EXTRACT_ARC_NEXT; } #endif mprintf(St(MNotRAR),ArcName); #ifndef SFX_MODULE if (CmpExt(ArcName,L"rar")) #endif ErrHandler.SetErrorCode(RARX_WARNING); return EXTRACT_ARC_NEXT; } if (Arc.FailedHeaderDecryption) // Bad archive password. return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE if (Arc.Volume && !Arc.FirstVolume) { wchar FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames.Search(FirstVolName,false)) return EXTRACT_ARC_NEXT; } #endif int64 VolumeSetSize=0; // Total size of volumes after the current volume. if (Arc.Volume) { // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. wchar NextName[NM]; wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); FindData FD; if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; if (*Cmd->Command=='I') { Cmd->DisablePercentage=true; } else uiStartArchiveExtract(!Cmd->Test,ArcName); Arc.ViewComment(); while (1) { size_t Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Arc,Size,Repeat)) if (Repeat) { // If we started extraction from not first volume and need to // restart it from first, we must correct DataIO.TotalArcSize // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. FindData OldArc,NewArc; if (FindFile::FastFind(Arc.FileName,&OldArc) && FindFile::FastFind(ArcName,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; return EXTRACT_ARC_REPEAT; } else break; } #if !defined(SFX_MODULE) && !defined(RARDLL) if (Cmd->Test && Arc.Volume) RecVolumesTest(Cmd,&Arc,ArcName); #endif return EXTRACT_ARC_NEXT; } bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { wchar Command=Cmd->Command[0]; if (HeaderSize==0) if (DataIO.UnpVolume) { #ifdef NOVOLUME return false; #else // Supposing we unpack an old RAR volume without the end of archive // record and last file is not split between volumes. if (!MergeArchive(Arc,&DataIO,false,Command)) { ErrHandler.SetErrorCode(RARX_WARNING); return false; } #endif } else return false; HEADER_TYPE HeaderType=Arc.GetHeaderType(); if (HeaderType!=HEAD_FILE) { #ifndef SFX_MODULE if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) SetExtraInfo20(Cmd,Arc,DestFileName); #endif if (HeaderType==HEAD_SERVICE && PrevProcessed) SetExtraInfo(Cmd,Arc,DestFileName); if (HeaderType==HEAD_ENDARC) if (Arc.EndArcHead.NextVolume) { #ifndef NOVOLUME if (!MergeArchive(Arc,&DataIO,false,Command)) { ErrHandler.SetErrorCode(RARX_WARNING); return false; } #endif Arc.Seek(Arc.CurBlockPos,SEEK_SET); return true; } else return false; Arc.SeekToNext(); return true; } PrevProcessed=false; // We can get negative sizes in corrupt archive and it is unacceptable // for size comparisons in ComprDataIO::UnpRead, where we cast sizes // to size_t and can exceed another read or available size. We could fix it // when reading an archive. But we prefer to do it here, because this // function is called directly in unrar.dll, so we fix bad parameters // passed to dll. Also we want to see real negative sizes in the listing // of corrupt archive. To prevent uninitialized data access perform // these checks after rejecting zero length and non-file headers above. if (Arc.FileHead.PackSize<0) Arc.FileHead.PackSize=0; if (Arc.FileHead.UnpSize<0) Arc.FileHead.UnpSize=0; if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) return false; int MatchType=MATCH_WILDSUBPATH; bool EqualNames=false; wchar MatchedArg[NM]; int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,MatchedArg,ASIZE(MatchedArg)); bool MatchFound=MatchNumber!=0; #ifndef SFX_MODULE if (Cmd->ExclPath==EXCL_BASEPATH) { wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); *PointToName(Cmd->ArcPath)=0; if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. *Cmd->ArcPath=0; } #endif if (MatchFound && !EqualNames) AllMatchesExact=false; Arc.ConvertAttributes(); #if !defined(SFX_MODULE) && !defined(RARDLL) if (Arc.FileHead.SplitBefore && FirstFile) { wchar CurVolName[NM]; wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering); if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) { wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction". // If first volume name does not match the current name and if such // volume name really exists, let's unpack from this first volume. Repeat=true; return false; } #ifndef RARDLL if (!ReconstructDone) { ReconstructDone=true; if (RecVolumesRestore(Cmd,Arc.FileName,true)) { Repeat=true; return false; } } #endif wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); } #endif wchar ArcFileName[NM]; ConvertPath(Arc.FileHead.FileName,ArcFileName); if (Arc.FileHead.Version) { if (Cmd->VersionControl!=1 && !EqualNames) { if (Cmd->VersionControl==0) MatchFound=false; int Version=ParseVersionFileName(ArcFileName,false); if (Cmd->VersionControl-1==Version) ParseVersionFileName(ArcFileName,true); else MatchFound=false; } } else if (!Arc.IsArcDir() && Cmd->VersionControl>1) MatchFound=false; DataIO.UnpVolume=Arc.FileHead.SplitAfter; DataIO.NextVolumeMissing=false; Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); bool ExtrFile=false; bool SkipSolid=false; #ifndef SFX_MODULE if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore) { if (MatchFound) { uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName); #ifdef RARDLL Cmd->DllError=ERAR_BAD_DATA; #endif ErrHandler.SetErrorCode(RARX_OPEN); } MatchFound=false; } FirstFile=false; #endif if (MatchFound || (SkipSolid=Arc.Solid)!=0) { // First common call of uiStartFileExtract. It is done before overwrite // prompts, so if SkipSolid state is changed below, we'll need to make // additional uiStartFileExtract calls with updated parameters. if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) return false; ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); // DestFileName can be set empty in case of excessive -ap switch. ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) { FindData FD; if (FindFile::FastFind(DestFileName,&FD)) { if (FD.mtime >= Arc.FileHead.mtime) { // If directory already exists and its modification time is newer // than start of extraction, it is likely it was created // when creating a path to one of already extracted items. // In such case we'll better update its time even if archived // directory is older. if (!FD.IsDir || FD.mtimeFreshFiles) ExtrFile=false; } if (!CheckUnpVer(Arc,ArcFileName)) { ErrHandler.SetErrorCode(RARX_FATAL); #ifdef RARDLL Cmd->DllError=ERAR_UNKNOWN_FORMAT; #endif Arc.SeekToNext(); return !Arc.Solid; // Can try extracting next file only in non-solid archive. } while (true) // Repeat the password prompt for wrong and empty passwords. { if (Arc.FileHead.Encrypted) { // Stop archive extracting if user cancelled a password prompt. #ifdef RARDLL if (!ExtrDllGetPassword()) { Cmd->DllError=ERAR_MISSING_PASSWORD; return false; } #else if (!ExtrGetPassword(Arc,ArcFileName)) { PasswordCancelled=true; return false; } #endif } // Set a password before creating the file, so we can skip creating // in case of wrong password. SecPassword FilePassword=Cmd->Password; #if defined(_WIN_ALL) && !defined(SFX_MODULE) ConvertDosPassword(Arc,FilePassword); #endif byte PswCheck[SIZE_PSWCHECK]; DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, Arc.FileHead.InitV,Arc.FileHead.Lg2Count, Arc.FileHead.HashKey,PswCheck); // If header is damaged, we cannot rely on password check value, // because it can be damaged too. if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && !Arc.BrokenHeader) { if (GlobalPassword) // For -p or Ctrl+P. { // This message is used by Android GUI to reset cached passwords. // Update appropriate code if changed. uiMsg(UIERROR_BADPSW,ArcFileName); } else // For passwords entered manually. { // This message is used by Android GUI and Windows GUI and SFX to // reset cached passwords. Update appropriate code if changed. uiMsg(UIWAIT_BADPSW,ArcFileName); Cmd->Password.Clean(); // Avoid new requests for unrar.dll to prevent the infinite loop // if app always returns the same password. #ifndef RARDLL continue; // Request a password again. #endif } #ifdef RARDLL // If we already have ERAR_EOPEN as result of missing volume, // we should not replace it with less precise ERAR_BAD_PASSWORD. if (Cmd->DllError!=ERAR_EOPEN) Cmd->DllError=ERAR_BAD_PASSWORD; #endif ErrHandler.SetErrorCode(RARX_BADPWD); ExtrFile=false; } break; } #ifdef RARDLL if (*Cmd->DllDestName!=0) wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); #endif File CurFile; bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) { if (ExtrFile && Command!='P' && !Cmd->Test) { // Overwrite prompt for symbolic and hard links. bool UserReject=false; if (FileExist(DestFileName) && !UserReject) FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); if (UserReject) ExtrFile=false; } } else if (Arc.IsArcDir()) { if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) return true; TotalFileCount++; ExtrCreateDir(Arc,ArcFileName); // It is important to not increment MatchedArgs here, so we extract // dir with its entire contents and not dir record only even if // dir record precedes files. return true; } else if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). ExtrFile=ExtrCreateFile(Arc,CurFile); if (!ExtrFile && Arc.Solid) { SkipSolid=true; ExtrFile=true; // We changed SkipSolid, so we need to call uiStartFileExtract // with "Skip" parameter to change the operation status // from "extracting" to "skipping". For example, it can be necessary // if user answered "No" to overwrite prompt when unpacking // a solid archive. if (!uiStartFileExtract(ArcFileName,false,false,true)) return false; } if (ExtrFile) { // Set it in test mode, so we also test subheaders such as NTFS streams // after tested file. if (Cmd->Test) PrevProcessed=true; bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk. if (!SkipSolid) { if (!TestMode && Command!='P' && CurFile.IsDevice()) { uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName); ErrHandler.WriteError(Arc.FileName,DestFileName); } TotalFileCount++; } FileCount++; if (Command!='I') if (SkipSolid) mprintf(St(MExtrSkipFile),ArcFileName); else switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. { case 'T': mprintf(St(MExtrTestFile),ArcFileName); break; #ifndef SFX_MODULE case 'P': mprintf(St(MExtrPrinting),ArcFileName); break; #endif case 'X': case 'E': mprintf(St(MExtrFile),DestFileName); break; } if (!Cmd->DisablePercentage) mprintf(L" "); DataIO.CurUnpRead=0; DataIO.CurUnpWrite=0; DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize); DataIO.SetFiles(&Arc,&CurFile); DataIO.SetTestMode(TestMode); DataIO.SetSkipUnpCRC(SkipSolid); #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32)) { if (!Fat32) // Not detected yet. NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath)); if (Fat32) uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit. } #endif if (!TestMode && !Arc.BrokenHeader && (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize && (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) CurFile.Prealloc(Arc.FileHead.UnpSize); CurFile.SetAllowDelete(!Cmd->KeepBroken); bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; bool ShowChecksum=true; // Display checksum verification result. bool LinkSuccess=true; // Assume success for test mode. if (LinkEntry) { FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType; if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) { wchar NameExisting[NM]; ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting)); if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting)); else LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); } else if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) { if (FileCreateMode) LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); } else { uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName); LinkSuccess=false; } if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode) { // RAR 5.x links have a valid data checksum even in case of // failure, because they do not store any data. // We do not want to display "OK" in this case. // For 4.x symlinks we verify the checksum only when extracting, // but not when testing an archive. ShowChecksum=false; } PrevProcessed=FileCreateMode && LinkSuccess; } else if (!Arc.FileHead.SplitBefore) if (Arc.FileHead.Method==0) UnstoreFile(DataIO,Arc.FileHead.UnpSize); else { Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) Unp->DoUnpack(15,FileCount>1 && Arc.Solid); else #endif Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid); } Arc.SeekToNext(); // We check for "split after" flag to detect partially extracted files // from incomplete volume sets. For them file header contains packed // data hash, which must not be compared against unpacked data hash // to prevent accidental match. Moreover, for -m0 volumes packed data // hash would match truncated unpacked data hash and lead to fake "OK" // in incomplete volume set. bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL); // We set AnySolidDataUnpackedWell to true if we found at least one // valid non-zero solid file in preceding solid stream. If it is true // and if current encrypted file is broken, we do not need to hint // about a wrong password and can report CRC error only. if (!Arc.FileHead.Solid) AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. else if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC) AnySolidDataUnpackedWell=true; bool BrokenFile=false; // Checksum is not calculated in skip solid mode for performance reason. if (!SkipSolid && ShowChecksum) { if (ValidCRC) { if (Command!='P' && Command!='I') mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); } else { if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || Arc.BrokenHeader) && !AnySolidDataUnpackedWell) uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName); else uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName); BrokenFile=true; ErrHandler.SetErrorCode(RARX_CRC); #ifdef RARDLL // If we already have ERAR_EOPEN as result of missing volume // or ERAR_BAD_PASSWORD for RAR5 wrong password, // we should not replace it with less precise ERAR_BAD_DATA. if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD) Cmd->DllError=ERAR_BAD_DATA; #endif } } else mprintf(L"\b\b\b\b\b "); if (!TestMode && (Command=='X' || Command=='E') && (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && (!BrokenFile || Cmd->KeepBroken)) { // We could preallocate more space that really written to broken file. if (BrokenFile) CurFile.Truncate(); #if defined(_WIN_ALL) || defined(_EMX) if (Cmd->ClearArc) Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; #endif CurFile.SetOpenFileTime( Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); CurFile.Close(); #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (Cmd->SetCompressedAttr && (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) SetFileCompression(CurFile.FileName,true); #endif SetFileHeaderExtra(Cmd,Arc,CurFile.FileName); CurFile.SetCloseFileTime( Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr)) uiMsg(UIERROR_FILEATTR,Arc.FileName,CurFile.FileName); PrevProcessed=true; } } } // It is important to increment it for files, but not dirs. So we extract // dir with its entire contents, not just dir record only even if dir // record precedes files. if (MatchFound) MatchedArgs++; if (DataIO.NextVolumeMissing) return false; if (!ExtrFile) if (!Arc.Solid) Arc.SeekToNext(); else if (!SkipSolid) return false; return true; } void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) { Array Buffer(File::CopyBufferSize()); while (true) { int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size()); if (ReadSize<=0) break; int WriteSize=ReadSize0) { DataIO.UnpWrite(&Buffer[0],WriteSize); DestUnpSize-=WriteSize; } } } bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) { SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. File Existing; if (!Existing.WOpen(NameExisting)) { uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); uiMsg(UIERROR_FILECOPYHINT,ArcName); #ifdef RARDLL Cmd->DllError=ERAR_EREFERENCE; #endif return false; } Array Buffer(0x100000); int64 CopySize=0; while (true) { Wait(); int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); if (ReadSize==0) break; New.Write(&Buffer[0],ReadSize); CopySize+=ReadSize; } return true; } void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) { wcsncpyz(DestName,Cmd->ExtrPath,DestSize); if (*Cmd->ExtrPath!=0) { wchar LastChar=*PointToLastChar(Cmd->ExtrPath); // We need IsPathDiv check here to correctly handle Unix forward slash // in the end of destination path in Windows: rar x arc dest/ // IsDriveDiv is needed for current drive dir: rar x arc d: if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) { // Destination path can be without trailing slash if it come from GUI shell. AddEndSlash(DestName,DestSize); } } #ifndef SFX_MODULE if (Cmd->AppendArcNameToPath) { wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); SetExt(DestName,NULL,DestSize); AddEndSlash(DestName,DestSize); } #endif #ifndef SFX_MODULE size_t ArcPathLength=wcslen(Cmd->ArcPath); if (ArcPathLength>0) { size_t NameLength=wcslen(ArcFileName); // Earlier we compared lengths only here, but then noticed a cosmetic bug // in WinRAR. When extracting a file reference from subfolder with // "Extract relative paths", so WinRAR sets ArcPath, if reference target // is missing, error message removed ArcPath both from reference and target // names. If target was stored in another folder, its name looked wrong. if (NameLength>=ArcPathLength && wcsnicompc(Cmd->ArcPath,ArcFileName,ArcPathLength)==0 && (IsPathDiv(Cmd->ArcPath[ArcPathLength-1]) || IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) { ArcFileName+=Min(ArcPathLength,NameLength); while (IsPathDiv(*ArcFileName)) ArcFileName++; if (*ArcFileName==0) // Excessive -ap switch. { *DestName=0; return; } } } #endif wchar Command=Cmd->Command[0]; // Use -ep3 only in systems, where disk letters are exist, not in Unix. bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); // We do not use any user specified destination paths when extracting // absolute paths in -ep3 mode. if (AbsPaths) *DestName=0; if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) wcsncatz(DestName,PointToName(ArcFileName),DestSize); else wcsncatz(DestName,ArcFileName,DestSize); #ifdef _WIN_ALL // Must do after Cmd->ArcPath processing above, so file name and arc path // trailing spaces are in sync. if (!Cmd->AllowIncompatNames) MakeNameCompatible(DestName); #endif wchar DiskLetter=toupperw(DestName[0]); if (AbsPaths) { if (DestName[1]=='_' && IsPathDiv(DestName[2]) && DiskLetter>='A' && DiskLetter<='Z') DestName[1]=':'; else if (DestName[0]=='_' && DestName[1]=='_') { // Convert __server\share to \\server\share. DestName[0]=CPATHDIVIDER; DestName[1]=CPATHDIVIDER; } } } #ifdef RARDLL bool CmdExtract::ExtrDllGetPassword() { if (!Cmd->Password.IsSet()) { if (Cmd->Callback!=NULL) { wchar PasswordW[MAXPASSWORD]; *PasswordW=0; if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) *PasswordW=0; if (*PasswordW==0) { char PasswordA[MAXPASSWORD]; *PasswordA=0; if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) *PasswordA=0; GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); cleandata(PasswordA,sizeof(PasswordA)); } Cmd->Password.Set(PasswordW); cleandata(PasswordW,sizeof(PasswordW)); Cmd->ManualPassword=true; } if (!Cmd->Password.IsSet()) return false; } return true; } #endif #ifndef RARDLL bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) { if (!Cmd->Password.IsSet()) { if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) { // Suppress "test is ok" message if user cancelled the password prompt. uiMsg(UIERROR_INCERRCOUNT); return false; } Cmd->ManualPassword=true; } #if !defined(SILENT) else if (!GlobalPassword && !Arc.FileHead.Solid) { eprintf(St(MUseCurPsw),ArcFileName); switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) { case -1: ErrHandler.Exit(RARX_USERBREAK); case 2: if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) return false; break; case 3: GlobalPassword=true; break; } } #endif return true; } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) { if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS) { // We need the password in OEM encoding if file was encrypted by // native RAR/DOS (not extender based). Let's make the conversion. wchar PlainPsw[MAXPASSWORD]; Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw)); char PswA[MAXPASSWORD]; CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); PswA[ASIZE(PswA)-1]=0; CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); DestPwd.Set(PlainPsw); cleandata(PlainPsw,sizeof(PlainPsw)); cleandata(PswA,sizeof(PswA)); } } #endif void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) { if (Cmd->Test) { mprintf(St(MExtrTestFile),ArcFileName); mprintf(L" %s",St(MOk)); return; } MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); bool DirExist=false; if (MDCode!=MKDIR_SUCCESS) { DirExist=FileExist(DestFileName); if (DirExist && !IsDir(GetFileAttr(DestFileName))) { // File with name same as this directory exists. Propose user // to overwrite it. bool UserReject; FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); DirExist=false; } if (!DirExist) { CreatePath(DestFileName,true); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); if (MDCode!=MKDIR_SUCCESS) { wchar OrigName[ASIZE(DestFileName)]; wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); MakeNameUsable(DestFileName,true); CreatePath(DestFileName,true); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); #ifndef SFX_MODULE if (MDCode==MKDIR_SUCCESS) uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif } } } if (MDCode==MKDIR_SUCCESS) { mprintf(St(MCreatDir),DestFileName); mprintf(L" %s",St(MOk)); PrevProcessed=true; } else if (DirExist) { if (!Cmd->IgnoreGeneralAttr) SetFileAttr(DestFileName,Arc.FileHead.FileAttr); PrevProcessed=true; } else { uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName); ErrHandler.SysErrMsg(); #ifdef RARDLL Cmd->DllError=ERAR_ECREATE; #endif ErrHandler.SetErrorCode(RARX_CREATE); } if (PrevProcessed) { #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (Cmd->SetCompressedAttr && (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) SetFileCompression(DestFileName,true); #endif SetFileHeaderExtra(Cmd,Arc,DestFileName); SetDirTime(DestFileName, Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); } } bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) { bool Success=true; wchar Command=Cmd->Command[0]; #if !defined(SFX_MODULE) if (Command=='P') CurFile.SetHandleType(FILE_HANDLESTD); #endif if ((Command=='E' || Command=='X') && !Cmd->Test) { bool UserReject; // Specify "write only" mode to avoid OpenIndiana NAS problems // with SetFileTime and read+write files. if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { Success=false; if (!UserReject) { ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); #ifdef RARDLL Cmd->DllError=ERAR_ECREATE; #endif if (!IsNameUsable(DestFileName)) { uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); wchar OrigName[ASIZE(DestFileName)]; wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); MakeNameUsable(DestFileName,true); CreatePath(DestFileName,true); if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { #ifndef SFX_MODULE uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif Success=true; } else ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); } } } } return Success; } bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) { bool WrongVer; if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; else { #ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK; #else // All formats since 1.3 for RAR. WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK; #endif } // We can unpack stored files regardless of compression version field. if (Arc.FileHead.Method==0) WrongVer=false; if (WrongVer) { ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); uiMsg(UIERROR_NEWERRAR,Arc.FileName); } return !WrongVer; } unrar/filcreat.cpp000666 000000 000000 00000012517 13343205464 012666 0ustar00000000 000000 #include "rar.hpp" // If NewFile==NULL, we delete created file after user confirmation. // It is useful we we need to overwrite an existing folder or file, // but need user confirmation for that. bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) *UserReject=false; #ifdef _WIN_ALL bool ShortNameChanged=false; #endif while (FileExist(Name)) { #if defined(_WIN_ALL) if (!ShortNameChanged) { // Avoid the infinite loop if UpdateExistingShortName returns // the same name. ShortNameChanged=true; // Maybe our long name matches the short name of existing file. // Let's check if we can change the short name. if (UpdateExistingShortName(Name)) continue; } // Allow short name check again. It is necessary, because rename and // autorename below can change the name, so we need to check it again. ShortNameChanged=false; #endif UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); if (Choice==UIASKREP_R_REPLACE) break; if (Choice==UIASKREP_R_SKIP) { if (UserReject!=NULL) *UserReject=true; return false; } if (Choice==UIASKREP_R_CANCEL) ErrHandler.Exit(RARX_USERBREAK); } // Try to truncate the existing file first instead of delete, // so we preserve existing file permissions such as NTFS permissions. uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; if (NewFile!=NULL && NewFile->Create(Name,FileMode)) return true; CreatePath(Name,true); return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) { wchar NewName[NM]; size_t NameLength=wcslen(Name); wchar *Ext=GetExt(Name); if (Ext==NULL) Ext=Name+NameLength; for (uint FileVer=1;;FileVer++) { swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); if (!FileExist(NewName)) { wcsncpyz(Name,NewName,MaxNameSize); break; } if (FileVer>=1000000) return false; } return true; } #if defined(_WIN_ALL) // If we find a file, which short name is equal to 'Name', we try to change // its short name, while preserving the long name. It helps when unpacking // an archived file, which long name is equal to short name of already // existing file. Otherwise we would overwrite the already existing file, // even though its long name does not match the name of unpacking file. bool UpdateExistingShortName(const wchar *Name) { wchar LongPathName[NM]; DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); if (Res==0 || Res>=ASIZE(LongPathName)) return false; wchar ShortPathName[NM]; Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); if (Res==0 || Res>=ASIZE(ShortPathName)) return false; wchar *LongName=PointToName(LongPathName); wchar *ShortName=PointToName(ShortPathName); // We continue only if file has a short name, which does not match its // long name, and this short name is equal to name of file which we need // to create. if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || wcsicomp(PointToName(Name),ShortName)!=0) return false; // Generate the temporary new name for existing file. wchar NewName[NM]; *NewName=0; for (int I=0;I<10000 && *NewName==0;I+=123) { // Here we copy the path part of file to create. We'll make the temporary // file in the same folder. wcsncpyz(NewName,Name,ASIZE(NewName)); // Here we set the random name part. swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); // If such file is already exist, try next random name. if (FileExist(NewName)) *NewName=0; } // If we could not generate the name not used by any other file, we return. if (*NewName==0) return false; // FastFind returns the name without path, but we need the fully qualified // name for renaming, so we use the path from file to create and long name // from existing file. wchar FullName[NM]; wcsncpyz(FullName,Name,ASIZE(FullName)); SetName(FullName,LongName,ASIZE(FullName)); // Rename the existing file to randomly generated name. Normally it changes // the short name too. if (!MoveFile(FullName,NewName)) return false; // Now we need to create the temporary empty file with same name as // short name of our already existing file. We do it to occupy its previous // short name and not allow to use it again when renaming the file back to // its original long name. File KeepShortFile; bool Created=false; if (!FileExist(Name)) Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); // Now we rename the existing file from temporary name to original long name. // Since its previous short name is occupied by another file, it should // get another short name. MoveFile(NewName,FullName); if (Created) { // Delete the temporary zero length file occupying the short name, KeepShortFile.Close(); KeepShortFile.Delete(); } // We successfully changed the short name. Maybe sometimes we'll simplify // this function by use of SetFileShortName Windows API call. // But SetFileShortName is not available in older Windows. return true; } #endif unrar/file.cpp000666 000000 000000 00000042203 13343205464 012007 0ustar00000000 000000 #include "rar.hpp" File::File() { hFile=FILE_BAD_HANDLE; *FileName=0; NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; SkipClose=false; IgnoreReadErrors=false; ErrorType=FILE_SUCCESS; OpenShared=false; AllowDelete=true; AllowExceptions=true; #ifdef _WIN_ALL NoSequentialRead=false; CreateMode=FMF_UNDEFINED; #endif } File::~File() { if (hFile!=FILE_BAD_HANDLE && !SkipClose) if (NewFile) Delete(); else Close(); } void File::operator = (File &SrcFile) { hFile=SrcFile.hFile; NewFile=SrcFile.NewFile; LastWrite=SrcFile.LastWrite; HandleType=SrcFile.HandleType; wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); SrcFile.SkipClose=true; } bool File::Open(const wchar *Name,uint Mode) { ErrorType=FILE_SUCCESS; FileHandle hNewFile; bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; bool UpdateMode=(Mode & FMF_UPDATE)!=0; bool WriteMode=(Mode & FMF_WRITE)!=0; #ifdef _WIN_ALL uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; if (UpdateMode) Access|=GENERIC_WRITE; uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; if (OpenShared) ShareMode|=FILE_SHARE_WRITE; uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); DWORD LastError; if (hNewFile==FILE_BAD_HANDLE) { LastError=GetLastError(); wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) { hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); // For archive names longer than 260 characters first CreateFile // (without \\?\) fails and sets LastError to 3 (access denied). // We need the correct "file not found" error code to decide // if we create a new archive or quit with "cannot create" error. // So we need to check the error code after \\?\ CreateFile again, // otherwise we'll fail to create new archives with long names. // But we cannot simply assign the new code to LastError, // because it would break "..\arcname.rar" relative names processing. // First CreateFile returns the correct "file not found" code for such // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating // dots as a directory name. So we check only for "file not found" // error here and for other errors use the first CreateFile result. if (GetLastError()==ERROR_FILE_NOT_FOUND) LastError=ERROR_FILE_NOT_FOUND; } } if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) ErrorType=FILE_NOTFOUND; #else int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); #ifdef O_BINARY flags|=O_BINARY; #if defined(_AIX) && defined(_LARGE_FILE_API) flags|=O_LARGEFILE; #endif #endif char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); int handle=open(NameA,flags); #ifdef LOCK_EX #ifdef _OSF_SOURCE extern "C" int flock(int, int); #endif if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) { close(handle); return false; } #endif if (handle==-1) hNewFile=FILE_BAD_HANDLE; else { #ifdef FILE_USE_OPEN hNewFile=handle; #else hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); #endif } if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT) ErrorType=FILE_NOTFOUND; #endif NewFile=false; HandleType=FILE_HANDLENORMAL; SkipClose=false; bool Success=hNewFile!=FILE_BAD_HANDLE; if (Success) { hFile=hNewFile; wcsncpyz(FileName,Name,ASIZE(FileName)); } return Success; } #if !defined(SFX_MODULE) void File::TOpen(const wchar *Name) { if (!WOpen(Name)) ErrHandler.Exit(RARX_OPEN); } #endif bool File::WOpen(const wchar *Name) { if (Open(Name)) return true; ErrHandler.OpenErrorMsg(Name); return false; } bool File::Create(const wchar *Name,uint Mode) { // OpenIndiana based NAS and CIFS shares fail to set the file time if file // was created in read+write mode and some data was written and not flushed // before SetFileTime call. So we should use the write only mode if we plan // SetFileTime call and do not need to read from file. bool WriteMode=(Mode & FMF_WRITE)!=0; bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; #ifdef _WIN_ALL CreateMode=Mode; uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; // Windows automatically removes dots and spaces in the end of file name, // So we detect such names and process them with \\?\ prefix. wchar *LastChar=PointToLastChar(Name); bool Special=*LastChar=='.' || *LastChar==' '; if (Special && (Mode & FMF_STANDARDNAMES)==0) hFile=FILE_BAD_HANDLE; else hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); if (hFile==FILE_BAD_HANDLE) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); } #else char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); #ifdef FILE_USE_OPEN hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); #else hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); #endif #endif NewFile=true; HandleType=FILE_HANDLENORMAL; SkipClose=false; wcsncpyz(FileName,Name,ASIZE(FileName)); return hFile!=FILE_BAD_HANDLE; } #if !defined(SFX_MODULE) void File::TCreate(const wchar *Name,uint Mode) { if (!WCreate(Name,Mode)) ErrHandler.Exit(RARX_FATAL); } #endif bool File::WCreate(const wchar *Name,uint Mode) { if (Create(Name,Mode)) return true; ErrHandler.CreateErrorMsg(Name); return false; } bool File::Close() { bool Success=true; if (hFile!=FILE_BAD_HANDLE) { if (!SkipClose) { #ifdef _WIN_ALL // We use the standard system handle for stdout in Windows // and it must not be closed here. if (HandleType==FILE_HANDLENORMAL) Success=CloseHandle(hFile)==TRUE; #else #ifdef FILE_USE_OPEN Success=close(hFile)!=-1; #else Success=fclose(hFile)!=EOF; #endif #endif } hFile=FILE_BAD_HANDLE; } HandleType=FILE_HANDLENORMAL; if (!Success && AllowExceptions) ErrHandler.CloseError(FileName); return Success; } bool File::Delete() { if (HandleType!=FILE_HANDLENORMAL) return false; if (hFile!=FILE_BAD_HANDLE) Close(); if (!AllowDelete) return false; return DelFile(FileName); } bool File::Rename(const wchar *NewName) { // No need to rename if names are already same. bool Success=wcscmp(FileName,NewName)==0; if (!Success) Success=RenameFile(FileName,NewName); if (Success) wcscpy(FileName,NewName); return Success; } bool File::Write(const void *Data,size_t Size) { if (Size==0) return true; if (HandleType==FILE_HANDLESTD) { #ifdef _WIN_ALL hFile=GetStdHandle(STD_OUTPUT_HANDLE); #else // Cannot use the standard stdout here, because it already has wide orientation. if (hFile==FILE_BAD_HANDLE) { #ifdef FILE_USE_OPEN hFile=dup(STDOUT_FILENO); // Open new stdout stream. #else hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream. #endif } #endif } bool Success; while (1) { Success=false; #ifdef _WIN_ALL DWORD Written=0; if (HandleType!=FILE_HANDLENORMAL) { // writing to stdout can fail in old Windows if data block is too large const size_t MaxSize=0x4000; for (size_t I=0;ISize && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff) ErrHandler.WriteErrorFAT(FileName); #endif if (ErrHandler.AskRepeatWrite(FileName,false)) { #if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN) clearerr(hFile); #endif if (Written0) Seek(Tell()-Written,SEEK_SET); continue; } ErrHandler.WriteError(NULL,FileName); } break; } LastWrite=true; return Success; // It can return false only if AllowExceptions is disabled. } int File::Read(void *Data,size_t Size) { int64 FilePos=0; // Initialized only to suppress some compilers warning. if (IgnoreReadErrors) FilePos=Tell(); int ReadSize; while (true) { ReadSize=DirectRead(Data,Size); if (ReadSize==-1) { ErrorType=FILE_READERROR; if (AllowExceptions) if (IgnoreReadErrors) { ReadSize=0; for (size_t I=0;IMaxDeviceRead) // Size=MaxDeviceRead; hFile=GetStdHandle(STD_INPUT_HANDLE); #else #ifdef FILE_USE_OPEN hFile=STDIN_FILENO; #else hFile=stdin; #endif #endif } #ifdef _WIN_ALL // For pipes like 'type file.txt | rar -si arcname' ReadFile may return // data in small ~4KB blocks. It may slightly reduce the compression ratio. DWORD Read; if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) { if (IsDevice() && Size>MaxDeviceRead) return DirectRead(Data,MaxDeviceRead); if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) return 0; // We had a bug report about failure to archive 1C database lock file // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB // permanently locked. If our first read request uses too large buffer // and if we are in -dh mode, so we were able to open the file, // we'll fail with "Read error". So now we use try a smaller buffer size // in case of lock error. if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead && GetLastError()==ERROR_LOCK_VIOLATION) return DirectRead(Data,MaxLockedRead); return -1; } return Read; #else #ifdef FILE_USE_OPEN ssize_t ReadSize=read(hFile,Data,Size); if (ReadSize==-1) return -1; return (int)ReadSize; #else if (LastWrite) { fflush(hFile); LastWrite=false; } clearerr(hFile); size_t ReadSize=fread(Data,1,Size,hFile); if (ferror(hFile)) return -1; return (int)ReadSize; #endif #endif } void File::Seek(int64 Offset,int Method) { if (!RawSeek(Offset,Method) && AllowExceptions) ErrHandler.SeekError(FileName); } bool File::RawSeek(int64 Offset,int Method) { if (hFile==FILE_BAD_HANDLE) return true; if (Offset<0 && Method!=SEEK_SET) { Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; Method=SEEK_SET; } #ifdef _WIN_ALL LONG HighDist=(LONG)(Offset>>32); if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && GetLastError()!=NO_ERROR) return false; #else LastWrite=false; #ifdef FILE_USE_OPEN if (lseek(hFile,(off_t)Offset,Method)==-1) return false; #elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) if (fseeko(hFile,Offset,Method)!=0) return false; #else if (fseek(hFile,(long)Offset,Method)!=0) return false; #endif #endif return true; } int64 File::Tell() { if (hFile==FILE_BAD_HANDLE) if (AllowExceptions) ErrHandler.SeekError(FileName); else return -1; #ifdef _WIN_ALL LONG HighDist=0; uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) if (AllowExceptions) ErrHandler.SeekError(FileName); else return -1; return INT32TO64(HighDist,LowDist); #else #ifdef FILE_USE_OPEN return lseek(hFile,0,SEEK_CUR); #elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) return ftello(hFile); #else return ftell(hFile); #endif #endif } void File::Prealloc(int64 Size) { #ifdef _WIN_ALL if (RawSeek(Size,SEEK_SET)) { Truncate(); Seek(0,SEEK_SET); } #endif #if defined(_UNIX) && defined(USE_FALLOCATE) // fallocate is rather new call. Only latest kernels support it. // So we are not using it by default yet. int fd = GetFD(); if (fd >= 0) fallocate(fd, 0, 0, Size); #endif } byte File::GetByte() { byte Byte=0; Read(&Byte,1); return Byte; } void File::PutByte(byte Byte) { Write(&Byte,1); } bool File::Truncate() { #ifdef _WIN_ALL return SetEndOfFile(hFile)==TRUE; #else return ftruncate(GetFD(),(off_t)Tell())==0; #endif } void File::Flush() { #ifdef _WIN_ALL FlushFileBuffers(hFile); #else #ifndef FILE_USE_OPEN fflush(hFile); #endif fsync(GetFD()); #endif } void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) { #ifdef _WIN_ALL // Workaround for OpenIndiana NAS time bug. If we cannot create a file // in write only mode, we need to flush the write buffer before calling // SetFileTime or file time will not be changed. if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) FlushFileBuffers(hFile); bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); FILETIME fm,fc,fa; if (sm) ftm->GetWinFT(&fm); if (sc) ftc->GetWinFT(&fc); if (sa) fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); #endif } void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) { // Android APP_PLATFORM := android-14 does not support futimens and futimes. // Newer platforms support futimens, but fail on Android 4.2. // We have to use utime for Android. // Also we noticed futimens fail to set timestamps on NTFS partition // mounted to virtual Linux x86 machine, but utimensat worked correctly. // So we set timestamps for already closed files in Unix. #ifdef _UNIX SetCloseFileTimeByName(FileName,ftm,fta); #endif } void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) { #ifdef _UNIX bool setm=ftm!=NULL && ftm->IsSet(); bool seta=fta!=NULL && fta->IsSet(); if (setm || seta) { char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); #ifdef UNIX_TIME_NS timespec times[2]; times[0].tv_sec=seta ? fta->GetUnix() : 0; times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; times[1].tv_sec=setm ? ftm->GetUnix() : 0; times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; utimensat(AT_FDCWD,NameA,times,0); #else utimbuf ut; if (setm) ut.modtime=ftm->GetUnix(); else ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0. if (seta) ut.actime=fta->GetUnix(); else ut.actime=ut.modtime; // Need to set something, cannot left it 0. utime(NameA,&ut); #endif } #endif } void File::GetOpenFileTime(RarTime *ft) { #ifdef _WIN_ALL FILETIME FileTime; GetFileTime(hFile,NULL,NULL,&FileTime); ft->SetWinFT(&FileTime); #endif #if defined(_UNIX) || defined(_EMX) struct stat st; fstat(GetFD(),&st); ft->SetUnix(st.st_mtime); #endif } int64 File::FileLength() { SaveFilePos SavePos(*this); Seek(0,SEEK_END); return Tell(); } bool File::IsDevice() { if (hFile==FILE_BAD_HANDLE) return false; #ifdef _WIN_ALL uint Type=GetFileType(hFile); return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE; #else return isatty(GetFD()); #endif } #ifndef SFX_MODULE int64 File::Copy(File &Dest,int64 Length) { Array Buffer(File::CopyBufferSize()); int64 CopySize=0; bool CopyAll=(Length==INT64NDF); while (CopyAll || Length>0) { Wait(); size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); byte *Buf=&Buffer[0]; int ReadSize=Read(Buf,SizeToRead); if (ReadSize==0) break; size_t WriteSize=ReadSize; #ifdef _WIN_ALL // For FAT32 USB flash drives in Windows if first write is 4 KB or more, // write caching is disabled and "write through" is enabled, resulting // in bad performance, especially for many small files. It happens when // we create SFX archive on USB drive, because SFX module is written first. // So we split the first write to small 1 KB followed by rest of data. if (CopySize==0 && WriteSize>=4096) { const size_t FirstWrite=1024; Dest.Write(Buf,FirstWrite); Buf+=FirstWrite; WriteSize-=FirstWrite; } #endif Dest.Write(Buf,WriteSize); CopySize+=ReadSize; if (!CopyAll) Length-=ReadSize; } return CopySize; } #endif unrar/filefn.cpp000666 000000 000000 00000027520 13343205464 012340 0ustar00000000 000000 #include "rar.hpp" MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) { #ifdef _WIN_ALL // Windows automatically removes dots and spaces in the end of directory // name. So we detect such names and process them with \\?\ prefix. wchar *LastChar=PointToLastChar(Name); bool Special=*LastChar=='.' || *LastChar==' '; BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); if (RetCode==0 && !FileExist(Name)) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) RetCode=CreateDirectory(LongName,NULL); } if (RetCode!=0) // Non-zero return code means success for CreateDirectory. { if (SetAttr) SetFileAttr(Name,Attr); return MKDIR_SUCCESS; } int ErrCode=GetLastError(); if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) return MKDIR_BADPATH; return MKDIR_ERROR; #elif defined(_UNIX) char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); mode_t uattr=SetAttr ? (mode_t)Attr:0777; int ErrCode=mkdir(NameA,uattr); if (ErrCode==-1) return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; return MKDIR_SUCCESS; #else return MKDIR_ERROR; #endif } bool CreatePath(const wchar *Path,bool SkipLastName) { if (Path==NULL || *Path==0) return false; #if defined(_WIN_ALL) || defined(_EMX) uint DirAttr=0; #else uint DirAttr=0777; #endif bool Success=true; for (const wchar *s=Path;*s!=0;s++) { wchar DirName[NM]; if (s-Path>=ASIZE(DirName)) break; // Process all kinds of path separators, so user can enter Unix style // path in Windows or Windows in Unix. s>Path check avoids attempting // creating an empty directory for paths starting from path separator. if (IsPathDiv(*s) && s>Path) { #ifdef _WIN_ALL // We must not attempt to create "D:" directory, because first // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine // to create "D:" directory. if (s==Path+2 && Path[1]==':') continue; #endif wcsncpy(DirName,Path,s-Path); DirName[s-Path]=0; Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; if (Success) { mprintf(St(MCreatDir),DirName); mprintf(L" %s",St(MOk)); } } } if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; return Success; } void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) { #if defined(_WIN_ALL) bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); uint DirAttr=GetFileAttr(Name); bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); if (ResetAttr) SetFileAttr(Name,0); HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); } if (hFile==INVALID_HANDLE_VALUE) return; FILETIME fm,fc,fa; if (sm) ftm->GetWinFT(&fm); if (sc) ftc->GetWinFT(&fc); if (sa) fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); CloseHandle(hFile); if (ResetAttr) SetFileAttr(Name,DirAttr); #endif #if defined(_UNIX) || defined(_EMX) File::SetCloseFileTimeByName(Name,ftm,fta); #endif } bool IsRemovable(const wchar *Name) { #if defined(_WIN_ALL) wchar Root[NM]; GetPathRoot(Name,Root,ASIZE(Root)); int Type=GetDriveType(*Root!=0 ? Root:NULL); return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; #else return false; #endif } #ifndef SFX_MODULE int64 GetFreeDisk(const wchar *Name) { #ifdef _WIN_ALL wchar Root[NM]; GetFilePath(Name,Root,ASIZE(Root)); ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); return 0; #elif defined(_UNIX) wchar Root[NM]; GetFilePath(Name,Root,ASIZE(Root)); char RootA[NM]; WideToChar(Root,RootA,ASIZE(RootA)); struct statvfs sfs; if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) return 0; int64 FreeSize=sfs.f_bsize; FreeSize=FreeSize*sfs.f_bavail; return FreeSize; #else return 0; #endif } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) // Return 'true' for FAT and FAT32, so we can adjust the maximum supported // file size to 4 GB for these file systems. bool IsFAT(const wchar *Name) { wchar Root[NM]; GetPathRoot(Name,Root,ASIZE(Root)); wchar FileSystem[MAX_PATH+1]; if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; return false; } #endif bool FileExist(const wchar *Name) { #ifdef _WIN_ALL return GetFileAttr(Name)!=0xffffffff; #elif defined(ENABLE_ACCESS) char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); return access(NameA,0)==0; #else FindData FD; return FindFile::FastFind(Name,&FD); #endif } bool WildFileExist(const wchar *Name) { if (IsWildcard(Name)) { FindFile Find; Find.SetMask(Name); FindData fd; return Find.Next(&fd); } return FileExist(Name); } bool IsDir(uint Attr) { #ifdef _WIN_ALL return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; #endif #if defined(_UNIX) return (Attr & 0xF000)==0x4000; #endif } bool IsUnreadable(uint Attr) { #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); #endif return false; } bool IsLink(uint Attr) { #ifdef _UNIX return (Attr & 0xF000)==0xA000; #elif defined(_WIN_ALL) return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; #else return false; #endif } bool IsDeleteAllowed(uint FileAttr) { #ifdef _WIN_ALL return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; #else return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); #endif } void PrepareToDelete(const wchar *Name) { #if defined(_WIN_ALL) || defined(_EMX) SetFileAttr(Name,0); #endif #ifdef _UNIX if (Name!=NULL) { char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); } #endif } uint GetFileAttr(const wchar *Name) { #ifdef _WIN_ALL DWORD Attr=GetFileAttributes(Name); if (Attr==0xffffffff) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) Attr=GetFileAttributes(LongName); } return Attr; #else char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); struct stat st; if (stat(NameA,&st)!=0) return 0; return st.st_mode; #endif } bool SetFileAttr(const wchar *Name,uint Attr) { #ifdef _WIN_ALL bool Success=SetFileAttributes(Name,Attr)!=0; if (!Success) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) Success=SetFileAttributes(LongName,Attr)!=0; } return Success; #elif defined(_UNIX) char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); return chmod(NameA,(mode_t)Attr)==0; #else return false; #endif } #if 0 wchar *MkTemp(wchar *Name,size_t MaxSize) { size_t Length=wcslen(Name); RarTime CurTime; CurTime.SetCurrentTime(); // We cannot use CurTime.GetWin() as is, because its lowest bits can // have low informational value, like being a zero or few fixed numbers. uint Random=(uint)(CurTime.GetWin()/100000); // Using PID we guarantee that different RAR copies use different temp names // even if started in exactly the same time. uint PID=0; #ifdef _WIN_ALL PID=(uint)GetCurrentProcessId(); #elif defined(_UNIX) PID=(uint)getpid(); #endif for (uint Attempt=0;;Attempt++) { uint Ext=Random%50000+Attempt; wchar RndText[50]; swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) return NULL; wcscpy(Name+Length,RndText); if (!FileExist(Name)) break; } return Name; } #endif #if !defined(SFX_MODULE) void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) { SaveFilePos SavePos(*SrcFile); #ifndef SILENT int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; #endif if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) uiMsg(UIEVENT_FILESUMSTART); if ((Flags & CALCFSUM_CURPOS)==0) SrcFile->Seek(0,SEEK_SET); const size_t BufSize=0x100000; Array Data(BufSize); DataHash HashCRC,HashBlake2; HashCRC.Init(HASH_CRC32,Threads); HashBlake2.Init(HASH_BLAKE2,Threads); int64 BlockCount=0; int64 TotalRead=0; while (true) { size_t SizeToRead; if (Size==INT64NDF) // If we process the entire file. SizeToRead=BufSize; // Then always attempt to read the entire buffer. else SizeToRead=(size_t)Min((int64)BufSize,Size); int ReadSize=SrcFile->Read(&Data[0],SizeToRead); if (ReadSize==0) break; TotalRead+=ReadSize; if ((++BlockCount & 0xf)==0) { #ifndef SILENT if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength); else { if ((Flags & CALCFSUM_SHOWPERCENT)!=0) uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); } #endif Wait(); } if (CRC32!=NULL) HashCRC.Update(&Data[0],ReadSize); if (Blake2!=NULL) HashBlake2.Update(&Data[0],ReadSize); if (Size!=INT64NDF) Size-=ReadSize; } if ((Flags & CALCFSUM_SHOWPERCENT)!=0) uiMsg(UIEVENT_FILESUMEND); if (CRC32!=NULL) *CRC32=HashCRC.GetCRC32(); if (Blake2!=NULL) { HashValue Result; HashBlake2.Result(&Result); memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); } } #endif bool RenameFile(const wchar *SrcName,const wchar *DestName) { #ifdef _WIN_ALL bool Success=MoveFile(SrcName,DestName)!=0; if (!Success) { wchar LongName1[NM],LongName2[NM]; if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) Success=MoveFile(LongName1,LongName2)!=0; } return Success; #else char SrcNameA[NM],DestNameA[NM]; WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); WideToChar(DestName,DestNameA,ASIZE(DestNameA)); bool Success=rename(SrcNameA,DestNameA)==0; return Success; #endif } bool DelFile(const wchar *Name) { #ifdef _WIN_ALL bool Success=DeleteFile(Name)!=0; if (!Success) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) Success=DeleteFile(LongName)!=0; } return Success; #else char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); bool Success=remove(NameA)==0; return Success; #endif } #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool SetFileCompression(const wchar *Name,bool State) { HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hFile==INVALID_HANDLE_VALUE) { wchar LongName[NM]; if (GetWinLongPath(Name,LongName,ASIZE(LongName))) hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); } if (hFile==INVALID_HANDLE_VALUE) return false; SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; DWORD Result; int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, sizeof(NewState),NULL,0,&Result,NULL); CloseHandle(hFile); return RetCode!=0; } #endif unrar/filestr.cpp000666 000000 000000 00000010076 13343205464 012543 0ustar00000000 000000 #include "rar.hpp" bool ReadTextFile( const wchar *Name, StringList *List, bool Config, bool AbortOnError, RAR_CHARSET SrcCharset, bool Unquote, bool SkipComments, bool ExpandEnvStr) { wchar FileName[NM]; *FileName=0; if (Name!=NULL) if (Config) GetConfigName(Name,FileName,ASIZE(FileName),true,false); else wcsncpyz(FileName,Name,ASIZE(FileName)); File SrcFile; if (*FileName!=0) { bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); if (!OpenCode) { if (AbortOnError) ErrHandler.Exit(RARX_OPEN); return false; } } else SrcFile.SetHandleType(FILE_HANDLESTD); uint DataSize=0,ReadSize; const int ReadBlock=4096; Array Data(ReadBlock); while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) { DataSize+=ReadSize; Data.Add(ReadSize); // Always have ReadBlock available for next data. } // Set to really read size, so we can zero terminate it correctly. Data.Alloc(DataSize); int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; if (SrcCharset==RCH_DEFAULT) SrcCharset=DetectTextEncoding(&Data[0],DataSize); Array DataW; if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) { Data.Push(0); // Zero terminate. #if defined(_WIN_ALL) if (SrcCharset==RCH_OEM) OemToCharA((char *)&Data[0],(char *)&Data[0]); #endif DataW.Alloc(Data.Size()); CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); } if (SrcCharset==RCH_UNICODE) { size_t Start=2; // Skip byte order mark. if (!LittleEndian && !BigEndian) // No byte order mask. { Start=0; LittleEndian=1; } DataW.Alloc(Data.Size()/2+1); size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. for (size_t I=Start;I=CurStr;SpacePtr--) { if (*SpacePtr!=' ' && *SpacePtr!='\t') break; *SpacePtr=0; } if (Unquote && *CurStr=='\"') { size_t Length=wcslen(CurStr); if (CurStr[Length-1]=='\"') { CurStr[Length-1]=0; CurStr++; } } bool Expanded=false; #if defined(_WIN_ALL) if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows. { wchar ExpName[NM]; *ExpName=0; DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); Expanded=Result!=0 && ResultAddString(ExpName); } #endif if (!Expanded && *CurStr!=0) List->AddString(CurStr); if (Done) break; CurStr=NextStr+1; while (*CurStr=='\r' || *CurStr=='\n') CurStr++; } return true; } RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize) { if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf && IsTextUtf8(Data+3,DataSize-3)) return RCH_UTF8; bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254; bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255; if (LittleEndian || BigEndian) for (size_t I=LittleEndian ? 3 : 2;IError=false; if (*FindMask==0) return false; #ifdef _WIN_ALL if (FirstCall) { if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE) return false; } else if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE) return false; #else if (FirstCall) { wchar DirName[NM]; wcsncpyz(DirName,FindMask,ASIZE(DirName)); RemoveNameFromPath(DirName); if (*DirName==0) wcscpy(DirName,L"."); char DirNameA[NM]; WideToChar(DirName,DirNameA,ASIZE(DirNameA)); if ((dirp=opendir(DirNameA))==NULL) { fd->Error=(errno!=ENOENT); return false; } } while (1) { struct dirent *ent=readdir(dirp); if (ent==NULL) return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; wchar Name[NM]; if (!CharToWide(ent->d_name,Name,ASIZE(Name))) uiMsg(UIERROR_INVALIDNAME,UINULL,Name); if (CmpName(FindMask,Name,MATCH_NAMES)) { wchar FullName[NM]; wcscpy(FullName,FindMask); *PointToName(FullName)=0; if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) { uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); return false; } wcscat(FullName,Name); if (!FastFind(FullName,fd,GetSymLink)) { ErrHandler.OpenErrorMsg(FullName); continue; } wcscpy(fd->Name,FullName); break; } } #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); fd->IsLink=IsLink(fd->FileAttr); FirstCall=false; wchar *NameOnly=PointToName(fd->Name); if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) return Next(fd); return true; } bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) { fd->Error=false; #ifndef _UNIX if (IsWildcard(FindMask)) return false; #endif #ifdef _WIN_ALL HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd); if (hFind==INVALID_HANDLE_VALUE) return false; FindClose(hFind); #else char FindMaskA[NM]; WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); struct stat st; if (GetSymLink) { #ifdef SAVE_LINKS if (lstat(FindMaskA,&st)!=0) #else if (stat(FindMaskA,&st)!=0) #endif { fd->Error=(errno!=ENOENT); return false; } } else if (stat(FindMaskA,&st)!=0) { fd->Error=(errno!=ENOENT); return false; } fd->FileAttr=st.st_mode; fd->Size=st.st_size; #ifdef UNIX_TIME_NS fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); #else fd->mtime.SetUnix(st.st_mtime); fd->atime.SetUnix(st.st_atime); fd->ctime.SetUnix(st.st_ctime); #endif wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); fd->IsLink=IsLink(fd->FileAttr); return true; } #ifdef _WIN_ALL HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) { WIN32_FIND_DATA FindData; if (hFind==INVALID_HANDLE_VALUE) { hFind=FindFirstFile(Mask,&FindData); if (hFind==INVALID_HANDLE_VALUE) { wchar LongMask[NM]; if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) hFind=FindFirstFile(LongMask,&FindData); } if (hFind==INVALID_HANDLE_VALUE) { int SysErr=GetLastError(); // We must not issue an error for "file not found" and "path not found", // because it is normal to not find anything for wildcard mask when // archiving. Also searching for non-existent file is normal in some // other modules, like WinRAR scanning for winrar_theme_description.txt // to check if any themes are available. fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND && SysErr!=ERROR_NO_MORE_FILES; } } else if (!FindNextFile(hFind,&FindData)) { hFind=INVALID_HANDLE_VALUE; fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; } if (hFind!=INVALID_HANDLE_VALUE) { wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); fd->FileAttr=FindData.dwFileAttributes; fd->ftCreationTime=FindData.ftCreationTime; fd->ftLastAccessTime=FindData.ftLastAccessTime; fd->ftLastWriteTime=FindData.ftLastWriteTime; fd->mtime.SetWinFT(&FindData.ftLastWriteTime); fd->ctime.SetWinFT(&FindData.ftCreationTime); fd->atime.SetWinFT(&FindData.ftLastAccessTime); } fd->Flags=0; return hFind; } #endif unrar/getbits.cpp000666 000000 000000 00000002077 13343205464 012536 0ustar00000000 000000 #include "rar.hpp" BitInput::BitInput(bool AllocBuffer) { ExternalBuffer=false; if (AllocBuffer) { // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. // So let's allocate 3 additional bytes for situation, when we need to // read only 1 byte from the last position of buffer and avoid a crash // from access to next 3 bytes, which contents we do not need. size_t BufSize=MAX_SIZE+3; InBuf=new byte[BufSize]; // Ensure that we get predictable results when accessing bytes in area // not filled with read data. memset(InBuf,0,BufSize); } else InBuf=NULL; } BitInput::~BitInput() { if (!ExternalBuffer) delete[] InBuf; } void BitInput::faddbits(uint Bits) { // Function wrapped version of inline addbits to save code size. addbits(Bits); } uint BitInput::fgetbits() { // Function wrapped version of inline getbits to save code size. return getbits(); } void BitInput::SetExternalBuffer(byte *Buf) { if (InBuf!=NULL && !ExternalBuffer) delete[] InBuf; InBuf=Buf; ExternalBuffer=true; } unrar/global.cpp000666 000000 000000 00000000161 13343205464 012325 0ustar00000000 000000 #define INCLUDEGLOBAL #if defined(__BORLANDC__) || defined(_MSC_VER) #pragma hdrstop #endif #include "rar.hpp" unrar/hardlinks.cpp000666 000000 000000 00000001770 13343205464 013053 0ustar00000000 000000 bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) { SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. if (!FileExist(NameExisting)) { uiMsg(UIERROR_HLINKCREATE,NameNew); uiMsg(UIERROR_NOLINKTARGET); ErrHandler.SetErrorCode(RARX_CREATE); return false; } CreatePath(NameNew,true); #ifdef _WIN_ALL bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CREATE); } return Success; #elif defined(_UNIX) char NameExistingA[NM],NameNewA[NM]; WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); bool Success=link(NameExistingA,NameNewA)==0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CREATE); } return Success; #else return false; #endif } unrar/hash.cpp000666 000000 000000 00000006222 13343205464 012014 0ustar00000000 000000 #include "rar.hpp" void HashValue::Init(HASH_TYPE Type) { HashValue::Type=Type; // Zero length data CRC32 is 0. It is important to set it when creating // headers with no following data like directories or symlinks. if (Type==HASH_RAR14 || Type==HASH_CRC32) CRC32=0; if (Type==HASH_BLAKE2) { // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f // is BLAKE2sp hash of empty data. We init the structure to this value, // so if we create a file or service header with no following data like // "file copy" or "symlink", we set the checksum to proper value avoiding // additional header type or size checks when extracting. static byte EmptyHash[32]={ 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43, 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25, 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1, 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f }; memcpy(Digest,EmptyHash,sizeof(Digest)); } } bool HashValue::operator == (const HashValue &cmp) { if (Type==HASH_NONE || cmp.Type==HASH_NONE) return true; if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 || Type==HASH_CRC32 && cmp.Type==HASH_CRC32) return CRC32==cmp.CRC32; if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2) return memcmp(Digest,cmp.Digest,sizeof(Digest))==0; return false; } DataHash::DataHash() { blake2ctx=NULL; HashType=HASH_NONE; #ifdef RAR_SMP ThPool=NULL; MaxThreads=0; #endif } DataHash::~DataHash() { #ifdef RAR_SMP DestroyThreadPool(ThPool); #endif cleandata(&CurCRC32, sizeof(CurCRC32)); if (blake2ctx!=NULL) { cleandata(blake2ctx, sizeof(blake2sp_state)); delete blake2ctx; } } void DataHash::Init(HASH_TYPE Type,uint MaxThreads) { if (blake2ctx==NULL) blake2ctx=new blake2sp_state; HashType=Type; if (Type==HASH_RAR14) CurCRC32=0; if (Type==HASH_CRC32) CurCRC32=0xffffffff; // Initial CRC32 value. if (Type==HASH_BLAKE2) blake2sp_init(blake2ctx); #ifdef RAR_SMP DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); #endif } void DataHash::Update(const void *Data,size_t DataSize) { #ifndef SFX_MODULE if (HashType==HASH_RAR14) CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); #endif if (HashType==HASH_CRC32) CurCRC32=CRC32(CurCRC32,Data,DataSize); if (HashType==HASH_BLAKE2) { #ifdef RAR_SMP if (MaxThreads>1 && ThPool==NULL) ThPool=CreateThreadPool(); blake2ctx->ThPool=ThPool; blake2ctx->MaxThreads=MaxThreads; #endif blake2sp_update( blake2ctx, (byte *)Data, DataSize); } } void DataHash::Result(HashValue *Result) { Result->Type=HashType; if (HashType==HASH_RAR14) Result->CRC32=CurCRC32; if (HashType==HASH_CRC32) Result->CRC32=CurCRC32^0xffffffff; if (HashType==HASH_BLAKE2) { // Preserve the original context, so we can continue hashing if necessary. blake2sp_state res=*blake2ctx; blake2sp_final(&res,Result->Digest); } } uint DataHash::GetCRC32() { return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0; } bool DataHash::Cmp(HashValue *CmpValue,byte *Key) { HashValue Final; Result(&Final); if (Key!=NULL) ConvertHashToMAC(&Final,Key); return Final==*CmpValue; } unrar/headers.cpp000666 000000 000000 00000001741 13343205464 012505 0ustar00000000 000000 #include "rar.hpp" void FileHeader::Reset(size_t SubDataSize) { SubData.Alloc(SubDataSize); BaseBlock::Reset(); FileHash.Init(HASH_NONE); mtime.Reset(); atime.Reset(); ctime.Reset(); SplitBefore=false; SplitAfter=false; UnknownUnpSize=0; SubFlags=0; // Important for RAR 3.0 subhead. CryptMethod=CRYPT_NONE; Encrypted=false; SaltSet=false; UsePswCheck=false; UseHashKey=false; Lg2Count=0; Solid=false; Dir=false; WinSize=0; Inherited=false; SubBlock=false; CommentInHeader=false; Version=false; LargeFile=false; RedirType=FSREDIR_NONE; DirTarget=false; UnixOwnerSet=false; } FileHeader& FileHeader::operator = (FileHeader &hd) { SubData.Reset(); memcpy(this,&hd,sizeof(*this)); SubData.CleanData(); SubData=hd.SubData; return *this; } void MainHeader::Reset() { HighPosAV=0; PosAV=0; CommentInHeader=false; PackComment=false; Locator=false; QOpenOffset=0; QOpenMaxSize=0; RROffset=0; RRMaxSize=0; } unrar/isnt.cpp000666 000000 000000 00000001007 13343205464 012042 0ustar00000000 000000 #include "rar.hpp" #ifdef _WIN_ALL DWORD WinNT() { static int dwPlatformId=-1; static DWORD dwMajorVersion,dwMinorVersion; if (dwPlatformId==-1) { OSVERSIONINFO WinVer; WinVer.dwOSVersionInfoSize=sizeof(WinVer); GetVersionEx(&WinVer); dwPlatformId=WinVer.dwPlatformId; dwMajorVersion=WinVer.dwMajorVersion; dwMinorVersion=WinVer.dwMinorVersion; } DWORD Result=0; if (dwPlatformId==VER_PLATFORM_WIN32_NT) Result=dwMajorVersion*0x100+dwMinorVersion; return Result; } #endif unrar/list.cpp000666 000000 000000 00000035006 13343205464 012046 0ustar00000000 000000 #include "rar.hpp" static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare); static void ListSymLink(Archive &Arc); static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); static void ListOldSubHeader(Archive &Arc); static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); void ListArchive(CommandData *Cmd) { int64 SumPackSize=0,SumUnpSize=0; uint ArcCount=0,SumFileCount=0; bool Technical=(Cmd->Command[1]=='T'); bool ShowService=Technical && Cmd->Command[2]=='A'; bool Bare=(Cmd->Command[1]=='B'); bool Verbose=(Cmd->Command[0]=='V'); wchar ArcName[NM]; while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. Archive Arc(Cmd); #ifdef _WIN_ALL Arc.RemoveSequentialFlag(); #endif if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; while (1) { int64 TotalPackSize=0,TotalUnpSize=0; uint FileCount=0; if (Arc.IsArchive(true)) { bool TitleShown=false; if (!Bare) { Arc.ViewComment(); mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); mprintf(L"\n%s: ",St(MListDetails)); uint SetCount=0; const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); if (Arc.Solid) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); if (Arc.SFXSize>0) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); if (Arc.Volume) if (Arc.Format==RARFMT50) { // RAR 5.0 archives store the volume number in main header, // so it is already available now. if (SetCount++ > 0) mprintf(L", "); mprintf(St(MVolumeNumber),Arc.VolNumber+1); } else mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); if (Arc.Protected) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); if (Arc.Locked) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); if (Arc.Encrypted) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); mprintf(L"\n"); } wchar VolNumText[50]; *VolNumText=0; while(Arc.ReadHeader()>0) { HEADER_TYPE HeaderType=Arc.GetHeaderType(); if (HeaderType==HEAD_ENDARC) { #ifndef SFX_MODULE // Only RAR 1.5 archives store the volume number in end record. if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15) swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1); #endif if (Technical && ShowService) { mprintf(L"\n%12ls: %ls",St(MListService),L"EOF"); if (*VolNumText!=0) mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText); mprintf(L"\n"); } break; } switch(HeaderType) { case HEAD_FILE: FileMatched=Cmd->IsProcessFile(Arc.FileHead)!=0; if (FileMatched) { ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare); if (!Arc.FileHead.SplitBefore) { TotalUnpSize+=Arc.FileHead.UnpSize; FileCount++; } TotalPackSize+=Arc.FileHead.PackSize; } break; case HEAD_SERVICE: if (FileMatched && !Bare) { if (Technical && ShowService) ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false); } break; } Arc.SeekToNext(); } if (!Bare && !Technical) if (TitleShown) { wchar UnpSizeText[20]; itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText)); wchar PackSizeText[20]; itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText)); if (Verbose) { mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), VolNumText,FileCount); } else { mprintf(L"\n----------- --------- ---------- ----- ----"); mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); } SumFileCount+=FileCount; SumUnpSize+=TotalUnpSize; SumPackSize+=TotalPackSize; mprintf(L"\n"); } else mprintf(St(MListNoFiles)); ArcCount++; #ifndef NOVOLUME if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && MergeArchive(Arc,NULL,false,Cmd->Command[0])) Arc.Seek(0,SEEK_SET); else #endif break; } else { if (Cmd->ArcNames.ItemsCount()<2 && !Bare) mprintf(St(MNotRAR),Arc.FileName); break; } } } // Clean user entered password. Not really required, just for extra safety. if (Cmd->ManualPassword) Cmd->Password.Clean(); if (ArcCount>1 && !Bare && !Technical) { wchar UnpSizeText[20],PackSizeText[20]; itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText)); itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText)); if (Verbose) mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText, ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount); else mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount); } } enum LISTCOL_TYPE { LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR }; void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare) { wchar *Name=hd.FileName; RARFORMAT Format=Arc.Format; if (Bare) { mprintf(L"%s\n",Name); return; } if (!TitleShown && !Technical) { if (Verbose) { mprintf(L"\n%ls",St(MListTitleV)); mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); } else { mprintf(L"\n%ls",St(MListTitleL)); mprintf(L"\n----------- --------- ---------- ----- ----"); } TitleShown=true; } wchar UnpSizeText[30],PackSizeText[30]; if (hd.UnpSize==INT64NDF) wcscpy(UnpSizeText,L"?"); else itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText)); itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText)); wchar AttrStr[30]; if (hd.HeaderType==HEAD_SERVICE) swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.'); else ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr)); wchar RatioStr[10]; if (hd.SplitBefore && hd.SplitAfter) wcscpy(RatioStr,L"<->"); else if (hd.SplitBefore) wcscpy(RatioStr,L"<--"); else if (hd.SplitAfter) wcscpy(RatioStr,L"-->"); else swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); wchar DateStr[50]; hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); if (Technical) { mprintf(L"\n%12s: %s",St(MListName),Name); bool FileBlock=hd.HeaderType==HEAD_FILE; if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) { mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); wchar StreamName[NM]; GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); } else { const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService); if (hd.RedirType!=FSREDIR_NONE) switch(hd.RedirType) { case FSREDIR_UNIXSYMLINK: Type=St(MListUSymlink); break; case FSREDIR_WINSYMLINK: Type=St(MListWSymlink); break; case FSREDIR_JUNCTION: Type=St(MListJunction); break; case FSREDIR_HARDLINK: Type=St(MListHardlink); break; case FSREDIR_FILECOPY: Type=St(MListCopy); break; } mprintf(L"\n%12ls: %ls",St(MListType),Type); if (hd.RedirType!=FSREDIR_NONE) if (Format==RARFMT15) { char LinkTargetA[NM]; if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); } else { int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); Arc.Read(LinkTargetA,DataSize); LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; } wchar LinkTarget[NM]; CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); } else mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); } if (!hd.Dir) { mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); } if (hd.mtime.IsSet()) mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr); if (hd.ctime.IsSet()) { hd.ctime.GetText(DateStr,ASIZE(DateStr),true); mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr); } if (hd.atime.IsSet()) { hd.atime.GetText(DateStr,ASIZE(DateStr),true); mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr); } mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); if (hd.FileHash.Type==HASH_CRC32) mprintf(L"\n%12ls: %8.8X", hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32", hd.FileHash.CRC32); if (hd.FileHash.Type==HASH_BLAKE2) { wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); mprintf(L"\n%12ls: %ls", hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", BlakeStr); } const wchar *HostOS=L""; if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN) HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix"; if (Format==RARFMT15) { static const wchar *RarOS[]={ L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L"" }; if (hd.HostOS=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, hd.WinSize>=0x100000 ? L"M":L"K"); if (hd.Solid || hd.Encrypted) { mprintf(L"\n%12ls: ",St(MListFlags)); if (hd.Solid) mprintf(L"%ls ",St(MListSolid)); if (hd.Encrypted) mprintf(L"%ls ",St(MListEnc)); } if (hd.Version) { uint Version=ParseVersionFileName(Name,false); if (Version!=0) mprintf(L"\n%12ls: %u",St(MListFileVer),Version); } if (hd.UnixOwnerSet) { mprintf(L"\n%12ls: ",L"Unix owner"); if (*hd.UnixOwnerName!=0) mprintf(L"%ls:",GetWide(hd.UnixOwnerName)); if (*hd.UnixGroupName!=0) mprintf(L"%ls",GetWide(hd.UnixGroupName)); if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric)) mprintf(L" "); if (hd.UnixOwnerNumeric) mprintf(L"#%d:",hd.UnixOwnerID); if (hd.UnixGroupNumeric) mprintf(L"#%d:",hd.UnixGroupID); } mprintf(L"\n"); return; } mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); if (Verbose) mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); mprintf(L" %ls ",DateStr); if (Verbose) { if (hd.FileHash.Type==HASH_CRC32) mprintf(L"%8.8X ",hd.FileHash.CRC32); else if (hd.FileHash.Type==HASH_BLAKE2) { byte *S=hd.FileHash.Digest; mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); } else mprintf(L"???????? "); } mprintf(L"%ls",Name); } /* void ListSymLink(Archive &Arc) { if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); } else { char FileName[NM]; uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); Arc.Read(FileName,DataSize); FileName[DataSize]=0; mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); } } */ void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) { switch(HostType) { case HSYS_WINDOWS: swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c", (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed. (A & 0x0800)!=0 ? 'C' : '.', // Compressed. (A & 0x0020)!=0 ? 'A' : '.', // Archive. (A & 0x0010)!=0 ? 'D' : '.', // Directory. (A & 0x0004)!=0 ? 'S' : '.', // System. (A & 0x0002)!=0 ? 'H' : '.', // Hidden. (A & 0x0001)!=0 ? 'R' : '.'); // Read-only. break; case HSYS_UNIX: switch (A & 0xF000) { case 0x4000: AttrStr[0]='d'; break; case 0xA000: AttrStr[0]='l'; break; default: AttrStr[0]='-'; break; } swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c", (A & 0x0100) ? 'r' : '-', (A & 0x0080) ? 'w' : '-', (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'), (A & 0x0020) ? 'r' : '-', (A & 0x0010) ? 'w' : '-', (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'), (A & 0x0004) ? 'r' : '-', (A & 0x0002) ? 'w' : '-', (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-'); break; case HSYS_UNKNOWN: wcscpy(AttrStr,L"?"); break; } } unrar/log.cpp000666 000000 000000 00000001456 13343205464 011656 0ustar00000000 000000 #include "rar.hpp" static wchar LogName[NM]; static RAR_CHARSET LogCharset=RCH_DEFAULT; void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet) { wcsncpyz(LogName,LogFileName,ASIZE(LogName)); LogCharset=CSet; } #ifndef SILENT void Log(const wchar *ArcName,const wchar *fmt,...) { // Preserve the error code for possible following system error message. int Code=ErrHandler.GetSystemErrorCode(); uiAlarm(UIALARM_ERROR); // This buffer is for format string only, not for entire output, // so it can be short enough. wchar fmtw[1024]; PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); safebuf wchar Msg[2*NM+1024]; va_list arglist; va_start(arglist,fmt); vswprintf(Msg,ASIZE(Msg),fmtw,arglist); va_end(arglist); eprintf(L"%ls",Msg); ErrHandler.SetSystemErrorCode(Code); } #endif unrar/match.cpp000666 000000 000000 00000007472 13343205465 012176 0ustar00000000 000000 #include "rar.hpp" static bool match(const wchar *pattern,const wchar *string,bool ForceCase); static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase); static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); inline uint touppercw(uint ch,bool ForceCase) { if (ForceCase) return ch; #if defined(_UNIX) return ch; #else return toupperw(ch); #endif } bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode) { bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0; CmpMode&=MATCH_MODEMASK; if (CmpMode!=MATCH_NAMES) { size_t WildLength=wcslen(Wildcard); if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0) { // For all modes except MATCH_NAMES, MATCH_EXACT and MATCH_EXACTPATH // "path1" mask must match "path1\path2\filename.ext" and "path1" names. wchar NextCh=Name[WildLength]; if (NextCh==L'\\' || NextCh==L'/' || NextCh==0) return(true); } // Nothing more to compare for MATCH_SUBPATHONLY. if (CmpMode==MATCH_SUBPATHONLY) return(false); wchar Path1[NM],Path2[NM]; GetFilePath(Wildcard,Path1,ASIZE(Path1)); GetFilePath(Name,Path2,ASIZE(Path2)); if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH) if (IsWildcard(Path1)) return(match(Wildcard,Name,ForceCase)); else if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard)) { if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0) return(false); } else if (mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); } wchar *Name1=PointToName(Wildcard); wchar *Name2=PointToName(Name); // Always return false for RAR temporary files to exclude them // from archiving operations. if (mwcsnicompc(L"__rar_",Name2,6,false)==0) return(false); if (CmpMode==MATCH_EXACT) return(mwcsicompc(Name1,Name2,ForceCase)==0); return(match(Name1,Name2,ForceCase)); } bool match(const wchar *pattern,const wchar *string,bool ForceCase) { for (;; ++string) { wchar stringc=touppercw(*string,ForceCase); wchar patternc=touppercw(*pattern++,ForceCase); switch (patternc) { case 0: return(stringc==0); case '?': if (stringc == 0) return(false); break; case '*': if (*pattern==0) return(true); if (*pattern=='.') { if (pattern[1]=='*' && pattern[2]==0) return(true); const wchar *dot=wcschr(string,'.'); if (pattern[1]==0) return (dot==NULL || dot[1]==0); if (dot!=NULL) { string=dot; if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL) return(mwcsicompc(pattern+1,string+1,ForceCase)==0); } } while (*string) if (match(pattern,string++,ForceCase)) return(true); return(false); default: if (patternc != stringc) { // Allow "name." mask match "name" and "name.\" match "name\". if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.')) return(match(pattern,string,ForceCase)); else return(false); } break; } } } int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase) { if (ForceCase) return wcscmp(Str1,Str2); return wcsicompc(Str1,Str2); } int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) { if (ForceCase) return wcsncmp(Str1,Str2,N); #if defined(_UNIX) return wcsncmp(Str1,Str2,N); #else return wcsnicomp(Str1,Str2,N); #endif } unrar/model.cpp000666 000000 000000 00000037202 13343205465 012174 0ustar00000000 000000 /**************************************************************************** * This file is part of PPMd project * * Written and distributed to public domain by Dmitry Shkarin 1997, * * 1999-2000 * * Contents: model description and encoding/decoding routines * ****************************************************************************/ static const int MAX_O=64; /* maximum allowed model order */ const uint TOP=1 << 24, BOT=1 << 15; template inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats, RARPPM_STATE& FirstState) { RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext(); if ( pc ) { pc->NumStats=1; pc->OneState=FirstState; pc->Suffix=this; pStats->Successor=pc; } return pc; } ModelPPM::ModelPPM() { MinContext=NULL; MaxContext=NULL; MedContext=NULL; } void ModelPPM::RestartModelRare() { int i, k, m; memset(CharMask,0,sizeof(CharMask)); SubAlloc.InitSubAllocator(); InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1; MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext(); if (MinContext == NULL) throw std::bad_alloc(); MinContext->Suffix=NULL; OrderFall=MaxOrder; MinContext->U.SummFreq=(MinContext->NumStats=256)+1; FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2); if (FoundState == NULL) throw std::bad_alloc(); for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++) { MinContext->U.Stats[i].Symbol=i; MinContext->U.Stats[i].Freq=1; MinContext->U.Stats[i].Successor=NULL; } static const ushort InitBinEsc[]={ 0x3CDD,0x1F3F,0x59BF,0x48F3,0x64A1,0x5ABC,0x6632,0x6051 }; for (i=0;i < 128;i++) for (k=0;k < 8;k++) for (m=0;m < 64;m += 8) BinSumm[i][k+m]=BIN_SCALE-InitBinEsc[k]/(i+2); for (i=0;i < 25;i++) for (k=0;k < 16;k++) SEE2Cont[i][k].init(5*i+10); } void ModelPPM::StartModelRare(int MaxOrder) { int i, k, m ,Step; EscCount=1; /* if (MaxOrder < 2) { memset(CharMask,0,sizeof(CharMask)); OrderFall=ModelPPM::MaxOrder; MinContext=MaxContext; while (MinContext->Suffix != NULL) { MinContext=MinContext->Suffix; OrderFall--; } FoundState=MinContext->U.Stats; MinContext=MaxContext; } else */ { ModelPPM::MaxOrder=MaxOrder; RestartModelRare(); NS2BSIndx[0]=2*0; NS2BSIndx[1]=2*1; memset(NS2BSIndx+2,2*2,9); memset(NS2BSIndx+11,2*3,256-11); for (i=0;i < 3;i++) NS2Indx[i]=i; for (m=i, k=Step=1;i < 256;i++) { NS2Indx[i]=m; if ( !--k ) { k = ++Step; m++; } } memset(HB2Flag,0,0x40); memset(HB2Flag+0x40,0x08,0x100-0x40); DummySEE2Cont.Shift=PERIOD_BITS; } } void RARPPM_CONTEXT::rescale(ModelPPM *Model) { int OldNS=NumStats, i=NumStats-1, Adder, EscFreq; RARPPM_STATE* p1, * p; for (p=Model->FoundState;p != U.Stats;p--) _PPMD_SWAP(p[0],p[-1]); U.Stats->Freq += 4; U.SummFreq += 4; EscFreq=U.SummFreq-p->Freq; Adder=(Model->OrderFall != 0); U.SummFreq = (p->Freq=(p->Freq+Adder) >> 1); do { EscFreq -= (++p)->Freq; U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1); if (p[0].Freq > p[-1].Freq) { RARPPM_STATE tmp=*(p1=p); do { p1[0]=p1[-1]; } while (--p1 != U.Stats && tmp.Freq > p1[-1].Freq); *p1=tmp; } } while ( --i ); if (p->Freq == 0) { do { i++; } while ((--p)->Freq == 0); EscFreq += i; if ((NumStats -= i) == 1) { RARPPM_STATE tmp=*U.Stats; do { tmp.Freq-=(tmp.Freq >> 1); EscFreq>>=1; } while (EscFreq > 1); Model->SubAlloc.FreeUnits(U.Stats,(OldNS+1) >> 1); *(Model->FoundState=&OneState)=tmp; return; } } U.SummFreq += (EscFreq -= (EscFreq >> 1)); int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1; if (n0 != n1) U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); Model->FoundState=U.Stats; } inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1) { RARPPM_STATE UpState; RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; RARPPM_STATE * p, * ps[MAX_O], ** pps=ps; if ( !Skip ) { *pps++ = FoundState; if ( !pc->Suffix ) goto NO_LOOP; } if ( p1 ) { p=p1; pc=pc->Suffix; goto LOOP_ENTRY; } do { pc=pc->Suffix; if (pc->NumStats != 1) { if ((p=pc->U.Stats)->Symbol != FoundState->Symbol) do { p++; } while (p->Symbol != FoundState->Symbol); } else p=&(pc->OneState); LOOP_ENTRY: if (p->Successor != UpBranch) { pc=p->Successor; break; } // We ensure that PPM order input parameter does not exceed MAX_O (64), // so we do not really need this check and added it for extra safety. // See CVE-2017-17969 for details. if (pps>=ps+ASIZE(ps)) return NULL; *pps++ = p; } while ( pc->Suffix ); NO_LOOP: if (pps == ps) return pc; UpState.Symbol=*(byte*) UpBranch; UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1); if (pc->NumStats != 1) { if ((byte*) pc <= SubAlloc.pText) return(NULL); if ((p=pc->U.Stats)->Symbol != UpState.Symbol) do { p++; } while (p->Symbol != UpState.Symbol); uint cf=p->Freq-1; uint s0=pc->U.SummFreq-pc->NumStats-cf; UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0))); } else UpState.Freq=pc->OneState.Freq; do { pc = pc->createChild(this,*--pps,UpState); if ( !pc ) return NULL; } while (pps != ps); return pc; } inline void ModelPPM::UpdateModel() { RARPPM_STATE fs = *FoundState, *p = NULL; RARPPM_CONTEXT *pc, *Successor; uint ns1, ns, cf, sf, s0; if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL) { if (pc->NumStats != 1) { if ((p=pc->U.Stats)->Symbol != fs.Symbol) { do { p++; } while (p->Symbol != fs.Symbol); if (p[0].Freq >= p[-1].Freq) { _PPMD_SWAP(p[0],p[-1]); p--; } } if (p->Freq < MAX_FREQ-9) { p->Freq += 2; pc->U.SummFreq += 2; } } else { p=&(pc->OneState); p->Freq += (p->Freq < 32); } } if ( !OrderFall ) { MinContext=MaxContext=FoundState->Successor=CreateSuccessors(TRUE,p); if ( !MinContext ) goto RESTART_MODEL; return; } *SubAlloc.pText++ = fs.Symbol; Successor = (RARPPM_CONTEXT*) SubAlloc.pText; if (SubAlloc.pText >= SubAlloc.FakeUnitsStart) goto RESTART_MODEL; if ( fs.Successor ) { if ((byte*) fs.Successor <= SubAlloc.pText && (fs.Successor=CreateSuccessors(FALSE,p)) == NULL) goto RESTART_MODEL; if ( !--OrderFall ) { Successor=fs.Successor; SubAlloc.pText -= (MaxContext != MinContext); } } else { FoundState->Successor=Successor; fs.Successor=MinContext; } s0=MinContext->U.SummFreq-(ns=MinContext->NumStats)-(fs.Freq-1); for (pc=MaxContext;pc != MinContext;pc=pc->Suffix) { if ((ns1=pc->NumStats) != 1) { if ((ns1 & 1) == 0) { pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); if ( !pc->U.Stats ) goto RESTART_MODEL; } pc->U.SummFreq += (2*ns1 < ns)+2*((4*ns1 <= ns) & (pc->U.SummFreq <= 8*ns1)); } else { p=(RARPPM_STATE*) SubAlloc.AllocUnits(1); if ( !p ) goto RESTART_MODEL; *p=pc->OneState; pc->U.Stats=p; if (p->Freq < MAX_FREQ/4-1) p->Freq += p->Freq; else p->Freq = MAX_FREQ-4; pc->U.SummFreq=p->Freq+InitEsc+(ns > 3); } cf=2*fs.Freq*(pc->U.SummFreq+6); sf=s0+pc->U.SummFreq; if (cf < 6*sf) { cf=1+(cf > sf)+(cf >= 4*sf); pc->U.SummFreq += 3; } else { cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf); pc->U.SummFreq += cf; } p=pc->U.Stats+ns1; p->Successor=Successor; p->Symbol = fs.Symbol; p->Freq = cf; pc->NumStats=++ns1; } MaxContext=MinContext=fs.Successor; return; RESTART_MODEL: RestartModelRare(); EscCount=0; } // Tabulated escapes for exponential symbol distribution static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; #define GET_MEAN(SUMM,SHIFT,ROUND) ((SUMM+(1 << (SHIFT-ROUND))) >> (SHIFT)) inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) { RARPPM_STATE& rs=OneState; Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+ Model->NS2BSIndx[Suffix->NumStats-1]+ Model->HiBitsFlag+2*Model->HB2Flag[rs.Symbol]+ ((Model->RunLength >> 26) & 0x20)]; if (Model->Coder.GetCurrentShiftCount(TOT_BITS) < bs) { Model->FoundState=&rs; rs.Freq += (rs.Freq < 128); Model->Coder.SubRange.LowCount=0; Model->Coder.SubRange.HighCount=bs; bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); Model->PrevSuccess=1; Model->RunLength++; } else { Model->Coder.SubRange.LowCount=bs; bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); Model->Coder.SubRange.HighCount=BIN_SCALE; Model->InitEsc=ExpEscape[bs >> 10]; Model->NumMasked=1; Model->CharMask[rs.Symbol]=Model->EscCount; Model->PrevSuccess=0; Model->FoundState=NULL; } } inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; if (p[0].Freq > p[-1].Freq) { _PPMD_SWAP(p[0],p[-1]); Model->FoundState=--p; if (p->Freq > MAX_FREQ) rescale(Model); } } inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model) { Model->Coder.SubRange.scale=U.SummFreq; RARPPM_STATE* p=U.Stats; int i, HiCnt; int count=Model->Coder.GetCurrentCount(); if (count>=(int)Model->Coder.SubRange.scale) return(false); if (count < (HiCnt=p->Freq)) { Model->PrevSuccess=(2*(Model->Coder.SubRange.HighCount=HiCnt) > Model->Coder.SubRange.scale); Model->RunLength += Model->PrevSuccess; (Model->FoundState=p)->Freq=(HiCnt += 4); U.SummFreq += 4; if (HiCnt > MAX_FREQ) rescale(Model); Model->Coder.SubRange.LowCount=0; return(true); } else if (Model->FoundState==NULL) return(false); Model->PrevSuccess=0; i=NumStats-1; while ((HiCnt += (++p)->Freq) <= count) if (--i == 0) { Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; Model->Coder.SubRange.LowCount=HiCnt; Model->CharMask[p->Symbol]=Model->EscCount; i=(Model->NumMasked=NumStats)-1; Model->FoundState=NULL; do { Model->CharMask[(--p)->Symbol]=Model->EscCount; } while ( --i ); Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; return(true); } Model->Coder.SubRange.LowCount=(Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; update1(Model,p); return(true); } inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; if (p->Freq > MAX_FREQ) rescale(Model); Model->EscCount++; Model->RunLength=Model->InitRL; } inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) { RARPPM_SEE2_CONTEXT* psee2c; if (NumStats != 256) { psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+ (Diff < Suffix->NumStats-NumStats)+ 2*(U.SummFreq < 11*NumStats)+4*(Model->NumMasked > Diff)+ Model->HiBitsFlag; Model->Coder.SubRange.scale=psee2c->getMean(); } else { psee2c=&Model->DummySEE2Cont; Model->Coder.SubRange.scale=1; } return psee2c; } inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model) { int count, HiCnt, i=NumStats-Model->NumMasked; RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1; HiCnt=0; do { do { p++; } while (Model->CharMask[p->Symbol] == Model->EscCount); HiCnt += p->Freq; // We do not reuse PPMd coder in unstable state, so we do not really need // this check and added it for extra safety. See CVE-2017-17969 for details. if (pps>=ps+ASIZE(ps)) return false; *pps++ = p; } while ( --i ); Model->Coder.SubRange.scale += HiCnt; count=Model->Coder.GetCurrentCount(); if (count>=(int)Model->Coder.SubRange.scale) return(false); p=*(pps=ps); if (count < HiCnt) { HiCnt=0; while ((HiCnt += p->Freq) <= count) { pps++; if (pps>=ps+ASIZE(ps)) // Extra safety check. return false; p=*pps; } Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; psee2c->update(); update2(Model,p); } else { Model->Coder.SubRange.LowCount=HiCnt; Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; i=NumStats-Model->NumMasked; pps--; do { pps++; if (pps>=ps+ASIZE(ps)) // Extra safety check. return false; Model->CharMask[(*pps)->Symbol]=Model->EscCount; } while ( --i ); psee2c->Summ += Model->Coder.SubRange.scale; Model->NumMasked = NumStats; } return true; } inline void ModelPPM::ClearMask() { EscCount=1; memset(CharMask,0,sizeof(CharMask)); } // reset PPM variables after data error allowing safe resuming // of further data processing void ModelPPM::CleanUp() { SubAlloc.StopSubAllocator(); SubAlloc.StartSubAllocator(1); StartModelRare(2); } bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar) { int MaxOrder=UnpackRead->GetChar(); bool Reset=(MaxOrder & 0x20)!=0; int MaxMB; if (Reset) MaxMB=UnpackRead->GetChar(); else if (SubAlloc.GetAllocatedMemory()==0) return(false); if (MaxOrder & 0x40) EscChar=UnpackRead->GetChar(); Coder.InitDecoder(UnpackRead); if (Reset) { MaxOrder=(MaxOrder & 0x1f)+1; if (MaxOrder>16) MaxOrder=16+(MaxOrder-16)*3; if (MaxOrder==1) { SubAlloc.StopSubAllocator(); return(false); } SubAlloc.StartSubAllocator(MaxMB+1); StartModelRare(MaxOrder); } return(MinContext!=NULL); } int ModelPPM::DecodeChar() { if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd) return(-1); if (MinContext->NumStats != 1) { if ((byte*)MinContext->U.Stats <= SubAlloc.pText || (byte*)MinContext->U.Stats>SubAlloc.HeapEnd) return(-1); if (!MinContext->decodeSymbol1(this)) return(-1); } else MinContext->decodeBinSymbol(this); Coder.Decode(); while ( !FoundState ) { ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); do { OrderFall++; MinContext=MinContext->Suffix; if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd) return(-1); } while (MinContext->NumStats == NumMasked); if (!MinContext->decodeSymbol2(this)) return(-1); Coder.Decode(); } int Symbol=FoundState->Symbol; if (!OrderFall && (byte*) FoundState->Successor > SubAlloc.pText) MinContext=MaxContext=FoundState->Successor; else { UpdateModel(); if (EscCount == 0) ClearMask(); } ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); return(Symbol); } unrar/options.cpp000666 000000 000000 00000001165 13343205465 012566 0ustar00000000 000000 #include "rar.hpp" RAROptions::RAROptions() { Init(); } RAROptions::~RAROptions() { // It is important for security reasons, so we do not have the unnecessary // password data left in memory. memset(this,0,sizeof(RAROptions)); } void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); WinSize=0x2000000; Overwrite=OVERWRITE_DEFAULT; Method=3; MsgStream=MSG_STDOUT; ConvertNames=NAMES_ORIGINALCASE; xmtime=EXTTIME_HIGH3; FileSizeLess=INT64NDF; FileSizeMore=INT64NDF; HashType=HASH_CRC32; #ifdef RAR_SMP Threads=GetNumberOfThreads(); #endif #ifdef USE_QOPEN QOpenMode=QOPEN_AUTO; #endif } unrar/pathfn.cpp000666 000000 000000 00000056202 13343205465 012355 0ustar00000000 000000 #include "rar.hpp" wchar* PointToName(const wchar *Path) { for (int I=(int)wcslen(Path)-1;I>=0;I--) if (IsPathDiv(Path[I])) return (wchar*)&Path[I+1]; return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path); } wchar* PointToLastChar(const wchar *Path) { size_t Length=wcslen(Path); return (wchar*)(Length>0 ? Path+Length-1:Path); } wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath) { const wchar *DestPtr=SrcPath; // Prevent \..\ in any part of path string. for (const wchar *s=DestPtr;*s!=0;s++) if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) DestPtr=s+4; // Remove :\ and any sequence of . and \ in the beginning of path string. while (*DestPtr!=0) { const wchar *s=DestPtr; if (s[0]!=0 && IsDriveDiv(s[1])) s+=2; if (s[0]=='\\' && s[1]=='\\') { const wchar *Slash=wcschr(s+2,'\\'); if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) s=Slash+1; } for (const wchar *t=s;*t!=0;t++) if (IsPathDiv(*t)) s=t+1; else if (*t!='.') break; if (s==DestPtr) break; DestPtr=s; } // Code above does not remove last "..", doing here. if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0) DestPtr+=2; if (DestPath!=NULL) { // SrcPath and DestPath can point to same memory area, // so we use the temporary buffer for copying. wchar TmpStr[NM]; wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); wcscpy(DestPath,TmpStr); } return (wchar *)DestPtr; } void SetName(wchar *FullName,const wchar *Name,size_t MaxSize) { wchar *NamePtr=PointToName(FullName); wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName)); } void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize) { if (Name==NULL || *Name==0) return; wchar *Dot=GetExt(Name); if (Dot!=NULL) *Dot=0; if (NewExt!=NULL) { wcsncatz(Name,L".",MaxSize); wcsncatz(Name,NewExt,MaxSize); } } #ifndef SFX_MODULE void SetSFXExt(wchar *SFXName,size_t MaxSize) { if (SFXName==NULL || *SFXName==0) return; #ifdef _UNIX SetExt(SFXName,L"sfx",MaxSize); #endif #if defined(_WIN_ALL) || defined(_EMX) SetExt(SFXName,L"exe",MaxSize); #endif } #endif // 'Ext' is an extension with the leading dot, like L".rar". wchar *GetExt(const wchar *Name) { return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.'); } // 'Ext' is an extension without the leading dot, like L"rar". bool CmpExt(const wchar *Name,const wchar *Ext) { wchar *NameExt=GetExt(Name); return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0; } bool IsWildcard(const wchar *Str) { return Str==NULL ? false:wcspbrk(Str,L"*?")!=NULL; } bool IsPathDiv(int Ch) { #ifdef _WIN_ALL return Ch=='\\' || Ch=='/'; #else return Ch==CPATHDIVIDER; #endif } bool IsDriveDiv(int Ch) { #ifdef _UNIX return false; #else return Ch==':'; #endif } bool IsDriveLetter(const wchar *Path) { wchar Letter=etoupperw(Path[0]); return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]); } int GetPathDisk(const wchar *Path) { if (IsDriveLetter(Path)) return etoupperw(*Path)-'A'; else return -1; } void AddEndSlash(wchar *Path,size_t MaxLength) { size_t Length=wcslen(Path); if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) Name--; *Name=0; } #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; *Path=0; bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDList(ppidl,Path) && *Path!=0) { AddEndSlash(Path,MaxSize); wcsncatz(Path,L"WinRAR",MaxSize); Success=FileExist(Path); if (!Success && Create) Success=MakeDir(Path,false,0)==MKDIR_SUCCESS; } g_pMalloc->Free(ppidl); return Success; } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) { *Path=0; HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { DWORD DataSize=(DWORD)MaxSize,Type; RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); RegCloseKey(hKey); } if (*Path==0 || !FileExist(Path)) if (!GetAppDataPath(Path,MaxSize,Create)) { GetModuleFileName(NULL,Path,(DWORD)MaxSize); RemoveNameFromPath(Path); } } #endif #ifndef SFX_MODULE bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) { #ifdef _UNIX static const wchar *ConfPath[]={ L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc" }; if (Number==0) { char *EnvStr=getenv("HOME"); if (EnvStr!=NULL) CharToWide(EnvStr,Path,MaxSize); else wcsncpyz(Path,ConfPath[0],MaxSize); return true; } Number--; if (Number>=ASIZE(ConfPath)) return false; wcsncpyz(Path,ConfPath[Number], MaxSize); return true; #elif defined(_WIN_ALL) if (Number>1) return false; if (Number==0) GetRarDataPath(Path,MaxSize,Create); else { GetModuleFileName(NULL,Path,(DWORD)MaxSize); RemoveNameFromPath(Path); } return true; #else return false; #endif } #endif #ifndef SFX_MODULE void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create) { *FullName=0; for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++) { AddEndSlash(FullName,MaxSize); wcsncatz(FullName,Name,MaxSize); if (!CheckExist || WildFileExist(FullName)) break; } } #endif // Returns a pointer to rightmost digit of volume number. wchar* GetVolNumPart(const wchar *ArcName) { // Pointing to last name character. const wchar *ChPtr=ArcName+wcslen(ArcName)-1; // Skipping the archive extension. while (!IsDigit(*ChPtr) && ChPtr>ArcName) ChPtr--; // Skipping the numeric part of name. const wchar *NumPtr=ChPtr; while (IsDigit(*NumPtr) && NumPtr>ArcName) NumPtr--; // Searching for first numeric part in names like name.part##of##.rar. // Stop search on the first dot. while (NumPtr>ArcName && *NumPtr!='.') { if (IsDigit(*NumPtr)) { // Validate the first numeric part only if it has a dot somewhere // before it. wchar *Dot=wcschr(PointToName(ArcName),'.'); if (Dot!=NULL && Dot|\"")==NULL; } void MakeNameUsable(char *Name,bool Extended) { #ifdef _WIN_ALL // In Windows we also need to convert characters not defined in current // code page. This double conversion changes them to '?', which is // catched by code below. size_t NameLength=strlen(Name); wchar NameW[NM]; CharToWide(Name,NameW,ASIZE(NameW)); WideToChar(NameW,Name,NameLength+1); Name[NameLength]=0; #endif for (char *s=Name;*s!=0;s=charnext(s)) { if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32) *s='_'; #ifdef _EMX if (*s=='=') *s='_'; #endif #ifndef _UNIX if (s-Name>1 && *s==':') *s='_'; // Remove ' ' and '.' before path separator, but allow .\ and ..\. if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1])) *s='_'; #endif } } void MakeNameUsable(wchar *Name,bool Extended) { for (wchar *s=Name;*s!=0;s++) { if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) *s='_'; #ifndef _UNIX if (s-Name>1 && *s==':') *s='_'; #if 0 // We already can create such files. // Remove ' ' and '.' before path separator, but allow .\ and ..\. if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2])))) *s='_'; #endif #endif } } void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) { size_t Copied=0; for (;Copied0) *Dest=0; return; } #ifdef _WIN_ALL { wchar FullName[NM],*NamePtr; DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr); if (Code==0 || Code>ASIZE(FullName)) { wchar LongName[NM]; if (GetWinLongPath(Src,LongName,ASIZE(LongName))) Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr); } if (Code!=0 && Code=MaxSize) Length=0; wcsncpy(Root,Path,Length); Root[Length]=0; } } } int ParseVersionFileName(wchar *Name,bool Truncate) { int Version=0; wchar *VerText=wcsrchr(Name,';'); if (VerText!=NULL) { if (Version==0) Version=atoiw(VerText+1); if (Truncate) *VerText=0; } return Version; } #if !defined(SFX_MODULE) // Get the name of first volume. Return the leftmost digit of volume number. wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering) { if (FirstName!=VolName) wcsncpyz(FirstName,VolName,MaxSize); wchar *VolNumStart=FirstName; if (NewNumbering) { wchar N='1'; // From the rightmost digit of volume number to the left. for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) if (IsDigit(*ChPtr)) { *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else if (N=='0') { VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. break; } } else { // Old volume numbering scheme. Just set the extension to ".rar". SetExt(FirstName,L"rar",MaxSize); VolNumStart=GetExt(FirstName); } if (!FileExist(FirstName)) { // If the first volume, which name we just generated, is not exist, // check if volume with same name and any other extension is available. // It can help in case of *.exe or *.sfx first volume. wchar Mask[NM]; wcsncpyz(Mask,FirstName,ASIZE(Mask)); SetExt(Mask,L"*",ASIZE(Mask)); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { wcsncpyz(FirstName,FD.Name,MaxSize); break; } } } return VolNumStart; } #endif #ifndef SFX_MODULE static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent) { bool Prefix=false; if (*GenerateMask=='+') { Prefix=true; // Add the time string before the archive name. GenerateMask++; // Skip '+' in the beginning of time mask. } wchar Mask[MAX_GENERATE_MASK]; wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask)); bool QuoteMode=false,Hours=false; for (uint I=0;Mask[I]!=0;I++) { if (Mask[I]=='{' || Mask[I]=='}') { QuoteMode=(Mask[I]=='{'); continue; } if (QuoteMode) continue; int CurChar=toupperw(Mask[I]); if (CurChar=='H') Hours=true; if (Hours && CurChar=='M') { // Replace minutes with 'I'. We use 'M' both for months and minutes, // so we treat as minutes only those 'M' which are found after hours. Mask[I]='I'; } if (CurChar=='N') { uint Digits=GetDigits(ArcNumber); uint NCount=0; while (toupperw(Mask[I+NCount])=='N') NCount++; // Here we ensure that we have enough 'N' characters to fit all digits // of archive number. We'll replace them by actual number later // in this function. if (NCount=4) CurWeek++; char Field[10][6]; sprintf(Field[0],"%04u",rlt.Year); sprintf(Field[1],"%02u",rlt.Month); sprintf(Field[2],"%02u",rlt.Day); sprintf(Field[3],"%02u",rlt.Hour); sprintf(Field[4],"%02u",rlt.Minute); sprintf(Field[5],"%02u",rlt.Second); sprintf(Field[6],"%02u",(uint)CurWeek); sprintf(Field[7],"%u",(uint)WeekDay+1); sprintf(Field[8],"%03u",rlt.yDay+1); sprintf(Field[9],"%05u",ArcNumber); const wchar *MaskChars=L"YMDHISWAEN"; int CField[sizeof(Field)/sizeof(Field[0])]; memset(CField,0,sizeof(CField)); QuoteMode=false; for (int I=0;Mask[I]!=0;I++) { if (Mask[I]=='{' || Mask[I]=='}') { QuoteMode=(Mask[I]=='{'); continue; } if (QuoteMode) continue; const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); if (ChPtr!=NULL) CField[ChPtr-MaskChars]++; } wchar DateText[MAX_GENERATE_MASK]; *DateText=0; QuoteMode=false; for (size_t I=0,J=0;Mask[I]!=0 && J1) { // If we perform non-archiving operation, we need to use the last // existing archive before the first unused name. So we generate // the name for (ArcNumber-1) below. wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); GenArcName(NewName,GenerateMask,ArcNumber-1,ArcNumPresent); } break; } ArcNumber++; } wcsncpyz(ArcName,NewName,MaxSize); } #endif wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize) { if (NameW!=NULL && *NameW!=0) { if (DestW!=NameW) wcsncpy(DestW,NameW,DestSize); } else if (Name!=NULL) CharToWide(Name,DestW,DestSize); else *DestW=0; // Ensure that we return a zero terminate string for security reasons. if (DestSize>0) DestW[DestSize-1]=0; return DestW; } #ifdef _WIN_ALL // We should return 'true' even if resulting path is shorter than MAX_PATH, // because we can also use this function to open files with non-standard // characters, even if their path length is normal. bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize) { if (*Src==0) return false; const wchar *Prefix=L"\\\\?\\"; const size_t PrefixLength=4; bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]); size_t SrcLength=wcslen(Src); if (IsFullPath(Src)) // Paths in d:\path\name format. { if (IsDriveLetter(Src)) { if (MaxSize<=PrefixLength+SrcLength) return false; wcsncpy(Dest,Prefix,PrefixLength); wcscpy(Dest+PrefixLength,Src); return true; } else if (Src[0]=='\\' && Src[1]=='\\') { if (MaxSize<=PrefixLength+SrcLength+2) return false; wcsncpy(Dest,Prefix,PrefixLength); wcscpy(Dest+PrefixLength,L"UNC"); wcscpy(Dest+PrefixLength+3,Src+1); return true; } // We may be here only if we modify IsFullPath in the future. return false; } else { wchar CurDir[NM]; DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir); if (DirCode==0 || DirCode>ASIZE(CurDir)-1) return false; if (IsPathDiv(Src[0])) // Paths in \path\name format. { if (MaxSize<=PrefixLength+SrcLength+2) return false; wcsncpy(Dest,Prefix,PrefixLength); wcsncpy(Dest+PrefixLength,CurDir,2); // Copy drive letter 'd:'. wcscpy(Dest+PrefixLength+2,Src); return true; } else // Paths in path\name format. { AddEndSlash(CurDir,ASIZE(CurDir)); if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength) return false; wcsncpy(Dest,Prefix,PrefixLength); wcscpy(Dest+PrefixLength,CurDir); if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname. Src+=2; wcsncatz(Dest,Src,MaxSize); return true; } } return false; } // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. void ConvertToPrecomposed(wchar *Name,size_t NameSize) { wchar FileName[NM]; if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP. FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0) { FileName[ASIZE(FileName)-1]=0; wcsncpyz(Name,FileName,NameSize); } } // Remove trailing spaces and dots in file name and in dir names in path. void MakeNameCompatible(wchar *Name) { int Src=0,Dest=0; while (true) { if (IsPathDiv(Name[Src]) || Name[Src]==0) for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--) { // Permit path1/./path2 and ../path1 paths. if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) break; Dest--; } Name[Dest]=Name[Src]; if (Name[Src]==0) break; Src++; Dest++; } } #endif unrar/qopen.cpp000666 000000 000000 00000015131 13343205465 012213 0ustar00000000 000000 #include "rar.hpp" QuickOpen::QuickOpen() { Buf=NULL; Init(NULL,false); } QuickOpen::~QuickOpen() { Close(); delete[] Buf; } void QuickOpen::Init(Archive *Arc,bool WriteMode) { if (Arc!=NULL) // Unless called from constructor. Close(); QuickOpen::Arc=Arc; QuickOpen::WriteMode=WriteMode; ListStart=NULL; ListEnd=NULL; if (Buf==NULL) Buf=new byte[MaxBufSize]; CurBufSize=0; // Current size of buffered data in write mode. Loaded=false; } void QuickOpen::Close() { QuickOpenItem *Item=ListStart; while (Item!=NULL) { QuickOpenItem *Next=Item->Next; delete[] Item->Header; delete Item; Item=Next; } } void QuickOpen::Load(uint64 BlockPos) { if (!Loaded) { // If loading for the first time, perform additional intialization. SeekPos=Arc->Tell(); UnsyncSeekPos=false; SaveFilePos SavePos(*Arc); Arc->Seek(BlockPos,SEEK_SET); // If BlockPos points to original main header, we'll have the infinite // recursion, because ReadHeader() for main header will attempt to load // QOpen and call QuickOpen::Load again. If BlockPos points to long chain // of other main headers, we'll have multiple recursive calls of this // function wasting resources. So we prohibit QOpen temporarily to // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator // and QOpenOffset fields, so we cannot use them to prohibit QOpen. Arc->SetProhibitQOpen(true); size_t ReadSize=Arc->ReadHeader(); Arc->SetProhibitQOpen(false); if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) return; QOHeaderPos=Arc->CurBlockPos; RawDataStart=Arc->Tell(); RawDataSize=Arc->SubHead.UnpSize; Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader. } if (Arc->SubHead.Encrypted) { RAROptions *Cmd=Arc->GetRAROptions(); #ifndef RAR_NOCRYPT if (Cmd->Password.IsSet()) Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, Arc->SubHead.InitV,Arc->SubHead.Lg2Count, Arc->SubHead.HashKey,Arc->SubHead.PswCheck); else #endif { Loaded=false; return; } } RawDataPos=0; ReadBufSize=0; ReadBufPos=0; LastReadHeader.Reset(); LastReadHeaderPos=0; ReadBuffer(); } bool QuickOpen::Read(void *Data,size_t Size,size_t &Result) { if (!Loaded) return false; // Find next suitable cached block. while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos) if (!ReadNext()) break; if (!Loaded) { // If something wrong happened, let's set the correct file pointer // and stop further quick open processing. if (UnsyncSeekPos) Arc->File::Seek(SeekPos,SEEK_SET); return false; } if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size()) { memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size); Result=Size; SeekPos+=Size; UnsyncSeekPos=true; } else { if (UnsyncSeekPos) { Arc->File::Seek(SeekPos,SEEK_SET); UnsyncSeekPos=false; } int ReadSize=Arc->File::Read(Data,Size); if (ReadSize<0) { Loaded=false; return false; } Result=ReadSize; SeekPos+=ReadSize; } return true; } bool QuickOpen::Seek(int64 Offset,int Method) { if (!Loaded) return false; // Normally we process an archive sequentially from beginning to end, // so we read quick open data sequentially. But some operations like // archive updating involve several passes. So if we detect that file // pointer is moved back, we reload quick open data from beginning. if (Method==SEEK_SET && (uint64)OffsetFile::Seek(Offset,SEEK_END); SeekPos=Arc->File::Tell(); UnsyncSeekPos=false; } return true; } bool QuickOpen::Tell(int64 *Pos) { if (!Loaded) return false; *Pos=SeekPos; return true; } uint QuickOpen::ReadBuffer() { SaveFilePos SavePos(*Arc); Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET); size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize); if (Arc->SubHead.Encrypted) SizeToRead &= ~CRYPT_BLOCK_MASK; if (SizeToRead==0) return 0; int ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); if (ReadSize<=0) return 0; #ifndef RAR_NOCRYPT if (Arc->SubHead.Encrypted) Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); #endif RawDataPos+=ReadSize; ReadBufSize+=ReadSize; return ReadSize; } // Fill RawRead object from buffer. bool QuickOpen::ReadRaw(RawRead &Raw) { if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer. { // Ensure that we have enough data to read CRC and header size. size_t DataLeft=ReadBufSize-ReadBufPos; memcpy(Buf,Buf+ReadBufPos,DataLeft); ReadBufPos=0; ReadBufSize=DataLeft; ReadBuffer(); } const size_t FirstReadSize=7; if (ReadBufPos+FirstReadSize>ReadBufSize) return false; Raw.Read(Buf+ReadBufPos,FirstReadSize); ReadBufPos+=FirstReadSize; uint SavedCRC=Raw.Get4(); uint SizeBytes=Raw.GetVSize(4); uint64 BlockSize=Raw.GetV(); int SizeToRead=int(BlockSize); SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. if (SizeToRead<0 || SizeBytes==0 || BlockSize==0) { Loaded=false; // Invalid data. return false; } // If rest of block data crosses Buf boundary, read it in loop. while (SizeToRead>0) { size_t DataLeft=ReadBufSize-ReadBufPos; size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead); Raw.Read(Buf+ReadBufPos,CurSizeToRead); ReadBufPos+=CurSizeToRead; SizeToRead-=int(CurSizeToRead); if (SizeToRead>0) // We read the entire buffer and still need more data. { ReadBufPos=0; ReadBufSize=0; if (ReadBuffer()==0) return false; } } return SavedCRC==Raw.GetCRC50(); } // Read next cached header. bool QuickOpen::ReadNext() { RawRead Raw(NULL); if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block. return false; uint Flags=(uint)Raw.GetV(); uint64 Offset=Raw.GetV(); size_t HeaderSize=(size_t)Raw.GetV(); if (HeaderSize>MAX_HEADER_SIZE_RAR5) return false; LastReadHeader.Alloc(HeaderSize); Raw.GetB(&LastReadHeader[0],HeaderSize); // Calculate the absolute position as offset from quick open service header. LastReadHeaderPos=QOHeaderPos-Offset; return true; } unrar/rar.cpp000666 000000 000000 00000004276 13343205465 011665 0ustar00000000 000000 #include "rar.hpp" #if !defined(RARDLL) int main(int argc, char *argv[]) { #ifdef _UNIX setlocale(LC_ALL,""); #endif InitConsole(); ErrHandler.SetSignalHandlers(true); #ifdef SFX_MODULE wchar ModuleName[NM]; #ifdef _WIN_ALL GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName)); #else CharToWide(argv[0],ModuleName,ASIZE(ModuleName)); #endif #endif #ifdef _WIN_ALL SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) // Must be initialized, normal initialization can be skipped in case of // exception. POWER_MODE ShutdownOnClose=POWERMODE_KEEP; #endif try { CommandData *Cmd=new CommandData; #ifdef SFX_MODULE wcscpy(Cmd->Command,L"X"); char *Switch=argc>1 ? argv[1]:NULL; if (Switch!=NULL && Cmd->IsSwitch(Switch[0])) { int UpperCmd=etoupper(Switch[1]); switch(UpperCmd) { case 'T': case 'V': Cmd->Command[0]=UpperCmd; break; case '?': Cmd->OutHelp(RARX_SUCCESS); break; } } Cmd->AddArcName(ModuleName); Cmd->ParseDone(); Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source. #else // !SFX_MODULE Cmd->ParseCommandLine(true,argc,argv); if (!Cmd->ConfigDisabled) { Cmd->ReadConfig(); Cmd->ParseEnvVar(); } Cmd->ParseCommandLine(false,argc,argv); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) ShutdownOnClose=Cmd->Shutdown; #endif uiInit(Cmd->Sound); InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset); ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL); Cmd->OutTitle(); Cmd->ProcessCommand(); delete Cmd; } catch (RAR_EXIT ErrCode) { ErrHandler.SetErrorCode(ErrCode); } catch (std::bad_alloc&) { ErrHandler.MemoryErrorMsg(); ErrHandler.SetErrorCode(RARX_MEMORY); } catch (...) { ErrHandler.SetErrorCode(RARX_FATAL); } #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled()) Shutdown(ShutdownOnClose); #endif ErrHandler.MainExit=true; return ErrHandler.GetErrorCode(); } #endif unrar/rarpch.cpp000666 000000 000000 00000000131 13343205465 012342 0ustar00000000 000000 // We use rarpch.cpp to create precompiled headers for MS Visual C++. #include "rar.hpp" unrar/rarvm.cpp000666 000000 000000 00000023007 13343205465 012221 0ustar00000000 000000 #include "rar.hpp" RarVM::RarVM() { Mem=NULL; } RarVM::~RarVM() { delete[] Mem; } void RarVM::Init() { if (Mem==NULL) Mem=new byte[VM_MEMSIZE+4]; } void RarVM::Execute(VM_PreparedProgram *Prg) { memcpy(R,Prg->InitR,sizeof(Prg->InitR)); Prg->FilteredData=NULL; if (Prg->Type!=VMSF_NONE) { bool Success=ExecuteStandardFilter(Prg->Type); uint BlockSize=Prg->InitR[4] & VM_MEMMASK; Prg->FilteredDataSize=BlockSize; if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO) Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize; else Prg->FilteredData=Mem; } } void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg) { // Calculate the single byte XOR checksum to check validity of VM code. byte XorSum=0; for (uint I=1;IType=StdList[I].Type; break; } } uint RarVM::ReadData(BitInput &Inp) { uint Data=Inp.fgetbits(); switch(Data&0xc000) { case 0: Inp.faddbits(6); return (Data>>10)&0xf; case 0x4000: if ((Data&0x3c00)==0) { Data=0xffffff00|((Data>>2)&0xff); Inp.faddbits(14); } else { Data=(Data>>6)&0xff; Inp.faddbits(10); } return Data; case 0x8000: Inp.faddbits(2); Data=Inp.fgetbits(); Inp.faddbits(16); return Data; default: Inp.faddbits(2); Data=(Inp.fgetbits()<<16); Inp.faddbits(16); Data|=Inp.fgetbits(); Inp.faddbits(16); return Data; } } void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize) { if (PosVM_MEMSIZE || DataSize<4) return false; const uint FileSize=0x1000000; byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; for (uint CurPos=0;CurPos=0 RawPut4(Addr+FileSize,Data); } else if (((Addr-FileSize) & 0x80000000)!=0) // AddrVM_MEMSIZE || DataSize<21) return false; uint CurPos=0; FileOffset>>=4; while (CurPos=0) { static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0}; byte CmdMask=Masks[Byte]; if (CmdMask!=0) for (uint I=0;I<=2;I++) if (CmdMask & (1<VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0) return false; // Bytes from same channels are grouped to continual data blocks, // so we need to place them back to their interleaving positions. for (uint CurChannel=0;CurChannelVM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2) return false; byte *SrcData=Mem,*DestData=SrcData+DataSize; const uint Channels=3; for (uint CurChannel=0;CurChannel=Width+3) { byte *UpperData=DestData+I-Width; uint UpperByte=*UpperData; uint UpperLeftByte=*(UpperData-3); Predicted=PrevByte+UpperByte-UpperLeftByte; int pa=abs((int)(Predicted-PrevByte)); int pb=abs((int)(Predicted-UpperByte)); int pc=abs((int)(Predicted-UpperLeftByte)); if (pa<=pb && pa<=pc) Predicted=PrevByte; else if (pb<=pc) Predicted=UpperByte; else Predicted=UpperLeftByte; } else Predicted=PrevByte; DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); } } for (uint I=PosR,Border=DataSize-2;IVM_MEMSIZE/2 || Channels>128 || Channels==0) return false; for (uint CurChannel=0;CurChannel>3) & 0xff; uint CurByte=*(SrcData++); Predicted-=CurByte; DestData[I]=Predicted; PrevDelta=(signed char)(Predicted-PrevByte); PrevByte=Predicted; int D=(signed char)CurByte; // Left shift of negative value is undefined behavior in C++, // so we cast it to unsigned to follow the standard. D=(uint)D<<3; Dif[0]+=abs(D); Dif[1]+=abs(D-D1); Dif[2]+=abs(D+D1); Dif[3]+=abs(D-D2); Dif[4]+=abs(D+D2); Dif[5]+=abs(D-D3); Dif[6]+=abs(D+D3); if ((ByteCount & 0x1f)==0) { uint MinDif=Dif[0],NumMinDif=0; Dif[0]=0; for (uint J=1;J=-16) K1--; break; case 2: if (K1 < 16) K1++; break; case 3: if (K2>=-16) K2--; break; case 4: if (K2 < 16) K2++; break; case 5: if (K3>=-16) K3--; break; case 6: if (K3 < 16) K3++; break; } } } } } break; } return true; } uint RarVM::FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount) { uint InAddr=BitPos/8; uint InBit=BitPos&7; uint BitField=(uint)Data[InAddr++]; BitField|=(uint)Data[InAddr++] << 8; BitField|=(uint)Data[InAddr++] << 16; BitField|=(uint)Data[InAddr] << 24; BitField >>= InBit; return BitField & (0xffffffff>>(32-BitCount)); } void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount) { uint InAddr=BitPos/8; uint InBit=BitPos&7; uint AndMask=0xffffffff>>(32-BitCount); AndMask=~(AndMask<>8)|0xff000000; BitField>>=8; } } unrar/rawread.cpp000666 000000 000000 00000007547 13343205465 012532 0ustar00000000 000000 #include "rar.hpp" RawRead::RawRead() { RawRead::SrcFile=NULL; Reset(); } RawRead::RawRead(File *SrcFile) { RawRead::SrcFile=SrcFile; Reset(); } void RawRead::Reset() { Data.SoftReset(); ReadPos=0; DataSize=0; Crypt=NULL; } size_t RawRead::Read(size_t Size) { size_t ReadSize=0; #if !defined(RAR_NOCRYPT) if (Crypt!=NULL) { // Full size of buffer with already read data including data read // for encryption block alignment. size_t FullSize=Data.Size(); // Data read for alignment and not processed yet. size_t DataLeft=FullSize-DataSize; if (Size>DataLeft) // Need to read more than we already have. { size_t SizeToRead=Size-DataLeft; size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK); Data.Add(AlignedReadSize); ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize); Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize); DataSize+=ReadSize==0 ? 0:Size; } else // Use buffered data, no real read. { ReadSize=Size; DataSize+=Size; } } else #endif if (Size!=0) { Data.Add(Size); ReadSize=SrcFile->Read(&Data[DataSize],Size); DataSize+=ReadSize; } return ReadSize; } void RawRead::Read(byte *SrcData,size_t Size) { if (Size!=0) { Data.Add(Size); memcpy(&Data[DataSize],SrcData,Size); DataSize+=Size; } } byte RawRead::Get1() { return ReadPos0) memcpy(F,&Data[ReadPos],CopySize); if (Size>CopySize) memset(F+CopySize,0,Size-CopySize); ReadPos+=CopySize; return CopySize; } void RawRead::GetW(wchar *Field,size_t Size) { if (ReadPos+2*Size-1 0) { Archive *SrcArc=(Archive *)SrcFile; if (UnpackFromMemory) { memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize); ReadSize=(int)UnpackFromMemorySize; UnpackFromMemorySize=0; } else { size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count; if (SizeToRead > 0) { if (UnpVolume && Decryption && (int64)Count>UnpPackedSize) { // We need aligned blocks for decryption and we want "Keep broken // files" to work efficiently with missing encrypted volumes. // So for last data block in volume we adjust the size to read to // next equal or smaller block producing aligned total block size. // So we'll ask for next volume only when processing few unaligned // bytes left in the end, when most of data is already extracted. size_t NewTotalRead = TotalRead + SizeToRead; size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK); size_t NewSizeToRead = SizeToRead - Adjust; if ((int)NewSizeToRead > 0) SizeToRead = NewSizeToRead; } if (!SrcFile->IsOpened()) return -1; ReadSize=SrcFile->Read(ReadAddr,SizeToRead); FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead; if (!NoFileHeader && hd->SplitAfter) PackedDataHash.Update(ReadAddr,ReadSize); } } CurUnpRead+=ReadSize; TotalRead+=ReadSize; #ifndef NOVOLUME // These variable are not used in NOVOLUME mode, so it is better // to exclude commands below to avoid compiler warnings. ReadAddr+=ReadSize; Count-=ReadSize; #endif UnpPackedSize-=ReadSize; // Do not ask for next volume if we read something from current volume. // If next volume is missing, we need to process all data from current // volume before aborting. It helps to recover all possible data // in "Keep broken files" mode. But if we process encrypted data, // we ask for next volume also if we have non-aligned encryption block. // Since we adjust data size for decryption earlier above, // it does not hurt "Keep broken files" mode efficiency. if (UnpVolume && UnpPackedSize == 0 && (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) ) { #ifndef NOVOLUME if (!MergeArchive(*SrcArc,this,true,CurrentCommand)) #endif { NextVolumeMissing=true; return -1; } } else break; } Archive *SrcArc=(Archive *)SrcFile; if (SrcArc!=NULL) ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize); if (ReadSize!=-1) { ReadSize=TotalRead; #ifndef RAR_NOCRYPT if (Decryption) Decrypt->DecryptBlock(Addr,ReadSize); #endif } Wait(); return ReadSize; } #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Disable the run time stack check for unrar.dll, so we can manipulate // with ProcessDataProc call type below. Run time check would intercept // a wrong ESP before we restore it. #pragma runtime_checks( "s", off ) #endif void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { #ifdef RARDLL RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions(); if (Cmd->DllOpMode!=RAR_SKIP) { if (Cmd->Callback!=NULL && Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1) ErrHandler.Exit(RARX_USERBREAK); if (Cmd->ProcessDataProc!=NULL) { // Here we preserve ESP value. It is necessary for those developers, // who still define ProcessDataProc callback as "C" type function, // even though in year 2001 we announced in unrar.dll whatsnew.txt // that it will be PASCAL type (for compatibility with Visual Basic). #if defined(_MSC_VER) #ifndef _WIN_64 __asm mov ebx,esp #endif #elif defined(_WIN_ALL) && defined(__BORLANDC__) _EBX=_ESP; #endif int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); // Restore ESP after ProcessDataProc with wrongly defined calling // convention broken it. #if defined(_MSC_VER) #ifndef _WIN_64 __asm mov esp,ebx #endif #elif defined(_WIN_ALL) && defined(__BORLANDC__) _ESP=_EBX; #endif if (RetCode==0) ErrHandler.Exit(RARX_USERBREAK); } } #endif // RARDLL UnpWrAddr=Addr; UnpWrSize=Count; if (UnpackToMemory) { if (Count <= UnpackToMemorySize) { memcpy(UnpackToMemoryAddr,Addr,Count); UnpackToMemoryAddr+=Count; UnpackToMemorySize-=Count; } } else if (!TestMode) DestFile->Write(Addr,Count); CurUnpWrite+=Count; if (!SkipUnpCRC) UnpHash.Update(Addr,Count); ShowUnpWrite(); Wait(); } #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Restore the run time stack check for unrar.dll. #pragma runtime_checks( "s", restore ) #endif void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) { if (ShowProgress && SrcFile!=NULL) { if (TotalArcSize!=0) { // important when processing several archives or multivolume archive ArcSize=TotalArcSize; ArcPos+=ProcessedArcSize; } Archive *SrcArc=(Archive *)SrcFile; RAROptions *Cmd=SrcArc->GetRAROptions(); int CurPercent=ToPercent(ArcPos,ArcSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize); LastPercent=CurPercent; } } } void ComprDataIO::ShowUnpWrite() { } void ComprDataIO::SetFiles(File *SrcFile,File *DestFile) { if (SrcFile!=NULL) ComprDataIO::SrcFile=SrcFile; if (DestFile!=NULL) ComprDataIO::DestFile=DestFile; LastPercent=-1; } void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size) { *Data=UnpWrAddr; *Size=UnpWrSize; } void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, SecPassword *Password,const byte *Salt,const byte *InitV, uint Lg2Cnt,byte *HashKey,byte *PswCheck) { #ifndef RAR_NOCRYPT if (Encrypt) Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); else Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); #endif } #if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetAV15Encryption() { Decryption=true; Decrypt->SetAV15Encryption(); } #endif #if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetCmt13Encryption() { Decryption=true; Decrypt->SetCmt13Encryption(); } #endif void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size) { UnpackToMemory=true; UnpackToMemoryAddr=Addr; UnpackToMemorySize=Size; } unrar/recvol.cpp000666 000000 000000 00000005041 13343205465 012362 0ustar00000000 000000 #include "rar.hpp" #include "recvol3.cpp" #include "recvol5.cpp" bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) { Archive Arc(Cmd); if (!Arc.Open(Name)) { if (!Silent) ErrHandler.OpenErrorMsg(Name); return false; } RARFORMAT Fmt=RARFMT15; if (Arc.IsArchive(true)) Fmt=Arc.Format; else { byte Sign[REV5_SIGN_SIZE]; Arc.Seek(0,SEEK_SET); if (Arc.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0) Fmt=RARFMT50; } Arc.Close(); // We define RecVol as local variable for proper stack unwinding when // handling exceptions. So it can close and delete files on Cancel. if (Fmt==RARFMT15) { RecVolumes3 RecVol(false); return RecVol.Restore(Cmd,Name,Silent); } else { RecVolumes5 RecVol(false); return RecVol.Restore(Cmd,Name,Silent); } } void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) { wchar RevName[NM]; *RevName=0; if (Arc!=NULL) { // We received .rar or .exe volume as a parameter, trying to find // the matching .rev file number 1. bool NewNumbering=Arc->NewNumbering; wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); wchar RecVolMask[NM]; wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); size_t BaseNamePartLength=VolNumStart-ArcName; wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); FindFile Find; Find.SetMask(RecVolMask); FindData RecData; while (Find.Next(&RecData)) { wchar *Num=GetVolNumPart(RecData.Name); if (*Num!='1') // Name must have "0...01" numeric part. continue; bool FirstVol=true; while (--Num>=RecData.Name && IsDigit(*Num)) if (*Num!='0') { FirstVol=false; break; } if (FirstVol) { wcsncpyz(RevName,RecData.Name,ASIZE(RevName)); Name=RevName; break; } } if (*RevName==0) // First .rev file not found. return; } File RevFile; if (!RevFile.Open(Name)) { ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN. return; } mprintf(L"\n"); byte Sign[REV5_SIGN_SIZE]; bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0; RevFile.Close(); if (Rev5) { RecVolumes5 RecVol(true); RecVol.Test(Cmd,Name); } else { RecVolumes3 RecVol(true); RecVol.Test(Cmd,Name); } } unrar/recvol3.cpp000666 000000 000000 00000031730 13343205465 012451 0ustar00000000 000000 // Buffer size for all volumes involved. static const size_t TotalBufferSize=0x4000000; class RSEncode // Encode or decode data area, one object per one thread. { private: RSCoder RSC; public: void EncodeBuf(); void DecodeBuf(); void Init(int RecVolNumber) {RSC.Init(RecVolNumber);} byte *Buf; byte *OutBuf; int BufStart; int BufEnd; int FileNumber; int RecVolNumber; size_t RecBufferSize; int *Erasures; int EraSize; }; #ifdef RAR_SMP THREAD_PROC(RSEncodeThread) { RSEncode *rs=(RSEncode *)Data; rs->EncodeBuf(); } THREAD_PROC(RSDecodeThread) { RSEncode *rs=(RSEncode *)Data; rs->DecodeBuf(); } #endif RecVolumes3::RecVolumes3(bool TestOnly) { memset(SrcFile,0,sizeof(SrcFile)); if (TestOnly) { #ifdef RAR_SMP RSThreadPool=NULL; #endif } else { Buf.Alloc(TotalBufferSize); memset(SrcFile,0,sizeof(SrcFile)); #ifdef RAR_SMP RSThreadPool=CreateThreadPool(); #endif } } RecVolumes3::~RecVolumes3() { for (size_t I=0;IName;Ext--) if (!IsDigit(*Ext)) if (*Ext=='_' && IsDigit(*(Ext-1))) DigitGroup++; else break; return DigitGroup<2; } bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); wchar *Ext=GetExt(ArcName); bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10. bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0; if (RevName) { NewStyle=IsNewStyleRev(ArcName); while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_')) Ext--; wcscpy(Ext,L"*.*"); FindFile Find; Find.SetMask(ArcName); FindData fd; while (Find.Next(&fd)) { Archive Arc(Cmd); if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) { wcsncpyz(ArcName,fd.Name,ASIZE(ArcName)); break; } } } Archive Arc(Cmd); if (!Arc.WCheckOpen(ArcName)) return false; if (!Arc.Volume) { uiMsg(UIERROR_NOTVOLUME,ArcName); return false; } bool NewNumbering=Arc.NewNumbering; Arc.Close(); wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); wchar RecVolMask[NM]; wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); size_t BaseNamePartLength=VolNumStart-ArcName; wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); int64 RecFileSize=0; // We cannot display "Calculating CRC..." message here, because we do not // know if we'll find any recovery volumes. We'll display it after finding // the first recovery volume. bool CalcCRCMessageDone=false; FindFile Find; Find.SetMask(RecVolMask); FindData RecData; int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; wchar PrevName[NM]; while (Find.Next(&RecData)) { wchar *CurName=RecData.Name; int P[3]; if (!RevName && !NewStyle) { NewStyle=true; wchar *Dot=GetExt(CurName); if (Dot!=NULL) { int LineCount=0; Dot--; while (Dot>CurName && *Dot!='.') { if (*Dot=='_') LineCount++; Dot--; } if (LineCount==2) NewStyle=false; } } if (NewStyle) { if (!CalcCRCMessageDone) { uiMsg(UIMSG_RECVOLCALCCHECKSUM); CalcCRCMessageDone=true; } uiMsg(UIMSG_STRING,CurName); File CurFile; CurFile.TOpen(CurName); CurFile.Seek(0,SEEK_END); int64 Length=CurFile.Tell(); CurFile.Seek(Length-7,SEEK_SET); for (int I=0;I<3;I++) P[2-I]=CurFile.GetByte()+1; uint FileCRC=0; for (int I=0;I<4;I++) FileCRC|=CurFile.GetByte()<<(I*8); uint CalcCRC; CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4); if (FileCRC!=CalcCRC) { uiMsg(UIMSG_CHECKSUM,CurName); continue; } } else { wchar *Dot=GetExt(CurName); if (Dot==NULL) continue; bool WrongParam=false; for (size_t I=0;I=CurName+BaseNamePartLength); P[I]=atoiw(Dot+1); if (P[I]==0 || P[I]>255) WrongParam=true; } if (WrongParam) continue; } if (P[1]+P[2]>255) continue; if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) { uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName); return false; } RecVolNumber=P[1]; FileNumber=P[2]; wcscpy(PrevName,CurName); File *NewFile=new File; NewFile->TOpen(CurName); SrcFile[FileNumber+P[0]-1]=NewFile; FoundRecVolumes++; if (RecFileSize==0) RecFileSize=NewFile->FileLength(); } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return(false); bool WriteFlags[256]; memset(WriteFlags,0,sizeof(WriteFlags)); wchar LastVolName[NM]; *LastVolName=0; for (int CurArcNum=0;CurArcNumTOpen(ArcName); ValidVolume=NewFile->IsArchive(false); if (ValidVolume) { while (NewFile->ReadHeader()!=0) { if (NewFile->GetHeaderType()==HEAD_ENDARC) { uiMsg(UIMSG_STRING,ArcName); if (NewFile->EndArcHead.DataCRC) { uint CalcCRC; CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos); if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC) { ValidVolume=false; uiMsg(UIMSG_CHECKSUM,ArcName); } } break; } NewFile->SeekToNext(); } } if (!ValidVolume) { NewFile->Close(); wchar NewName[NM]; wcscpy(NewName,ArcName); wcscat(NewName,L".bad"); uiMsg(UIMSG_BADARCHIVE,ArcName); uiMsg(UIMSG_RENAMING,ArcName,NewName); RenameFile(ArcName,NewName); } NewFile->Seek(0,SEEK_SET); } if (!ValidVolume) { // It is important to return 'false' instead of aborting here, // so if we are called from extraction, we will be able to continue // extracting. It may happen if .rar and .rev are on read-only disks // like CDs. if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD)) { // We need to display the title of operation before the error message, // to make clear for user that create error is related to recovery // volumes. This is why we cannot use WCreate call here. Title must be // before create error, not after that. uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. uiMsg(UIERROR_RECONSTRUCTING); ErrHandler.CreateErrorMsg(ArcName); return false; } WriteFlags[CurArcNum]=true; MissingVolumes++; if (CurArcNum==FileNumber-1) wcscpy(LastVolName,ArcName); uiMsg(UIMSG_MISSINGVOL,ArcName); uiMsg(UIEVENT_NEWARCHIVE,ArcName); } SrcFile[CurArcNum]=(File*)NewFile; NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering); } uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); if (MissingVolumes==0) { uiMsg(UIERROR_RECVOLALLEXIST); return false; } if (MissingVolumes>FoundRecVolumes) { uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. uiMsg(UIERROR_RECVOLCANNOTFIX); return false; } uiMsg(UIMSG_RECONSTRUCTING); int TotalFiles=FileNumber+RecVolNumber; int Erasures[256],EraSize=0; for (int I=0;IThreads; RSEncode rse[MaxPoolThreads]; #else uint ThreadNumber=1; RSEncode rse[1]; #endif for (uint I=0;IRead(&Buf[I*RecBufferSize],RecBufferSize); if ((size_t)ReadSize!=RecBufferSize) memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize); if (ReadSize>MaxRead) MaxRead=ReadSize; } if (MaxRead==0) break; int CurPercent=ToPercent(ProcessedSize,RecFileSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { uiProcessProgress("RC",ProcessedSize,RecFileSize); LastPercent=CurPercent; } ProcessedSize+=MaxRead; int BlockStart=0; int BlockSize=MaxRead/ThreadNumber; if (BlockSize<0x100) BlockSize=MaxRead; for (uint CurThread=0;BlockStartBuf=&Buf[0]; curenc->BufStart=BlockStart; curenc->BufEnd=BlockStart+BlockSize; curenc->FileNumber=TotalFiles; curenc->RecBufferSize=RecBufferSize; curenc->Erasures=Erasures; curenc->EraSize=EraSize; #ifdef RAR_SMP if (ThreadNumber>1) RSThreadPool->AddTask(RSDecodeThread,(void*)curenc); else curenc->DecodeBuf(); #else curenc->DecodeBuf(); #endif BlockStart+=BlockSize; } #ifdef RAR_SMP RSThreadPool->WaitDone(); #endif // RAR_SMP for (int I=0;IWrite(&Buf[I*RecBufferSize],MaxRead); } for (int I=0;ITell(); CurFile->Seek(Length-7,SEEK_SET); for (int J=0;J<7;J++) CurFile->PutByte(0); } CurFile->Close(); SrcFile[I]=NULL; } if (*LastVolName!=0) { // Truncate the last volume to its real size. Archive Arc(Cmd); if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) && Arc.SearchBlock(HEAD_ENDARC)) { Arc.Seek(Arc.NextBlockPos,SEEK_SET); char Buf[8192]; int ReadSize=Arc.Read(Buf,sizeof(Buf)); int ZeroCount=0; while (ZeroCountDisablePercentage) mprintf(L"\b\b\b\b100%%"); if (!Silent && !Cmd->DisableDone) mprintf(St(MDone)); #endif return true; } void RSEncode::DecodeBuf() { for (int BufPos=BufStart;BufPosDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS); if (FileCRC==CalcCRC) { mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); } else { uiMsg(UIERROR_CHECKSUM,VolName,VolName); ErrHandler.SetErrorCode(RARX_CRC); } NextVolumeName(VolName,ASIZE(VolName),false); } } unrar/recvol5.cpp000666 000000 000000 00000031730 13343205465 012453 0ustar00000000 000000 static const uint MaxVolumes=65535; RecVolumes5::RecVolumes5(bool TestOnly) { RealBuf=NULL; RealReadBuffer=NULL; DataCount=0; RecCount=0; TotalCount=0; RecBufferSize=0; for (uint I=0;IRecRSPtr->ProcessAreaRS(td); } #endif void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) { /* RSCoder16 RS; RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags); uint Count=Encode ? RecCount : MissingVolumes; for (uint I=0;IThreads; #else uint ThreadNumber=1; #endif const uint MinThreadBlock=0x1000; ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock); if (ThreadNumber<1) ThreadNumber=1; uint ThreadDataSize=MaxRead/ThreadNumber; ThreadDataSize+=(ThreadDataSize&1); // Must be even for 16-bit RS coder. #ifdef USE_SSE ThreadDataSize=ALIGN_VALUE(ThreadDataSize,SSE_ALIGNMENT); // Alignment for SSE operations. #endif if (ThreadDataSizeRS==NULL) { td->RS=new RSCoder16; td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags); } td->DataNum=DataNum; td->Data=Data; td->Encode=Encode; td->StartPos=CurPos; size_t EndPos=CurPos+ThreadDataSize; if (EndPos>MaxRead || I==ThreadNumber-1) EndPos=MaxRead; td->Size=EndPos-CurPos; CurPos=EndPos; #ifdef RAR_SMP if (ThreadNumber>1) RecThreadPool->AddTask(RecThreadRS,(void*)td); else ProcessAreaRS(td); #else ProcessAreaRS(td); #endif } #ifdef RAR_SMP RecThreadPool->WaitDone(); #endif // RAR_SMP } void RecVolumes5::ProcessAreaRS(RecRSThreadData *td) { uint Count=td->Encode ? RecCount : MissingVolumes; for (uint I=0;IRS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size); } bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); wchar *Num=GetVolNumPart(ArcName); if (Num==ArcName) return false; // Number part is missing in the name. while (Num>ArcName && IsDigit(*(Num-1))) Num--; if (Num==ArcName) return false; // Entire volume name is numeric, not possible for REV file. wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); wchar FirstVolName[NM]; *FirstVolName=0; int64 RecFileSize=0; FindFile VolFind; VolFind.SetMask(ArcName); FindData fd; uint FoundRecVolumes=0; while (VolFind.Next(&fd)) { Wait(); Archive *Vol=new Archive(Cmd); int ItemPos=-1; if (Vol->WOpen(fd.Name)) { if (CmpExt(fd.Name,L"rev")) { uint RecNum=ReadHeader(Vol,FoundRecVolumes==0); if (RecNum!=0) { if (FoundRecVolumes==0) RecFileSize=Vol->FileLength(); ItemPos=RecNum; FoundRecVolumes++; } } else if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar"))) { if (!Vol->Volume && !Vol->BrokenHeader) { uiMsg(UIERROR_NOTVOLUME,ArcName); return false; } // We work with archive as with raw data file, so we do not want // to spend time to QOpen I/O redirection. Vol->QOpenUnload(); Vol->Seek(0,SEEK_SET); // RAR volume found. Get its number, store the handle in appropriate // array slot, clean slots in between if we had to grow the array. wchar *Num=GetVolNumPart(fd.Name); uint VolNum=0; for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) VolNum+=(*Num-'0')*K; if (VolNum==0 || VolNum>MaxVolumes) continue; size_t CurSize=RecItems.Size(); if (VolNum>CurSize) { RecItems.Alloc(VolNum); for (size_t I=CurSize;If=Vol; Item->New=false; wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); } } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return false; uiMsg(UIMSG_RECVOLCALCCHECKSUM); MissingVolumes=0; for (uint I=0;If!=NULL) { uiMsg(UIMSG_STRING,Item->Name); uint RevCRC; CalcFileSum(Item->f,&RevCRC,NULL,Cmd->Threads,INT64NDF,CALCFSUM_CURPOS); Item->Valid=RevCRC==Item->CRC; if (!Item->Valid) { uiMsg(UIMSG_CHECKSUM,Item->Name); // Close only corrupt REV volumes here. We'll close and rename corrupt // RAR volumes later, if we'll know that recovery is possible. if (I>=DataCount) { Item->f->Close(); Item->f=NULL; FoundRecVolumes--; } } } if (If==NULL || !Item->Valid)) MissingVolumes++; } uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); if (MissingVolumes==0) { uiMsg(UIERROR_RECVOLALLEXIST); return false; } if (MissingVolumes>FoundRecVolumes) { uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. uiMsg(UIERROR_RECVOLCANNOTFIX); return false; } uiMsg(UIMSG_RECONSTRUCTING); // Create missing and rename bad volumes. uint64 MaxVolSize=0; for (uint I=0;IFileSize>MaxVolSize) MaxVolSize=Item->FileSize; if (Item->f!=NULL && !Item->Valid) { Item->f->Close(); wchar NewName[NM]; wcscpy(NewName,Item->Name); wcscat(NewName,L".bad"); uiMsg(UIMSG_BADARCHIVE,Item->Name); uiMsg(UIMSG_RENAMING,Item->Name,NewName); RenameFile(Item->Name,NewName); delete Item->f; Item->f=NULL; } if ((Item->New=(Item->f==NULL))) // Additional parentheses to avoid GCC warning. { wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); uiMsg(UIMSG_CREATING,Item->Name); uiMsg(UIEVENT_NEWARCHIVE,Item->Name); File *NewVol=new File; bool UserReject; if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) { if (!UserReject) ErrHandler.CreateErrorMsg(Item->Name); ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); } NewVol->Prealloc(Item->FileSize); Item->f=NewVol; Item->New=true; } NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); } int64 ProcessedSize=0; int LastPercent=-1; mprintf(L" "); // Even though we already preliminary calculated missing volume number, // let's do it again now, when we have the final and exact information. MissingVolumes=0; ValidFlags=new bool[TotalCount]; for (uint I=0;If!=NULL && !Item->New) ReadSize=Item->f->Read(B,RecBufferSize); if (ReadSize!=RecBufferSize) memset(B+ReadSize,0,RecBufferSize-ReadSize); if (ReadSize>MaxRead) MaxRead=ReadSize; // We can have volumes of different size. Let's use data chunk // for largest volume size. uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize); ProcessRS(Cmd,I,B,DataToProcess,false); } if (MaxRead==0) break; for (uint I=0,J=0;IFileSize); Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); Item->FileSize-=WriteSize; } int CurPercent=ToPercent(ProcessedSize,RecFileSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { uiProcessProgress("RV",ProcessedSize,RecFileSize); LastPercent=CurPercent; } ProcessedSize+=MaxRead; } for (uint I=0;IClose(); delete[] ValidFlags; delete[] Data; #if !defined(SILENT) if (!Cmd->DisablePercentage) mprintf(L"\b\b\b\b100%%"); if (!Silent && !Cmd->DisableDone) mprintf(St(MDone)); #endif return true; } uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) { const size_t FirstReadSize=REV5_SIGN_SIZE+8; byte ShortBuf[FirstReadSize]; if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize) return 0; if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0) return 0; uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4); if (HeaderSize>0x100000 || HeaderSize<=5) return 0; uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE); RawRead Raw(RecFile); if (Raw.Read(HeaderSize)!=HeaderSize) return 0; // Calculate CRC32 of entire header including 4 byte size field. uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4); if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC) return 0; if (Raw.Get1()!=1) // Version check. return 0; DataCount=Raw.Get2(); RecCount=Raw.Get2(); TotalCount=DataCount+RecCount; uint RecNum=Raw.Get2(); // Number of recovery volume. if (RecNum>=TotalCount || TotalCount>MaxVolumes) return 0; uint RevCRC=Raw.Get4(); // CRC of current REV volume. if (FirstRev) { // If we have read the first valid REV file, init data structures // using information from REV header. size_t CurSize=RecItems.Size(); RecItems.Alloc(TotalCount); for (size_t I=CurSize;IDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS)); Valid=RevCRC==RecItems[RecNum].CRC; } if (Valid) { mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); } else { uiMsg(UIERROR_CHECKSUM,VolName,VolName); ErrHandler.SetErrorCode(RARX_CRC); } NextVolumeName(VolName,ASIZE(VolName),false); } } unrar/resource.cpp000666 000000 000000 00000000526 13343205466 012723 0ustar00000000 000000 #include "rar.hpp" #ifndef RARDLL const wchar *St(MSGID StringId) { return StringId; } // Needed for Unix swprintf to convert %s to %ls in legacy language resources. const wchar *StF(MSGID StringId) { static wchar FormattedStr[512]; PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr)); return FormattedStr; } #endif unrar/rijndael.cpp000666 000000 000000 00000035466 13343205466 012677 0ustar00000000 000000 /*************************************************************************** * This code is based on public domain Szymon Stefanek AES implementation: * * http://www.pragmaware.net/software/rijndael/index.php * * * * Dynamic tables generation is based on the Brian Gladman work: * * http://fp.gladman.plus.com/cryptography_technology/rijndael * ***************************************************************************/ #include "rar.hpp" #ifdef USE_SSE #include #endif static byte S[256],S5[256],rcon[30]; static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4]; static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4]; static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4]; inline void Xor128(void *dest,const void *arg1,const void *arg2) { #ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)arg1)[0]^((uint32*)arg2)[0]; ((uint32*)dest)[1]=((uint32*)arg1)[1]^((uint32*)arg2)[1]; ((uint32*)dest)[2]=((uint32*)arg1)[2]^((uint32*)arg2)[2]; ((uint32*)dest)[3]=((uint32*)arg1)[3]^((uint32*)arg2)[3]; #else for (int I=0;I<16;I++) ((byte*)dest)[I]=((byte*)arg1)[I]^((byte*)arg2)[I]; #endif } inline void Xor128(byte *dest,const byte *arg1,const byte *arg2, const byte *arg3,const byte *arg4) { #ifdef ALLOW_MISALIGNED (*(uint32*)dest)=(*(uint32*)arg1)^(*(uint32*)arg2)^(*(uint32*)arg3)^(*(uint32*)arg4); #else for (int I=0;I<4;I++) dest[I]=arg1[I]^arg2[I]^arg3[I]^arg4[I]; #endif } inline void Copy128(byte *dest,const byte *src) { #ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)src)[0]; ((uint32*)dest)[1]=((uint32*)src)[1]; ((uint32*)dest)[2]=((uint32*)src)[2]; ((uint32*)dest)[3]=((uint32*)src)[3]; #else for (int I=0;I<16;I++) dest[I]=src[I]; #endif } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // API ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Rijndael::Rijndael() { if (S[0]==0) GenerateTables(); CBCMode = true; // Always true for RAR. } void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { #ifdef USE_SSE // Check SSE here instead of constructor, so if object is a part of some // structure memset'ed before use, this variable is not lost. int CPUInfo[4]; __cpuid(CPUInfo, 1); AES_NI=(CPUInfo[2] & 0x2000000)!=0; #endif uint uKeyLenInBytes; switch(keyLen) { case 128: uKeyLenInBytes = 16; m_uRounds = 10; break; case 192: uKeyLenInBytes = 24; m_uRounds = 12; break; case 256: uKeyLenInBytes = 32; m_uRounds = 14; break; } byte keyMatrix[_MAX_KEY_COLUMNS][4]; for(uint i = 0; i < uKeyLenInBytes; i++) keyMatrix[i >> 2][i & 3] = key[i]; if (initVector==NULL) memset(m_initVector, 0, sizeof(m_initVector)); else for(int i = 0; i < MAX_IV_SIZE; i++) m_initVector[i] = initVector[i]; keySched(keyMatrix); if(!Encrypt) keyEncToDec(); } void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) { if (inputLen <= 0) return; size_t numBlocks = inputLen/16; #ifdef USE_SSE if (AES_NI) { blockEncryptSSE(input,numBlocks,outBuffer); return; } #endif byte *prevBlock = m_initVector; for(size_t i = numBlocks;i > 0;i--) { byte block[16]; if (CBCMode) Xor128(block,prevBlock,input); else Copy128(block,input); byte temp[4][4]; Xor128(temp,block,m_expandedKey[0]); Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); for(int r = 1; r < m_uRounds-1; r++) { Xor128(temp,outBuffer,m_expandedKey[r]); Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); } Xor128(temp,outBuffer,m_expandedKey[m_uRounds-1]); outBuffer[ 0] = T1[temp[0][0]][1]; outBuffer[ 1] = T1[temp[1][1]][1]; outBuffer[ 2] = T1[temp[2][2]][1]; outBuffer[ 3] = T1[temp[3][3]][1]; outBuffer[ 4] = T1[temp[1][0]][1]; outBuffer[ 5] = T1[temp[2][1]][1]; outBuffer[ 6] = T1[temp[3][2]][1]; outBuffer[ 7] = T1[temp[0][3]][1]; outBuffer[ 8] = T1[temp[2][0]][1]; outBuffer[ 9] = T1[temp[3][1]][1]; outBuffer[10] = T1[temp[0][2]][1]; outBuffer[11] = T1[temp[1][3]][1]; outBuffer[12] = T1[temp[3][0]][1]; outBuffer[13] = T1[temp[0][1]][1]; outBuffer[14] = T1[temp[1][2]][1]; outBuffer[15] = T1[temp[2][3]][1]; Xor128(outBuffer,outBuffer,m_expandedKey[m_uRounds]); prevBlock=outBuffer; outBuffer += 16; input += 16; } Copy128(m_initVector,prevBlock); } #ifdef USE_SSE void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer) { __m128i v = _mm_loadu_si128((__m128i*)m_initVector); __m128i *src=(__m128i*)input; __m128i *dest=(__m128i*)outBuffer; __m128i *rkey=(__m128i*)m_expandedKey; while (numBlocks > 0) { __m128i d = _mm_loadu_si128(src++); if (CBCMode) v = _mm_xor_si128(v, d); else v = d; __m128i r0 = _mm_loadu_si128(rkey); v = _mm_xor_si128(v, r0); for (int i=1; i 0; i--) { byte temp[4][4]; Xor128(temp,input,m_expandedKey[m_uRounds]); Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); for(int r = m_uRounds-1; r > 1; r--) { Xor128(temp,block,m_expandedKey[r]); Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); } Xor128(temp,block,m_expandedKey[1]); block[ 0] = S5[temp[0][0]]; block[ 1] = S5[temp[3][1]]; block[ 2] = S5[temp[2][2]]; block[ 3] = S5[temp[1][3]]; block[ 4] = S5[temp[1][0]]; block[ 5] = S5[temp[0][1]]; block[ 6] = S5[temp[3][2]]; block[ 7] = S5[temp[2][3]]; block[ 8] = S5[temp[2][0]]; block[ 9] = S5[temp[1][1]]; block[10] = S5[temp[0][2]]; block[11] = S5[temp[3][3]]; block[12] = S5[temp[3][0]]; block[13] = S5[temp[2][1]]; block[14] = S5[temp[1][2]]; block[15] = S5[temp[0][3]]; Xor128(block,block,m_expandedKey[0]); if (CBCMode) Xor128(block,block,iv); Copy128((byte*)iv,input); Copy128(outBuffer,block); input += 16; outBuffer += 16; } memcpy(m_initVector,iv,16); } #ifdef USE_SSE void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer) { __m128i initVector = _mm_loadu_si128((__m128i*)m_initVector); __m128i *src=(__m128i*)input; __m128i *dest=(__m128i*)outBuffer; __m128i *rkey=(__m128i*)m_expandedKey; while (numBlocks > 0) { __m128i rl = _mm_loadu_si128(rkey + m_uRounds); __m128i d = _mm_loadu_si128(src++); __m128i v = _mm_xor_si128(rl, d); for (int i=m_uRounds-1; i>0; i--) { __m128i ri = _mm_loadu_si128(rkey + i); v = _mm_aesdec_si128(v, ri); } __m128i r0 = _mm_loadu_si128(rkey); v = _mm_aesdeclast_si128(v, r0); if (CBCMode) v = _mm_xor_si128(v, initVector); initVector = d; _mm_storeu_si128(dest++,v); numBlocks--; } _mm_storeu_si128((__m128i*)m_initVector,initVector); } #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ALGORITHM ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Rijndael::keySched(byte key[_MAX_KEY_COLUMNS][4]) { int j,rconpointer = 0; // Calculate the necessary round keys // The number of calculations depends on keyBits and blockBits int uKeyColumns = m_uRounds - 6; byte tempKey[_MAX_KEY_COLUMNS][4]; // Copy the input key to the temporary key matrix memcpy(tempKey,key,sizeof(tempKey)); int r = 0; int t = 0; // copy values into round key array for(j = 0;(j < uKeyColumns) && (r <= m_uRounds); ) { for(;(j < uKeyColumns) && (t < 4); j++, t++) for (int k=0;k<4;k++) m_expandedKey[r][t][k]=tempKey[j][k]; if(t == 4) { r++; t = 0; } } while(r <= m_uRounds) { tempKey[0][0] ^= S[tempKey[uKeyColumns-1][1]]; tempKey[0][1] ^= S[tempKey[uKeyColumns-1][2]]; tempKey[0][2] ^= S[tempKey[uKeyColumns-1][3]]; tempKey[0][3] ^= S[tempKey[uKeyColumns-1][0]]; tempKey[0][0] ^= rcon[rconpointer++]; if (uKeyColumns != 8) for(j = 1; j < uKeyColumns; j++) for (int k=0;k<4;k++) tempKey[j][k] ^= tempKey[j-1][k]; else { for(j = 1; j < uKeyColumns/2; j++) for (int k=0;k<4;k++) tempKey[j][k] ^= tempKey[j-1][k]; tempKey[uKeyColumns/2][0] ^= S[tempKey[uKeyColumns/2 - 1][0]]; tempKey[uKeyColumns/2][1] ^= S[tempKey[uKeyColumns/2 - 1][1]]; tempKey[uKeyColumns/2][2] ^= S[tempKey[uKeyColumns/2 - 1][2]]; tempKey[uKeyColumns/2][3] ^= S[tempKey[uKeyColumns/2 - 1][3]]; for(j = uKeyColumns/2 + 1; j < uKeyColumns; j++) for (int k=0;k<4;k++) tempKey[j][k] ^= tempKey[j-1][k]; } for(j = 0; (j < uKeyColumns) && (r <= m_uRounds); ) { for(; (j < uKeyColumns) && (t < 4); j++, t++) for (int k=0;k<4;k++) m_expandedKey[r][t][k] = tempKey[j][k]; if(t == 4) { r++; t = 0; } } } } void Rijndael::keyEncToDec() { for(int r = 1; r < m_uRounds; r++) { byte n_expandedKey[4][4]; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { byte *w=m_expandedKey[r][j]; n_expandedKey[j][i]=U1[w[0]][i]^U2[w[1]][i]^U3[w[2]][i]^U4[w[3]][i]; } memcpy(m_expandedKey[r],n_expandedKey,sizeof(m_expandedKey[0])); } } #define ff_poly 0x011b #define ff_hi 0x80 #define FFinv(x) ((x) ? pow[255 - log[x]]: 0) #define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) #define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) #define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) #define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) #define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) #define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) #define fwd_affine(x) \ (w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8)))) #define inv_affine(x) \ (w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8)))) void Rijndael::GenerateTables() { unsigned char pow[512],log[256]; int i = 0, w = 1; do { pow[i] = (byte)w; pow[i + 255] = (byte)w; log[w] = (byte)i++; w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); } while (w != 1); for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++) { rcon[i] = w; w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); } for(int i = 0; i < 256; ++i) { unsigned char b=S[i]=fwd_affine(FFinv((byte)i)); T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b; T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b); T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b); S5[i] = b = FFinv(inv_affine((byte)i)); U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b); U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b); U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b); U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b); } } #if 0 static void TestRijndael(); struct TestRij {TestRij() {TestRijndael();exit(0);}} GlobalTestRij; // Test CBC encryption according to NIST 800-38A. void TestRijndael() { byte IV[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; byte PT[64]={ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10, }; byte Key128[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}; byte Chk128[16]={0x3f,0xf1,0xca,0xa1,0x68,0x1f,0xac,0x09,0x12,0x0e,0xca,0x30,0x75,0x86,0xe1,0xa7}; byte Key192[24]={0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52,0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5,0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b}; byte Chk192[16]={0x08,0xb0,0xe2,0x79,0x88,0x59,0x88,0x81,0xd9,0x20,0xa9,0xe6,0x4f,0x56,0x15,0xcd}; byte Key256[32]={0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}; byte Chk256[16]={0xb2,0xeb,0x05,0xe2,0xc3,0x9b,0xe9,0xfc,0xda,0x6c,0x19,0x07,0x8c,0x6a,0x9d,0x1b}; byte *Key[3]={Key128,Key192,Key256}; byte *Chk[3]={Chk128,Chk192,Chk256}; Rijndael rij; // Declare outside of loop to test re-initialization. for (uint L=0;L<3;L++) { byte Out[16]; wchar Str[sizeof(Out)*2+1]; uint KeyLength=128+L*64; rij.Init(true,Key[L],KeyLength,IV); for (uint I=0;I MAXPAR) J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1). } for (int I=MAXPAR;I0;J--) ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D); ShiftReg[0]=gfMult(GXPol[0],D); } for (int I=0;I0;I--) ELPol[I]^=gfMult(M,ELPol[I-1]); ErrCount=0; // Find roots of error locator polynomial. for (int Root=MAXPAR-DataSize;Root0) for (int I=0;I=0 && DataPosgfSize) E^=0x1100B; // Irreducible field-generator polynomial. } // log(0)+log(x) must be outside of usual log table, so we can set it // to 0 and avoid check for 0 in multiplication parameters. gfLog[0]= 2*gfSize; for (uint I=2*gfSize;I<=4*gfSize;I++) // Results for log(0)+log(x). gfExp[I]=0; } uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field. { return a^b; } uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field. { return gfExp[gfLog[a]+gfLog[b]]; } uint RSCoder16::gfInv(uint a) // Inverse element in Galois field. { return a==0 ? 0:gfExp[gfSize-gfLog[a]]; } bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags) { ND = DataCount; NR = RecCount; NE = 0; Decoding=ValidityFlags!=NULL; if (Decoding) { delete[] ValidFlags; ValidFlags=new bool[ND + NR]; for (uint I = 0; I < ND + NR; I++) ValidFlags[I]=ValidityFlags[I]; for (uint I = 0; I < ND; I++) if (!ValidFlags[I]) NE++; uint ValidECC=0; for (uint I = ND; I < ND + NR; I++) if (ValidFlags[I]) ValidECC++; if (NE > ValidECC || NE == 0 || ValidECC == 0) return false; } if (ND + NR > gfSize || NR > ND || ND == 0 || NR == 0) return false; delete[] MX; if (Decoding) { MX=new uint[NE * ND]; MakeDecoderMatrix(); InvertDecoderMatrix(); } else { MX=new uint[NR * ND]; MakeEncoderMatrix(); } return true; } void RSCoder16::MakeEncoderMatrix() { // Create Cauchy encoder generator matrix. Skip trivial "1" diagonal rows, // which would just copy source data to destination. for (uint I = 0; I < NR; I++) for (uint J = 0; J < ND; J++) MX[I * ND + J] = gfInv( gfAdd( (I+ND), J) ); } void RSCoder16::MakeDecoderMatrix() { // Create Cauchy decoder matrix. Skip trivial rows matching valid data // units and containing "1" on main diagonal. Such rows would just copy // source data to destination and they have no real value for us. // Include rows only for broken data units and replace them by first // available valid recovery code rows. for (uint Flag=0, R=ND, Dest=0; Flag < ND; Flag++) if (!ValidFlags[Flag]) // For every broken data unit. { while (!ValidFlags[R]) // Find a valid recovery unit. R++; for (uint J = 0; J < ND; J++) // And place its row to matrix. MX[Dest*ND + J] = gfInv( gfAdd(R,J) ); Dest++; R++; } } // Apply Gauss–Jordan elimination to find inverse of decoder matrix. // We have the square NDxND matrix, but we do not store its trivial // diagonal "1" rows matching valid data, so we work with NExND matrix. // Our original Cauchy matrix does not contain 0, so we skip search // for non-zero pivot. void RSCoder16::InvertDecoderMatrix() { uint *MI=new uint[NE * ND]; // We'll create inverse matrix here. memset(MI, 0, ND * NE * sizeof(*MI)); // Initialize to identity matrix. for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++) { while (ValidFlags[Kf]) // Skip trivial rows. Kf++; MI[Kr * ND + Kf] = 1; // Set diagonal 1. } // Kr is the number of row in our actual reduced NE x ND matrix, // which does not contain trivial diagonal 1 rows. // Kf is the number of row in full ND x ND matrix with all trivial rows // included. for (uint Kr = 0, Kf = 0; Kf < ND; Kr++, Kf++) // Select pivot row. { while (ValidFlags[Kf] && Kf < ND) { // Here we process trivial diagonal 1 rows matching valid data units. // Their processing can be simplified comparing to usual rows. // In full version of elimination we would set MX[I * ND + Kf] to zero // after MI[..]^=, but we do not need it for matrix inversion. for (uint I = 0; I < NE; I++) MI[I * ND + Kf] ^= MX[I * ND + Kf]; Kf++; } if (Kf == ND) break; uint *MXk = MX + Kr * ND; // k-th row of main matrix. uint *MIk = MI + Kr * ND; // k-th row of inversion matrix. uint PInv = gfInv( MXk[Kf] ); // Pivot inverse. // Divide the pivot row by pivot, so pivot cell contains 1. for (uint I = 0; I < ND; I++) { MXk[I] = gfMul( MXk[I], PInv ); MIk[I] = gfMul( MIk[I], PInv ); } for (uint I = 0; I < NE; I++) if (I != Kr) // For all rows except containing the pivot cell. { // Apply Gaussian elimination Mij -= Mkj * Mik / pivot. // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik. uint *MXi = MX + I * ND; // i-th row of main matrix. uint *MIi = MI + I * ND; // i-th row of inversion matrix. uint Mik = MXi[Kf]; // Cell in pivot position. for (uint J = 0; J < ND; J++) { MXi[J] ^= gfMul(MXk[J] , Mik); MIi[J] ^= gfMul(MIk[J] , Mik); } } } // Copy data to main matrix. for (uint I = 0; I < NE * ND; I++) MX[I] = MI[I]; delete[] MI; } #if 0 // Multiply matrix to data vector. When encoding, it contains data in Data // and stores error correction codes in Out. When decoding it contains // broken data followed by ECC in Data and stores recovered data to Out. // We do not use this function now, everything is moved to UpdateECC. void RSCoder16::Process(const uint *Data, uint *Out) { uint ProcData[gfSize]; for (uint I = 0; I < ND; I++) ProcData[I]=Data[I]; if (Decoding) { // Replace broken data units with first available valid recovery codes. // 'Data' array must contain recovery codes after data. for (uint I=0, R=ND, Dest=0; I < ND; I++) if (!ValidFlags[I]) // For every broken data unit. { while (!ValidFlags[R]) // Find a valid recovery unit. R++; ProcData[I]=Data[R]; R++; } } uint H=Decoding ? NE : NR; for (uint I = 0; I < H; I++) { uint R = 0; // Result of matrix row multiplication to data. uint *MXi=MX + I * ND; for (uint J = 0; J < ND; J++) R ^= gfMul(MXi[J], ProcData[J]); Out[I] = R; } } #endif // We update ECC in blocks by applying every data block to all ECC blocks. // This function applies one data block to one ECC block. void RSCoder16::UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize) { if (DataNum==0) // Init ECC data. memset(ECC, 0, BlockSize); bool DirectAccess; #ifdef LITTLE_ENDIAN // We can access data and ECC directly if we have little endian 16 bit uint. DirectAccess=sizeof(ushort)==2; #else DirectAccess=false; #endif #ifdef USE_SSE if (DirectAccess && SSE_UpdateECC(DataNum,ECCNum,Data,ECC,BlockSize)) return; #endif if (ECCNum==0) { if (DataLogSize!=BlockSize) { delete[] DataLog; DataLog=new uint[BlockSize]; DataLogSize=BlockSize; } if (DirectAccess) for (size_t I=0; I>8; ((byte *)&T1L)[I]=gfMul(I<<4,M); ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8; ((byte *)&T2L)[I]=gfMul(I<<8,M); ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8; ((byte *)&T3L)[I]=gfMul(I<<12,M); ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8; } size_t Pos=0; __m128i LowByteMask=_mm_set1_epi16(0xff); // 00ff00ff...00ff __m128i Low4Mask=_mm_set1_epi8(0xf); // 0f0f0f0f...0f0f __m128i High4Mask=_mm_slli_epi16(Low4Mask,4); // f0f0f0f0...f0f0 for (; Pos+2*sizeof(__m128i)<=BlockSize; Pos+=2*sizeof(__m128i)) { // We process two 128 bit chunks of source data at once. __m128i *D=(__m128i *)(Data+Pos); // Place high bytes of both chunks to one variable and low bytes to // another, so we can use the table lookup multiplication for 16 values // 4 bit length each at once. __m128i HighBytes0=_mm_srli_epi16(D[0],8); __m128i LowBytes0=_mm_and_si128(D[0],LowByteMask); __m128i HighBytes1=_mm_srli_epi16(D[1],8); __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask); __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1); __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1); // Multiply bits 0..3 of low bytes. Store low and high product bytes // separately in cumulative sum variables. __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask); __m128i LowBytesMultSum=_mm_shuffle_epi8(T0L,LowBytesLow4); __m128i HighBytesMultSum=_mm_shuffle_epi8(T0H,LowBytesLow4); // Multiply bits 4..7 of low bytes. Store low and high product bytes separately. __m128i LowBytesHigh4=_mm_and_si128(LowBytes,High4Mask); LowBytesHigh4=_mm_srli_epi16(LowBytesHigh4,4); __m128i LowBytesHigh4MultLow=_mm_shuffle_epi8(T1L,LowBytesHigh4); __m128i LowBytesHigh4MultHigh=_mm_shuffle_epi8(T1H,LowBytesHigh4); // Add new product to existing sum, low and high bytes separately. LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow); HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh); // Multiply bits 0..3 of high bytes. Store low and high product bytes separately. __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask); __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4); __m128i HighBytesLow4MultHigh=_mm_shuffle_epi8(T2H,HighBytesLow4); // Add new product to existing sum, low and high bytes separately. LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesLow4MultLow); HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesLow4MultHigh); // Multiply bits 4..7 of high bytes. Store low and high product bytes separately. __m128i HighBytesHigh4=_mm_and_si128(HighBytes,High4Mask); HighBytesHigh4=_mm_srli_epi16(HighBytesHigh4,4); __m128i HighBytesHigh4MultLow=_mm_shuffle_epi8(T3L,HighBytesHigh4); __m128i HighBytesHigh4MultHigh=_mm_shuffle_epi8(T3H,HighBytesHigh4); // Add new product to existing sum, low and high bytes separately. LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesHigh4MultLow); HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesHigh4MultHigh); // Combine separate low and high cumulative sum bytes to 16-bit words. __m128i HighBytesHigh4Mult0=_mm_unpacklo_epi8(LowBytesMultSum,HighBytesMultSum); __m128i HighBytesHigh4Mult1=_mm_unpackhi_epi8(LowBytesMultSum,HighBytesMultSum); // Add result to ECC. __m128i *StoreECC=(__m128i *)(ECC+Pos); StoreECC[0]=_mm_xor_si128(StoreECC[0],HighBytesHigh4Mult0); StoreECC[1]=_mm_xor_si128(StoreECC[1],HighBytesHigh4Mult1); } // If we have non 128 bit aligned data in the end of block, process them // in a usual way. We cannot do the same in the beginning of block, // because Data and ECC can have different alignment offsets. for (; Pos=0;I--) if (FindStack[I]!=NULL) delete FindStack[I]; } SCAN_CODE ScanTree::GetNext(FindData *FD) { if (Depth<0) return SCAN_DONE; #ifndef SILENT uint LoopCount=0; #endif SCAN_CODE FindCode; while (1) { if (*CurMask==0 && !GetNextMask()) return SCAN_DONE; #ifndef SILENT // Let's return some ticks to system or WinRAR can become irresponsible // while scanning files in command like "winrar a -r arc c:\file.ext". // Also we reset system sleep timer here. if ((++LoopCount & 0x3ff)==0) Wait(); #endif FindCode=FindProc(FD); if (FindCode==SCAN_ERROR) { Errors++; continue; } if (FindCode==SCAN_NEXT) continue; if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS) continue; if (FindCode==SCAN_DONE && GetNextMask()) continue; if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS) if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH)) continue; break; } return FindCode; } // For masks like dir1\dir2*\*.ext in non-recursive mode. bool ScanTree::ExpandFolderMask() { bool WildcardFound=false; uint SlashPos=0; for (int I=0;CurMask[I]!=0;I++) { if (CurMask[I]=='?' || CurMask[I]=='*') WildcardFound=true; if (WildcardFound && IsPathDiv(CurMask[I])) { // First path separator position after folder wildcard mask. // In case of dir1\dir2*\dir3\name.ext mask it may point not to file // name, so we cannot use PointToName() here. SlashPos=I; break; } } wchar Mask[NM]; wcsncpyz(Mask,CurMask,ASIZE(Mask)); Mask[SlashPos]=0; // Prepare the list of all folders matching the wildcard mask. ExpandedFolderList.Reset(); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) if (FD.IsDir) { wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name)); // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched // by such mask. Skipping empty dir with dir*\*.* confused some users. wchar *LastMask=PointToName(FD.Name); if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) RemoveNameFromPath(FD.Name); ExpandedFolderList.AddString(FD.Name); } if (ExpandedFolderList.ItemsCount()==0) return false; // Return the first matching folder name now. ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)); return true; } // For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask // and '*\dir2*\file.ext' filter. Masks without folder wildcards are // returned as is. bool ScanTree::GetFilteredMask() { // If we have some matching folders left for non-recursive folder wildcard // mask, we return it here. if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask))) return true; FolderWildcards=false; FilterList.Reset(); if (!FileMasks->GetString(CurMask,ASIZE(CurMask))) return false; // Check if folder wildcards present. bool WildcardFound=false; uint FolderWildcardCount=0; uint SlashPos=0; for (int I=0;CurMask[I]!=0;I++) { if (CurMask[I]=='?' || CurMask[I]=='*') WildcardFound=true; if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I])) { if (WildcardFound) { // Calculate a number of folder wildcards in current mask. FolderWildcardCount++; WildcardFound=false; } if (FolderWildcardCount==0) SlashPos=I; // Slash position before first folder wildcard mask. } } if (FolderWildcardCount==0) return true; FolderWildcards=true; // Global folder wildcards flag. // If we have only one folder wildcard component and -r is missing or -r- // is specified, prepare matching folders in non-recursive mode. // We assume -r for masks like dir1*\dir2*\file*, because it is complicated // to fast find them using OS file find API call. if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1) return ExpandFolderMask(); wchar Filter[NM]; // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders. wcscpy(Filter,L"*"); AddEndSlash(Filter,ASIZE(Filter)); // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*' wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos; wcsncatz(Filter,WildName,ASIZE(Filter)); // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched // by such mask. Skipping empty dir with dir*\*.* confused some users. wchar *LastMask=PointToName(Filter); if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) *LastMask=0; FilterList.AddString(Filter); bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]); if (RelativeDrive) SlashPos++; // Use "d:" instead of "d" for d:* mask. CurMask[SlashPos]=0; if (!RelativeDrive) // Keep d: mask as is, not convert to d:\* { // We need to append "\*" both for -ep1 to work correctly and to // convert d:\* masks previously truncated to d: back to original form. AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } return true; } bool ScanTree::GetNextMask() { if (!GetFilteredMask()) return false; #ifdef _WIN_ALL UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); #endif // We wish to scan entire disk if mask like c:\ is specified // regardless of recursion mode. Use c:\*.* mask when need to scan only // the root directory. ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; wchar *Name=PointToName(CurMask); if (*Name==0) wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) { AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } SpecPathLength=Name-CurMask; Depth=0; wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); return true; } SCAN_CODE ScanTree::FindProc(FindData *FD) { if (*CurMask==0) return SCAN_NEXT; bool FastFindFile=false; if (FindStack[Depth]==NULL) // No FindFile object for this depth yet. { bool Wildcards=IsWildcard(CurMask); // If we have a file name without wildcards, we can try to use // FastFind to optimize speed. For example, in Unix it results in // stat call instead of opendir/readdir/closedir. bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks); // Link check is important for NTFS, where links can have "Directory" // attribute, but we do not want to recurse to them in "get links" mode. bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink); // SearchAll means that we'll use "*" mask for search, so we'll find // subdirectories and will be able to recurse into them. // We do not use "*" for directories at any level or for files // at top level in recursion mode. We always comrpess the entire directory // if folder wildcard is specified. bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS || FolderWildcards && Recurse!=RECURSE_DISABLE || Wildcards && Recurse==RECURSE_WILDCARDS || ScanEntireDisk && Recurse!=RECURSE_DISABLE); if (Depth==0) SearchAllInRoot=SearchAll; if (SearchAll || Wildcards) { // Create the new FindFile object for wildcard based search. FindStack[Depth]=new FindFile; wchar SearchMask[NM]; wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask)); if (SearchAll) SetName(SearchMask,MASKALL,ASIZE(SearchMask)); FindStack[Depth]->SetMask(SearchMask); } else { // Either we failed to fast find or we found a file or we found // a directory in RECURSE_DISABLE mode, so we do not need to scan it. // We can return here and do not need to process further. // We need to process further only if we fast found a directory. if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE) { // Return SCAN_SUCCESS if we found a file. SCAN_CODE RetCode=SCAN_SUCCESS; if (!FindCode) { // Return SCAN_ERROR if problem is more serious than just // "file not found". RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT; // If we failed to find an object, but our current mask is excluded, // we skip this object and avoid indicating an error. if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) RetCode=SCAN_NEXT; else { ErrHandler.OpenErrorMsg(ErrArcName,CurMask); // User asked to return RARX_NOFILES and not RARX_OPEN here. ErrHandler.SetErrorCode(RARX_NOFILES); } } // If we searched only for one file or directory in "fast find" // (without a wildcard) mode, let's set masks to zero, // so calling function will know that current mask is used // and next one must be read from mask list for next call. // It is not necessary for directories, because even in "fast find" // mode, directory recursing will quit by (Depth < 0) condition, // which returns SCAN_DONE to calling function. *CurMask=0; return RetCode; } // We found a directory using only FindFile::FastFind function. FastFindFile=true; } } if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks)) { // We cannot find anything more in directory either because of // some error or just as result of all directory entries already read. bool Error=FD->Error; if (Error) ScanError(Error); wchar DirName[NM]; *DirName=0; // Going to at least one directory level higher. delete FindStack[Depth]; FindStack[Depth--]=NULL; while (Depth>=0 && FindStack[Depth]==NULL) Depth--; if (Depth < 0) { // Directories scanned both in normal and FastFindFile mode, // finally exit from scan here, by (Depth < 0) condition. if (Error) Errors++; return SCAN_DONE; } wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER); if (Slash!=NULL) { wchar Mask[NM]; wcsncpyz(Mask,Slash,ASIZE(Mask)); if (DepthIsDir) { FD->Flags|=FDDF_SECONDDIR; return Error ? SCAN_ERROR:SCAN_SUCCESS; } return Error ? SCAN_ERROR:SCAN_NEXT; } // Link check is required for NTFS links, not for Unix. if (FD->IsDir && (!GetLinks || !FD->IsLink)) { // If we found the directory in top (Depth==0) directory // and if we are not in "fast find" (directory name only as argument) // or in recurse (SearchAll was set when opening the top directory) mode, // we do not recurse into this directory. We either return it by itself // or skip it. if (!FastFindFile && Depth==0 && !SearchAllInRoot) return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT; // Let's check if directory name is excluded, so we do not waste // time searching in directory, which will be excluded anyway. if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) || Cmd->ExclDirByAttr(FD->FileAttr))) { // If we are here in "fast find" mode, it means that entire directory // specified in command line is excluded. Then we need to return // SCAN_DONE to go to next mask and avoid the infinite loop // in GetNext() function. Such loop would be possible in case of // SCAN_NEXT code and "rar a arc dir -xdir" command. return FastFindFile ? SCAN_DONE:SCAN_NEXT; } wchar Mask[NM]; wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) { uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); return SCAN_ERROR; } AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,Mask,ASIZE(CurMask)); Depth++; // We need to use OrigCurMask for depths less than SetAllMaskDepth // and "*" for depths equal or larger than SetAllMaskDepth. // It is important when "fast finding" directories at Depth > 0. // For example, if current directory is RootFolder and we compress // the following directories structure: // RootFolder // +--Folder1 // | +--Folder2 // | +--Folder3 // +--Folder4 // with 'rar a -r arcname Folder2' command, rar could add not only // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using // "*" mask at all levels. We need to use "*" mask inside of Folder2, // but return to "Folder2" mask when completing scanning Folder2. // We can rewrite SearchAll expression above to avoid fast finding // directories at Depth > 0, but then 'rar a -r arcname Folder2' // will add the empty Folder2 and do not add its contents. if (FastFindFile) SetAllMaskDepth=Depth; } if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES)) return SCAN_NEXT; return SCAN_SUCCESS; } void ScanTree::ScanError(bool &Error) { #ifdef _WIN_ALL if (Error) { // Get attributes of parent folder and do not display an error // if it is reparse point. We cannot scan contents of standard // Windows reparse points like "C:\Documents and Settings" // and we do not want to issue numerous useless errors for them. // We cannot just check FD->FileAttr here, it can be undefined // if we process "folder\*" mask or if we process "folder" mask, // but "folder" is inaccessible. wchar *Slash=PointToName(CurMask); if (Slash>CurMask) { *(Slash-1)=0; DWORD Attr=GetFileAttributes(CurMask); *(Slash-1)=CPATHDIVIDER; if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) Error=false; } // Do not display an error if we cannot scan contents of // "System Volume Information" folder. Normally it is not accessible. if (wcsstr(CurMask,L"System Volume Information\\")!=NULL) Error=false; } #endif if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) Error=false; if (Error) { if (ErrDirList!=NULL) ErrDirList->AddString(CurMask); if (ErrDirSpecPathLength!=NULL) ErrDirSpecPathLength->Push((uint)SpecPathLength); wchar FullName[NM]; // This conversion works for wildcard masks too. ConvertNameToFull(CurMask,FullName,ASIZE(FullName)); uiMsg(UIERROR_DIRSCAN,FullName); ErrHandler.SysErrMsg(); } } unrar/secpassword.cpp000666 000000 000000 00000013003 13343205466 013423 0ustar00000000 000000 #include "rar.hpp" #if defined(_WIN_ALL) typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); #ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE #define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 #define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00 #define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01 #endif class CryptLoader { private: HMODULE hCrypt; bool LoadCalled; public: CryptLoader() { hCrypt=NULL; pCryptProtectMemory=NULL; pCryptUnprotectMemory=NULL; LoadCalled=false; } ~CryptLoader() { if (hCrypt!=NULL) FreeLibrary(hCrypt); hCrypt=NULL; pCryptProtectMemory=NULL; pCryptUnprotectMemory=NULL; }; void Load() { if (!LoadCalled) { hCrypt = LoadSysLibrary(L"Crypt32.dll"); if (hCrypt != NULL) { // Available since Vista. pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory"); pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory"); } LoadCalled=true; } } CRYPTPROTECTMEMORY pCryptProtectMemory; CRYPTUNPROTECTMEMORY pCryptUnprotectMemory; }; // We need to call FreeLibrary when RAR is exiting. CryptLoader GlobalCryptLoader; #endif SecPassword::SecPassword() { CrossProcess=false; Set(L""); } SecPassword::~SecPassword() { Clean(); } void SecPassword::Clean() { PasswordSet=false; cleandata(Password,sizeof(Password)); } // When we call memset in end of function to clean local variables // for security reason, compiler optimizer can remove such call. // So we use our own function for this purpose. void cleandata(void *data,size_t size) { if (data==NULL || size==0) return; #if defined(_WIN_ALL) && defined(_MSC_VER) SecureZeroMemory(data,size); #else // 'volatile' is required. Otherwise optimizers can remove this function // if cleaning local variables, which are not used after that. volatile byte *d = (volatile byte *)data; for (size_t i=0;i parameter, so we need to take into account both sizes. memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess); } void SecPassword::Get(wchar *Psw,size_t MaxSize) { if (PasswordSet) { Process(Password,ASIZE(Password),Psw,MaxSize,false); Psw[MaxSize-1]=0; } else *Psw=0; } void SecPassword::Set(const wchar *Psw) { if (*Psw==0) { PasswordSet=false; memset(Password,0,sizeof(Password)); } else { PasswordSet=true; Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true); } } size_t SecPassword::Length() { wchar Plain[MAXPASSWORD]; Get(Plain,ASIZE(Plain)); size_t Length=wcslen(Plain); cleandata(Plain,ASIZE(Plain)); return Length; } bool SecPassword::operator == (SecPassword &psw) { // We cannot compare encoded data directly, because there is no guarantee // than encryption function will always produce the same result for same // data (salt?) and because we do not clean the rest of password buffer // after trailing zero before encoding password. So we decode first. wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD]; Get(Plain1,ASIZE(Plain1)); psw.Get(Plain2,ASIZE(Plain2)); bool Result=wcscmp(Plain1,Plain2)==0; cleandata(Plain1,ASIZE(Plain1)); cleandata(Plain2,ASIZE(Plain2)); return Result; } void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) { // CryptProtectMemory is not available in UWP and CryptProtectData // increases data size not allowing in place conversion. #if defined(_WIN_ALL) // Try to utilize the secure Crypt[Un]ProtectMemory if possible. if (GlobalCryptLoader.pCryptProtectMemory==NULL) GlobalCryptLoader.Load(); size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE; DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS; if (Encode) { if (GlobalCryptLoader.pCryptProtectMemory!=NULL) { if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags)) { ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed"); ErrHandler.SysErrMsg(); ErrHandler.Exit(RARX_FATAL); } return; } } else { if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL) { if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags)) { ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed"); ErrHandler.SysErrMsg(); ErrHandler.Exit(RARX_FATAL); } return; } } #endif // CryptProtectMemory is not available, so only slightly obfuscate data. uint Key; #ifdef _WIN_ALL Key=GetCurrentProcessId(); #elif defined(_UNIX) Key=getpid(); #else Key=0; // Just an arbitrary value. #endif for (size_t I=0;I 100% Public Domain */ #ifndef SFX_MODULE #define SHA1_UNROLL #endif /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifdef LITTLE_ENDIAN #define blk0(i) (block->l[i] = ByteSwap32(block->l[i])) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rotl32(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} #define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} #define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rotl32(v,5);w=rotl32(w,30);} #define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rotl32(v,5);w=rotl32(w,30);} #define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rotl32(v,5);w=rotl32(w,30);} /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(uint32 state[5], uint32 workspace[16], const byte buffer[64], bool inplace) { uint32 a, b, c, d, e; union CHAR64LONG16 { unsigned char c[64]; uint32 l[16]; } *block; if (inplace) block = (CHAR64LONG16*)buffer; else { block = (CHAR64LONG16*)workspace; memcpy(block, buffer, 64); } /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; #ifdef SHA1_UNROLL /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); #else for (uint I=0;;I+=5) { R0(a,b,c,d,e, I+0); if (I==15) break; R0(e,a,b,c,d, I+1); R0(d,e,a,b,c, I+2); R0(c,d,e,a,b, I+3); R0(b,c,d,e,a, I+4); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); for (uint I=20;I<=35;I+=5) { R2(a,b,c,d,e,I+0); R2(e,a,b,c,d,I+1); R2(d,e,a,b,c,I+2); R2(c,d,e,a,b,I+3); R2(b,c,d,e,a,I+4); } for (uint I=40;I<=55;I+=5) { R3(a,b,c,d,e,I+0); R3(e,a,b,c,d,I+1); R3(d,e,a,b,c,I+2); R3(c,d,e,a,b,I+3); R3(b,c,d,e,a,I+4); } for (uint I=60;I<=75;I+=5) { R4(a,b,c,d,e,I+0); R4(e,a,b,c,d,I+1); R4(d,e,a,b,c,I+2); R4(c,d,e,a,b,I+3); R4(b,c,d,e,a,I+4); } #endif /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } /* Initialize new context */ void sha1_init(sha1_context* context) { context->count = 0; /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; } /* Run your data through this. */ void sha1_process( sha1_context * context, const unsigned char * data, size_t len) { size_t i, j = (size_t)(context->count & 63); context->count += len; if ((j + len) > 63) { memcpy(context->buffer+j, data, (i = 64-j)); uint32 workspace[16]; SHA1Transform(context->state, workspace, context->buffer, true); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, workspace, data+i, false); j = 0; } else i = 0; if (len > i) memcpy(context->buffer+j, data+i, len - i); } void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len) { size_t i, j = (size_t)(context->count & 63); context->count += len; if ((j + len) > 63) { memcpy(context->buffer+j, data, (i = 64-j)); uint32 workspace[16]; SHA1Transform(context->state, workspace, context->buffer, true); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, workspace, data+i, false); for (uint k = 0; k < 16; k++) RawPut4(workspace[k],(void*)(data+i+k*4)); } j = 0; } else i = 0; if (len > i) memcpy(context->buffer+j, data+i, len - i); } /* Add padding and return the message digest. */ void sha1_done( sha1_context* context, uint32 digest[5]) { uint32 workspace[16]; uint64 BitLength = context->count * 8; uint BufPos = (uint)context->count & 0x3f; context->buffer[BufPos++] = 0x80; // Padding the message with "1" bit. if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. { if (BufPos>56) { while (BufPos<64) context->buffer[BufPos++] = 0; BufPos=0; } if (BufPos==0) SHA1Transform(context->state, workspace, context->buffer, true); memset(context->buffer+BufPos,0,56-BufPos); } RawPutBE4((uint32)(BitLength>>32), context->buffer + 56); RawPutBE4((uint32)(BitLength), context->buffer + 60); SHA1Transform(context->state, workspace, context->buffer, true); for (uint i = 0; i < 5; i++) digest[i] = context->state[i]; /* Wipe variables */ sha1_init(context); } unrar/sha256.cpp000666 000000 000000 00000010226 13343205466 012102 0ustar00000000 000000 #include "rar.hpp" #include "sha256.hpp" static const uint32 K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; // SHA-256 functions. We could optimize Ch and Maj a little, // but with no visible speed benefit. #define Ch(x, y, z) ((x & y) ^ (~x & z)) #define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) // Sigma functions. #define Sg0(x) (rotr32(x, 2) ^ rotr32(x,13) ^ rotr32(x, 22)) #define Sg1(x) (rotr32(x, 6) ^ rotr32(x,11) ^ rotr32(x, 25)) #define sg0(x) (rotr32(x, 7) ^ rotr32(x,18) ^ (x >> 3)) #define sg1(x) (rotr32(x,17) ^ rotr32(x,19) ^ (x >> 10)) void sha256_init(sha256_context *ctx) { ctx->H[0] = 0x6a09e667; // Set the initial hash value. ctx->H[1] = 0xbb67ae85; ctx->H[2] = 0x3c6ef372; ctx->H[3] = 0xa54ff53a; ctx->H[4] = 0x510e527f; ctx->H[5] = 0x9b05688c; ctx->H[6] = 0x1f83d9ab; ctx->H[7] = 0x5be0cd19; ctx->Count = 0; // Processed data counter. } static void sha256_transform(sha256_context *ctx) { uint32 W[64]; // Words of message schedule. uint32 v[8]; // FIPS a, b, c, d, e, f, g, h working variables. // Prepare message schedule. for (uint I = 0; I < 16; I++) W[I] = RawGetBE4(ctx->Buffer + I * 4); for (uint I = 16; I < 64; I++) W[I] = sg1(W[I-2]) + W[I-7] + sg0(W[I-15]) + W[I-16]; uint32 *H=ctx->H; v[0]=H[0]; v[1]=H[1]; v[2]=H[2]; v[3]=H[3]; v[4]=H[4]; v[5]=H[5]; v[6]=H[6]; v[7]=H[7]; for (uint I = 0; I < 64; I++) { uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I]; // It is possible to eliminate variable copying if we unroll loop // and rename variables every time. But my test did not show any speed // gain on i7 for such full or partial unrolling. v[7] = v[6]; v[6] = v[5]; v[5] = v[4]; v[4] = v[3] + T1; // It works a little faster when moved here from beginning of loop. uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]); v[3] = v[2]; v[2] = v[1]; v[1] = v[0]; v[0] = T1 + T2; } H[0]+=v[0]; H[1]+=v[1]; H[2]+=v[2]; H[3]+=v[3]; H[4]+=v[4]; H[5]+=v[5]; H[6]+=v[6]; H[7]+=v[7]; } void sha256_process(sha256_context *ctx, const void *Data, size_t Size) { const byte *Src=(const byte *)Data; size_t BufPos = (uint)ctx->Count & 0x3f; ctx->Count+=Size; while (Size > 0) { size_t BufSpace=sizeof(ctx->Buffer)-BufPos; size_t CopySize=Size>BufSpace ? BufSpace:Size; memcpy(ctx->Buffer+BufPos,Src,CopySize); Src+=CopySize; BufPos+=CopySize; Size-=CopySize; if (BufPos == 64) { BufPos = 0; sha256_transform(ctx); } } } void sha256_done(sha256_context *ctx, byte *Digest) { uint64 BitLength = ctx->Count * 8; uint BufPos = (uint)ctx->Count & 0x3f; ctx->Buffer[BufPos++] = 0x80; // Padding the message with "1" bit. if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. { if (BufPos>56) { while (BufPos<64) ctx->Buffer[BufPos++] = 0; BufPos=0; } if (BufPos==0) sha256_transform(ctx); memset(ctx->Buffer+BufPos,0,56-BufPos); } RawPutBE4((uint32)(BitLength>>32), ctx->Buffer + 56); RawPutBE4((uint32)(BitLength), ctx->Buffer + 60); sha256_transform(ctx); RawPutBE4(ctx->H[0], Digest + 0); RawPutBE4(ctx->H[1], Digest + 4); RawPutBE4(ctx->H[2], Digest + 8); RawPutBE4(ctx->H[3], Digest + 12); RawPutBE4(ctx->H[4], Digest + 16); RawPutBE4(ctx->H[5], Digest + 20); RawPutBE4(ctx->H[6], Digest + 24); RawPutBE4(ctx->H[7], Digest + 28); sha256_init(ctx); } unrar/smallfn.cpp000666 000000 000000 00000000401 13343205466 012520 0ustar00000000 000000 #include "rar.hpp" int ToPercent(int64 N1,int64 N2) { if (N2SrcLength) DestSize=SrcLength; OemToCharBuffA(Src,Dest,(DWORD)DestSize); Dest[DestSize-1]=0; #else if (Dest!=Src) strncpyz(Dest,Src,DestSize); #endif } // Convert archived names and comments to Unicode. // Allows user to select a code page in GUI. void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding) { #if defined(_WIN_ALL) // Console Windows RAR. if (Encoding==ACTW_UTF8) UtfToWide(Src,Dest,DestSize); else { Array NameA; if (Encoding==ACTW_OEM) { NameA.Alloc(DestSize+1); IntToExt(Src,&NameA[0],NameA.Size()); Src=&NameA[0]; } CharToWide(Src,Dest,DestSize); } #else // RAR for Unix. if (Encoding==ACTW_UTF8) UtfToWide(Src,Dest,DestSize); else CharToWide(Src,Dest,DestSize); #endif // Ensure that we return a zero terminate string for security reason. // While [Jni]CharToWide might already do it, be protected in case of future // changes in these functions. if (DestSize>0) Dest[DestSize-1]=0; } int stricomp(const char *s1,const char *s2) { #ifdef _WIN_ALL return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else while (toupper(*s1)==toupper(*s2)) { if (*s1==0) return 0; s1++; s2++; } return s1 < s2 ? -1 : 1; #endif } int strnicomp(const char *s1,const char *s2,size_t n) { #ifdef _WIN_ALL // If we specify 'n' exceeding the actual string length, CompareString goes // beyond the trailing zero and compares garbage. So we need to limit 'n' // to real string length. // It is important to use strnlen (or memchr(...,0)) instead of strlen, // because data can be not zero terminated. size_t l1=Min(strnlen(s1,n),n); size_t l2=Min(strnlen(s2,n),n); return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; #else if (n==0) return 0; while (toupper(*s1)==toupper(*s2)) { if (*s1==0 || --n==0) return 0; s1++; s2++; } return s1 < s2 ? -1 : 1; #endif } wchar* RemoveEOL(wchar *Str) { for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) Str[I]=0; return Str; } wchar* RemoveLF(wchar *Str) { for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) Str[I]=0; return Str; } unsigned char loctolower(unsigned char ch) { #if defined(_WIN_ALL) // Convert to LPARAM first to avoid a warning in 64 bit mode. // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] return (int)(LPARAM)CharLowerA((LPSTR)(uintptr_t)ch); #else return tolower(ch); #endif } unsigned char loctoupper(unsigned char ch) { #if defined(_WIN_ALL) // Convert to LPARAM first to avoid a warning in 64 bit mode. // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] return (int)(LPARAM)CharUpperA((LPSTR)(uintptr_t)ch); #else return toupper(ch); #endif } // toupper with English only results if English input is provided. // It avoids Turkish (small i) -> (big I with dot) conversion problem. // We do not define 'ch' as 'int' to avoid necessity to cast all // signed chars passed to this function to unsigned char. unsigned char etoupper(unsigned char ch) { if (ch=='i') return 'I'; return toupper(ch); } // Unicode version of etoupper. wchar etoupperw(wchar ch) { if (ch=='i') return 'I'; return toupperw(ch); } // We do not want to cast every signed char to unsigned when passing to // isdigit, so we implement the replacement. Shall work for Unicode too. // If chars are signed, conversion from char to int could generate negative // values, resulting in undefined behavior in standard isdigit. bool IsDigit(int ch) { return ch>='0' && ch<='9'; } // We do not want to cast every signed char to unsigned when passing to // isspace, so we implement the replacement. Shall work for Unicode too. // If chars are signed, conversion from char to int could generate negative // values, resulting in undefined behavior in standard isspace. bool IsSpace(int ch) { return ch==' ' || ch=='\t'; } // We do not want to cast every signed char to unsigned when passing to // isalpha, so we implement the replacement. Shall work for Unicode too. // If chars are signed, conversion from char to int could generate negative // values, resulting in undefined behavior in standard function. bool IsAlpha(int ch) { return ch>='A' && ch<='Z' || ch>='a' && ch<='z'; } void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize) { uint A=0,W=0; // ASCII and Unicode hex output positions. for (uint I=0;I> 4; uint Low=Bin[I] & 0xf; uint HighHex=High>9 ? 'a'+High-10:'0'+High; uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low; if (HexA!=NULL && A0) HexA[A]=0; if (HexW!=NULL && HexSize>0) HexW[W]=0; } #ifndef SFX_MODULE uint GetDigits(uint Number) { uint Digits=1; while (Number>=10) { Number/=10; Digits++; } return Digits; } #endif bool LowAscii(const char *Str) { for (int I=0;Str[I]!=0;I++) if ((byte)Str[I]<32 || (byte)Str[I]>127) return false; return true; } bool LowAscii(const wchar *Str) { for (int I=0;Str[I]!=0;I++) { // We convert wchar_t to uint just in case if some compiler // uses signed wchar_t. if ((uint)Str[I]<32 || (uint)Str[I]>127) return false; } return true; } int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison. { #if defined(_UNIX) return wcscmp(s1,s2); #else return wcsicomp(s1,s2); #endif } int wcsnicompc(const wchar *s1,const wchar *s2,size_t n) { #if defined(_UNIX) return wcsncmp(s1,s2,n); #else return wcsnicomp(s1,s2,n); #endif } // Safe strncpy: copies maxlen-1 max and always returns zero terminated dest. char* strncpyz(char *dest, const char *src, size_t maxlen) { if (maxlen>0) { strncpy(dest,src,maxlen-1); dest[maxlen-1]=0; } return dest; } // Safe wcsncpy: copies maxlen-1 max and always returns zero terminated dest. wchar* wcsncpyz(wchar *dest, const wchar *src, size_t maxlen) { if (maxlen>0) { wcsncpy(dest,src,maxlen-1); dest[maxlen-1]=0; } return dest; } // Safe strncat: resulting dest length cannot exceed maxlen and dest // is always zero terminated. Note that 'maxlen' parameter defines the entire // dest buffer size and is not compatible with standard strncat. char* strncatz(char* dest, const char* src, size_t maxlen) { size_t Length = strlen(dest); int avail=int(maxlen - Length - 1); if (avail > 0) strncat(dest, src, avail); return dest; } // Safe wcsncat: resulting dest length cannot exceed maxlen and dest // is always zero terminated. Note that 'maxlen' parameter defines the entire // dest buffer size and is not compatible with standard wcsncat. wchar* wcsncatz(wchar* dest, const wchar* src, size_t maxlen) { size_t Length = wcslen(dest); int avail=int(maxlen - Length - 1); if (avail > 0) wcsncat(dest, src, avail); return dest; } void itoa(int64 n,char *Str,size_t MaxSize) { char NumStr[50]; size_t Pos=0; int Neg=n < 0 ? 1 : 0; if (Neg) n=-n; do { if (Pos+1>=MaxSize-Neg) break; NumStr[Pos++]=char(n%10)+'0'; n=n/10; } while (n!=0); if (Neg) NumStr[Pos++]='-'; for (size_t I=0;I=MaxSize-Neg) break; NumStr[Pos++]=wchar(n%10)+'0'; n=n/10; } while (n!=0); if (Neg) NumStr[Pos++]='-'; for (size_t I=0;I= ASIZE(StrTable)) StrNum=0; wchar *Str=StrTable[StrNum]; CharToWide(Src,Str,MaxLength); Str[MaxLength-1]=0; return Str; } // Parse string containing parameters separated with spaces. // Support quote marks. Param can be NULL to return the pointer to next // parameter, which can be used to estimate the buffer size for Param. const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize) { while (IsSpace(*CmdLine)) CmdLine++; if (*CmdLine==0) return NULL; size_t ParamSize=0; bool Quote=false; while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine))) { if (*CmdLine=='\"') { if (CmdLine[1]=='\"') { // Insert the quote character instead of two adjoining quote characters. if (Param!=NULL && ParamSize StrW(strlen(Str)); CharToWide(Str,&StrW[0],StrW.Size()); AddString(&StrW[0]); } void StringList::AddString(const wchar *Str) { if (Str==NULL) Str=L""; size_t PrevSize=StringData.Size(); StringData.Add(wcslen(Str)+1); wcscpy(&StringData[PrevSize],Str); StringsCount++; } bool StringList::GetStringA(char *Str,size_t MaxLength) { Array StrW(MaxLength); if (!GetString(&StrW[0],StrW.Size())) return false; WideToChar(&StrW[0],Str,MaxLength); return true; } bool StringList::GetString(wchar *Str,size_t MaxLength) { wchar *StrPtr; if (!GetString(&StrPtr)) return false; wcsncpyz(Str,StrPtr,MaxLength); return true; } #ifndef SFX_MODULE bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) { SavePosition(); Rewind(); bool RetCode=true; while (StringNum-- >=0) if (!GetString(Str,MaxLength)) { RetCode=false; break; } RestorePosition(); return RetCode; } #endif wchar* StringList::GetString() { wchar *Str; GetString(&Str); return Str; } bool StringList::GetString(wchar **Str) { if (CurPos>=StringData.Size()) // No more strings left unprocessed. { if (Str!=NULL) *Str=NULL; return false; } wchar *CurStr=&StringData[CurPos]; CurPos+=wcslen(CurStr)+1; if (Str!=NULL) *Str=CurStr; return true; } void StringList::Rewind() { CurPos=0; } #ifndef SFX_MODULE bool StringList::Search(const wchar *Str,bool CaseSensitive) { SavePosition(); Rewind(); bool Found=false; wchar *CurStr; while (GetString(&CurStr)) { if (Str!=NULL && CurStr!=NULL) if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0) continue; Found=true; break; } RestorePosition(); return Found; } #endif #ifndef SFX_MODULE void StringList::SavePosition() { if (SavePosNumber0) { SavePosNumber--; CurPos=SaveCurPos[SavePosNumber]; } } #endif unrar/suballoc.cpp000666 000000 000000 00000017347 13343205466 012711 0ustar00000000 000000 /**************************************************************************** * This file is part of PPMd project * * Written and distributed to public domain by Dmitry Shkarin 1997, * * 1999-2000 * * Contents: memory allocation routines * ****************************************************************************/ static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK)); static const uint FIXED_UNIT_SIZE=12; SubAllocator::SubAllocator() { Clean(); } void SubAllocator::Clean() { SubAllocatorSize=0; } inline void SubAllocator::InsertNode(void* p,int indx) { ((RAR_NODE*) p)->next=FreeList[indx].next; FreeList[indx].next=(RAR_NODE*) p; } inline void* SubAllocator::RemoveNode(int indx) { RAR_NODE* RetVal=FreeList[indx].next; FreeList[indx].next=RetVal->next; return RetVal; } inline uint SubAllocator::U2B(int NU) { // We calculate the size of units in bytes based on real UNIT_SIZE. // In original implementation it was 8*NU+4*NU. return UNIT_SIZE*NU; } // Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be // equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address. inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items) { return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); } inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx) { int i, UDiff=Indx2Units[OldIndx]-Indx2Units[NewIndx]; byte* p=((byte*) pv)+U2B(Indx2Units[NewIndx]); if (Indx2Units[i=Units2Indx[UDiff-1]] != UDiff) { InsertNode(p,--i); p += U2B(i=Indx2Units[i]); UDiff -= i; } InsertNode(p,Units2Indx[UDiff-1]); } void SubAllocator::StopSubAllocator() { if ( SubAllocatorSize ) { SubAllocatorSize=0; free(HeapStart); } } bool SubAllocator::StartSubAllocator(int SASize) { uint t=SASize << 20; if (SubAllocatorSize == t) return true; StopSubAllocator(); // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size // can be larger. So let's recalculate the allocated size and add two more // units: one as reserve for HeapEnd overflow checks and another // to provide the space to correctly align UnitsStart. uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE; if ((HeapStart=(byte *)malloc(AllocSize)) == NULL) { ErrHandler.MemoryError(); return false; } // HeapEnd did not present in original algorithm. We added it to control // invalid memory access attempts when processing corrupt archived data. HeapEnd=HeapStart+AllocSize-UNIT_SIZE; SubAllocatorSize=t; return true; } void SubAllocator::InitSubAllocator() { int i, k; memset(FreeList,0,sizeof(FreeList)); pText=HeapStart; // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value // because of alignment and larger pointer fields size. // So we define UNIT_SIZE for this larger size and adjust memory // pointers accordingly. // Size2 is (HiUnit-LoUnit) memory area size to allocate as originally // supposed by compression algorithm. It is 7/8 of total allocated size. uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7); // RealSize2 is the real adjusted size of (HiUnit-LoUnit) memory taking // into account that our UNIT_SIZE can be larger than FIXED_UNIT_SIZE. uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE; // Size1 is the size of memory area from HeapStart to FakeUnitsStart // as originally supposed by compression algorithm. This area can contain // different data types, both single symbols and structures. uint Size1=SubAllocatorSize-Size2; // Real size of this area. We correct it according to UNIT_SIZE vs // FIXED_UNIT_SIZE difference. Also we add one more UNIT_SIZE // to compensate a possible reminder from Size1/FIXED_UNIT_SIZE, // which would be lost otherwise. We add UNIT_SIZE instead of // this Size1%FIXED_UNIT_SIZE reminder, because it allows to align // UnitsStart easily and adding more than reminder is ok for algorithm. uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE; // RealSize1 must be divided by UNIT_SIZE without a reminder, so UnitsStart // is aligned to UNIT_SIZE. It is important for those architectures, // where a proper memory alignment is mandatory. Since we produce RealSize1 // multiplying by UNIT_SIZE, this condition is always true. So LoUnit, // UnitsStart, HeapStart are properly aligned, LoUnit=UnitsStart=HeapStart+RealSize1; // When we reach FakeUnitsStart, we restart the model. It is where // the original algorithm expected to see UnitsStart. Real UnitsStart // can have a larger value. FakeUnitsStart=HeapStart+Size1; HiUnit=LoUnit+RealSize2; for (i=0,k=1;i < N1 ;i++,k += 1) Indx2Units[i]=k; for (k++;i < N1+N2 ;i++,k += 2) Indx2Units[i]=k; for (k++;i < N1+N2+N3 ;i++,k += 3) Indx2Units[i]=k; for (k++;i < N1+N2+N3+N4;i++,k += 4) Indx2Units[i]=k; for (GlueCount=k=i=0;k < 128;k++) { i += (Indx2Units[i] < k+1); Units2Indx[k]=i; } } inline void SubAllocator::GlueFreeBlocks() { RARPPM_MEM_BLK s0, * p, * p1; int i, k, sz; if (LoUnit != HiUnit) *LoUnit=0; for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++) while ( FreeList[i].next ) { p=(RARPPM_MEM_BLK*)RemoveNode(i); p->insertAt(&s0); p->Stamp=0xFFFF; p->NU=Indx2Units[i]; } for (p=s0.next;p != &s0;p=p->next) while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU < 0x10000) { p1->remove(); p->NU += p1->NU; } while ((p=s0.next) != &s0) { for (p->remove(), sz=p->NU;sz > 128;sz -= 128, p=MBPtr(p,128)) InsertNode(p,N_INDEXES-1); if (Indx2Units[i=Units2Indx[sz-1]] != sz) { k=sz-Indx2Units[--i]; InsertNode(MBPtr(p,sz-k),k-1); } InsertNode(p,i); } } void* SubAllocator::AllocUnitsRare(int indx) { if ( !GlueCount ) { GlueCount = 255; GlueFreeBlocks(); if ( FreeList[indx].next ) return RemoveNode(indx); } int i=indx; do { if (++i == N_INDEXES) { GlueCount--; i=U2B(Indx2Units[indx]); int j=FIXED_UNIT_SIZE*Indx2Units[indx]; if (FakeUnitsStart - pText > j) { FakeUnitsStart -= j; UnitsStart -= i; return UnitsStart; } return NULL; } } while ( !FreeList[i].next ); void* RetVal=RemoveNode(i); SplitBlock(RetVal,i,indx); return RetVal; } inline void* SubAllocator::AllocUnits(int NU) { int indx=Units2Indx[NU-1]; if ( FreeList[indx].next ) return RemoveNode(indx); void* RetVal=LoUnit; LoUnit += U2B(Indx2Units[indx]); if (LoUnit <= HiUnit) return RetVal; LoUnit -= U2B(Indx2Units[indx]); return AllocUnitsRare(indx); } void* SubAllocator::AllocContext() { if (HiUnit != LoUnit) return (HiUnit -= UNIT_SIZE); if ( FreeList->next ) return RemoveNode(0); return AllocUnitsRare(0); } void* SubAllocator::ExpandUnits(void* OldPtr,int OldNU) { int i0=Units2Indx[OldNU-1], i1=Units2Indx[OldNU-1+1]; if (i0 == i1) return OldPtr; void* ptr=AllocUnits(OldNU+1); if ( ptr ) { memcpy(ptr,OldPtr,U2B(OldNU)); InsertNode(OldPtr,i0); } return ptr; } void* SubAllocator::ShrinkUnits(void* OldPtr,int OldNU,int NewNU) { int i0=Units2Indx[OldNU-1], i1=Units2Indx[NewNU-1]; if (i0 == i1) return OldPtr; if ( FreeList[i1].next ) { void* ptr=RemoveNode(i1); memcpy(ptr,OldPtr,U2B(NewNU)); InsertNode(OldPtr,i0); return ptr; } else { SplitBlock(OldPtr,i0,i1); return OldPtr; } } void SubAllocator::FreeUnits(void* ptr,int OldNU) { InsertNode(ptr,Units2Indx[OldNU-1]); } unrar/system.cpp000666 000000 000000 00000010226 13343205466 012416 0ustar00000000 000000 #include "rar.hpp" static int SleepTime=0; void InitSystemOptions(int SleepTime) { ::SleepTime=SleepTime; } #if !defined(SFX_MODULE) void SetPriority(int Priority) { #ifdef _WIN_ALL uint PriorityClass; int PriorityLevel; if (Priority<1 || Priority>15) return; if (Priority==1) { PriorityClass=IDLE_PRIORITY_CLASS; PriorityLevel=THREAD_PRIORITY_IDLE; // Background mode for Vista, can be slow for many small files. // if (WinNT()>=WNT_VISTA) // SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); } else if (Priority<7) { PriorityClass=IDLE_PRIORITY_CLASS; PriorityLevel=Priority-4; } else if (Priority==7) { PriorityClass=BELOW_NORMAL_PRIORITY_CLASS; PriorityLevel=THREAD_PRIORITY_ABOVE_NORMAL; } else if (Priority<10) { PriorityClass=NORMAL_PRIORITY_CLASS; PriorityLevel=Priority-7; } else if (Priority==10) { PriorityClass=ABOVE_NORMAL_PRIORITY_CLASS; PriorityLevel=THREAD_PRIORITY_NORMAL; } else { PriorityClass=HIGH_PRIORITY_CLASS; PriorityLevel=Priority-13; } SetPriorityClass(GetCurrentProcess(),PriorityClass); SetThreadPriority(GetCurrentThread(),PriorityLevel); #ifdef RAR_SMP ThreadPool::SetPriority(PriorityLevel); #endif #endif } #endif // Monotonic clock. Like clock(), returns time passed in CLOCKS_PER_SEC items. // In Android 5+ and Unix usual clock() returns time spent by all threads // together, so we cannot use it to measure time intervals anymore. clock_t MonoClock() { return clock(); } void Wait() { if (ErrHandler.UserBreak) ErrHandler.Exit(RARX_USERBREAK); #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (SleepTime!=0) { static clock_t LastTime=MonoClock(); if (MonoClock()-LastTime>10*CLOCKS_PER_SEC/1000) { Sleep(SleepTime); LastTime=MonoClock(); } } #endif #if defined(_WIN_ALL) // Reset system sleep timer to prevent system going sleep. SetThreadExecutionState(ES_SYSTEM_REQUIRED); #endif } #if defined(_WIN_ALL) && !defined(SFX_MODULE) void Shutdown(POWER_MODE Mode) { HANDLE hToken; TOKEN_PRIVILEGES tkp; if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) { LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0); } if (Mode==POWERMODE_OFF) ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); if (Mode==POWERMODE_SLEEP) SetSuspendState(FALSE,FALSE,FALSE); if (Mode==POWERMODE_HIBERNATE) SetSuspendState(TRUE,FALSE,FALSE); if (Mode==POWERMODE_RESTART) ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); } #endif #if defined(_WIN_ALL) // Load library from Windows System32 folder. Use this function to prevent // loading a malicious code from current folder or same folder as exe. HMODULE WINAPI LoadSysLibrary(const wchar *Name) { wchar SysDir[NM]; if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0) return NULL; MakeName(SysDir,Name,SysDir,ASIZE(SysDir)); return LoadLibrary(SysDir); } bool IsUserAdmin() { SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup; BOOL b = AllocateAndInitializeSid(&NtAuthority,2,SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); if (b) { if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) b = FALSE; FreeSid(AdministratorsGroup); } return b!=FALSE; } #endif #ifdef USE_SSE SSE_VERSION _SSE_Version=GetSSEVersion(); SSE_VERSION GetSSEVersion() { int CPUInfo[4]; __cpuid(CPUInfo, 7); if ((CPUInfo[1] & 0x20)!=0) return SSE_AVX2; __cpuid(CPUInfo, 1); if ((CPUInfo[2] & 0x80000)!=0) return SSE_SSE41; if ((CPUInfo[2] & 0x200)!=0) return SSE_SSSE3; if ((CPUInfo[3] & 0x4000000)!=0) return SSE_SSE2; if ((CPUInfo[3] & 0x2000000)!=0) return SSE_SSE; return SSE_NONE; } #endif unrar/threadmisc.cpp000666 000000 000000 00000011125 13343205466 013214 0ustar00000000 000000 // Typically we use the same global thread pool for all RAR modules. static ThreadPool *GlobalPool=NULL; static uint GlobalPoolUseCount=0; static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection) { #ifdef _WIN_ALL InitializeCriticalSection(CritSection); return true; #elif defined(_UNIX) return pthread_mutex_init(CritSection,NULL)==0; #endif } static inline void CriticalSectionDelete(CRITSECT_HANDLE *CritSection) { #ifdef _WIN_ALL DeleteCriticalSection(CritSection); #elif defined(_UNIX) pthread_mutex_destroy(CritSection); #endif } static inline void CriticalSectionStart(CRITSECT_HANDLE *CritSection) { #ifdef _WIN_ALL EnterCriticalSection(CritSection); #elif defined(_UNIX) pthread_mutex_lock(CritSection); #endif } static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection) { #ifdef _WIN_ALL LeaveCriticalSection(CritSection); #elif defined(_UNIX) pthread_mutex_unlock(CritSection); #endif } static struct GlobalPoolCreateSync { CRITSECT_HANDLE CritSection; GlobalPoolCreateSync() { CriticalSectionCreate(&CritSection); } ~GlobalPoolCreateSync() { CriticalSectionDelete(&CritSection); } } PoolCreateSync; ThreadPool* CreateThreadPool() { CriticalSectionStart(&PoolCreateSync.CritSection); if (GlobalPoolUseCount++ == 0) GlobalPool=new ThreadPool(MaxPoolThreads); // We use a simple thread pool, which does not allow to add tasks from // different functions and threads in the same time. It is ok for RAR, // but UnRAR.dll can be used in multithreaded environment. So if one of // threads requests a copy of global pool and another copy is already // in use, we create and return a new pool instead of existing global. if (GlobalPoolUseCount > 1) { ThreadPool *Pool = new ThreadPool(MaxPoolThreads); CriticalSectionEnd(&PoolCreateSync.CritSection); return Pool; } CriticalSectionEnd(&PoolCreateSync.CritSection); return GlobalPool; } void DestroyThreadPool(ThreadPool *Pool) { if (Pool!=NULL) { CriticalSectionStart(&PoolCreateSync.CritSection); if (Pool==GlobalPool && GlobalPoolUseCount > 0 && --GlobalPoolUseCount == 0) delete GlobalPool; // To correctly work in multithreaded environment UnRAR.dll creates // new pools if global pool is already in use. We delete such pools here. if (Pool!=GlobalPool) delete Pool; CriticalSectionEnd(&PoolCreateSync.CritSection); } } static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data) { #ifdef _UNIX /* pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); */ pthread_t pt; int Code=pthread_create(&pt,NULL/*&attr*/,Proc,Data); if (Code!=0) { wchar Msg[100]; swprintf(Msg,ASIZE(Msg),L"\npthread_create failed, code %d\n",Code); ErrHandler.GeneralErrMsg(Msg); ErrHandler.SysErrMsg(); ErrHandler.Exit(RARX_FATAL); } return pt; #else DWORD ThreadId; HANDLE hThread=CreateThread(NULL,0x10000,Proc,Data,0,&ThreadId); if (hThread==NULL) { ErrHandler.GeneralErrMsg(L"CreateThread failed"); ErrHandler.SysErrMsg(); ErrHandler.Exit(RARX_FATAL); } return hThread; #endif } static void ThreadClose(THREAD_HANDLE hThread) { #ifdef _UNIX pthread_join(hThread,NULL); #else CloseHandle(hThread); #endif } #ifdef _WIN_ALL static void CWaitForSingleObject(HANDLE hHandle) { DWORD rc=WaitForSingleObject(hHandle,INFINITE); if (rc==WAIT_FAILED) { ErrHandler.GeneralErrMsg(L"\nWaitForMultipleObjects error %d, GetLastError %d",rc,GetLastError()); ErrHandler.Exit(RARX_FATAL); } } #endif #ifdef _UNIX static void cpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { int rc=pthread_cond_wait(cond,mutex); if (rc!=0) { ErrHandler.GeneralErrMsg(L"\npthread_cond_wait error %d",rc); ErrHandler.Exit(RARX_FATAL); } } #endif uint GetNumberOfCPU() { #ifndef RAR_SMP return 1; #else #ifdef _UNIX #ifdef _SC_NPROCESSORS_ONLN uint Count=(uint)sysconf(_SC_NPROCESSORS_ONLN); return Count<1 ? 1:Count; #elif defined(_APPLE) uint Count; size_t Size=sizeof(Count); return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1; #endif #else // !_UNIX DWORD_PTR ProcessMask; DWORD_PTR SystemMask; if (!GetProcessAffinityMask(GetCurrentProcess(),&ProcessMask,&SystemMask)) return 1; uint Count=0; for (DWORD_PTR Mask=1;Mask!=0;Mask<<=1) if ((ProcessMask & Mask)!=0) Count++; return Count<1 ? 1:Count; #endif #endif // RAR_SMP } uint GetNumberOfThreads() { uint NumCPU=GetNumberOfCPU(); if (NumCPU<1) return 1; if (NumCPU>MaxPoolThreads) return MaxPoolThreads; return NumCPU; } unrar/threadpool.cpp000666 000000 000000 00000013005 13343205466 013231 0ustar00000000 000000 #include "rar.hpp" #ifdef RAR_SMP #include "threadmisc.cpp" #ifdef _WIN_ALL int ThreadPool::ThreadPriority=THREAD_PRIORITY_NORMAL; #endif ThreadPool::ThreadPool(uint MaxThreads) { MaxAllowedThreads = MaxThreads; if (MaxAllowedThreads>MaxPoolThreads) MaxAllowedThreads=MaxPoolThreads; if (MaxAllowedThreads==0) MaxAllowedThreads=1; ThreadsCreatedCount=0; // If we have more threads than queue size, we'll hang on pool destroying, // not releasing all waiting threads. if (MaxAllowedThreads>ASIZE(TaskQueue)) MaxAllowedThreads=ASIZE(TaskQueue); Closing=false; bool Success = CriticalSectionCreate(&CritSection); #ifdef _WIN_ALL QueuedTasksCnt=CreateSemaphore(NULL,0,ASIZE(TaskQueue),NULL); NoneActive=CreateEvent(NULL,TRUE,TRUE,NULL); Success=Success && QueuedTasksCnt!=NULL && NoneActive!=NULL; #elif defined(_UNIX) AnyActive = false; QueuedTasksCnt = 0; Success=Success && pthread_cond_init(&AnyActiveCond,NULL)==0 && pthread_mutex_init(&AnyActiveMutex,NULL)==0 && pthread_cond_init(&QueuedTasksCntCond,NULL)==0 && pthread_mutex_init(&QueuedTasksCntMutex,NULL)==0; #endif if (!Success) { ErrHandler.GeneralErrMsg(L"\nThread pool initialization failed."); ErrHandler.Exit(RARX_FATAL); } QueueTop = 0; QueueBottom = 0; ActiveThreads = 0; } ThreadPool::~ThreadPool() { WaitDone(); Closing=true; #ifdef _WIN_ALL ReleaseSemaphore(QueuedTasksCnt,ASIZE(TaskQueue),NULL); #elif defined(_UNIX) // Threads still can access QueuedTasksCnt for a short time after WaitDone(), // so lock is required. We would occassionally hang without it. pthread_mutex_lock(&QueuedTasksCntMutex); QueuedTasksCnt+=ASIZE(TaskQueue); pthread_mutex_unlock(&QueuedTasksCntMutex); pthread_cond_broadcast(&QueuedTasksCntCond); #endif for(uint I=0;IPoolThreadLoop(); return 0; } void ThreadPool::PoolThreadLoop() { QueueEntry Task; while (GetQueuedTask(&Task)) { Task.Proc(Task.Param); CriticalSectionStart(&CritSection); if (--ActiveThreads == 0) { #ifdef _WIN_ALL SetEvent(NoneActive); #elif defined(_UNIX) pthread_mutex_lock(&AnyActiveMutex); AnyActive=false; pthread_cond_signal(&AnyActiveCond); pthread_mutex_unlock(&AnyActiveMutex); #endif } CriticalSectionEnd(&CritSection); } } bool ThreadPool::GetQueuedTask(QueueEntry *Task) { #ifdef _WIN_ALL CWaitForSingleObject(QueuedTasksCnt); #elif defined(_UNIX) pthread_mutex_lock(&QueuedTasksCntMutex); while (QueuedTasksCnt==0) cpthread_cond_wait(&QueuedTasksCntCond,&QueuedTasksCntMutex); QueuedTasksCnt--; pthread_mutex_unlock(&QueuedTasksCntMutex); #endif if (Closing) return false; CriticalSectionStart(&CritSection); *Task = TaskQueue[QueueBottom]; QueueBottom = (QueueBottom + 1) % ASIZE(TaskQueue); CriticalSectionEnd(&CritSection); return true; } // Add task to queue. We assume that it is always called from main thread, // it allows to avoid any locks here. We process collected tasks only // when WaitDone is called. void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) { if (ThreadsCreatedCount == 0) CreateThreads(); // If queue is full, wait until it is empty. if ((QueueTop + 1) % ASIZE(TaskQueue) == QueueBottom) WaitDone(); TaskQueue[QueueTop].Proc = Proc; TaskQueue[QueueTop].Param = Data; QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); } // Start queued tasks and wait until all threads are inactive. // We assume that it is always called from main thread, when pool threads // are sleeping yet. void ThreadPool::WaitDone() { // We add ASIZE(TaskQueue) for case if TaskQueue array size is not // a power of two. Negative numbers would not suit our purpose here. ActiveThreads=(QueueTop+ASIZE(TaskQueue)-QueueBottom) % ASIZE(TaskQueue); if (ActiveThreads==0) return; #ifdef _WIN_ALL ResetEvent(NoneActive); ReleaseSemaphore(QueuedTasksCnt,ActiveThreads,NULL); CWaitForSingleObject(NoneActive); #elif defined(_UNIX) AnyActive=true; // Threads reset AnyActive before accessing QueuedTasksCnt and even // preceding WaitDone() call does not guarantee that some slow thread // is not accessing QueuedTasksCnt now. So lock is necessary. pthread_mutex_lock(&QueuedTasksCntMutex); QueuedTasksCnt+=ActiveThreads; pthread_mutex_unlock(&QueuedTasksCntMutex); pthread_cond_broadcast(&QueuedTasksCntCond); pthread_mutex_lock(&AnyActiveMutex); while (AnyActive) cpthread_cond_wait(&AnyActiveCond,&AnyActiveMutex); pthread_mutex_unlock(&AnyActiveMutex); #endif } #endif // RAR_SMP unrar/timefn.cpp000666 000000 000000 00000017075 13343205467 012366 0ustar00000000 000000 #include "rar.hpp" void RarTime::GetLocal(RarLocalTime *lt) { #ifdef _WIN_ALL FILETIME ft; GetWinFT(&ft); FILETIME lft; if (WinNT() < WNT_VISTA) { // SystemTimeToTzSpecificLocalTime based code produces 1 hour error on XP. FileTimeToLocalFileTime(&ft,&lft); } else { // We use these functions instead of FileTimeToLocalFileTime according to // MSDN recommendation: "To account for daylight saving time // when converting a file time to a local time ..." SYSTEMTIME st1,st2; FileTimeToSystemTime(&ft,&st1); SystemTimeToTzSpecificLocalTime(NULL,&st1,&st2); SystemTimeToFileTime(&st2,&lft); // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. FILETIME rft; SystemTimeToFileTime(&st1,&rft); uint64 Corrected=INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)- INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime); lft.dwLowDateTime=(DWORD)Corrected; lft.dwHighDateTime=(DWORD)(Corrected>>32); } SYSTEMTIME st; FileTimeToSystemTime(&lft,&st); lt->Year=st.wYear; lt->Month=st.wMonth; lt->Day=st.wDay; lt->Hour=st.wHour; lt->Minute=st.wMinute; lt->Second=st.wSecond; lt->wDay=st.wDayOfWeek; lt->yDay=lt->Day-1; static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; for (uint I=1;IMonth && I<=ASIZE(mdays);I++) lt->yDay+=mdays[I-1]; if (lt->Month>2 && IsLeapYear(lt->Year)) lt->yDay++; #else time_t ut=GetUnix(); struct tm *t; t=localtime(&ut); lt->Year=t->tm_year+1900; lt->Month=t->tm_mon+1; lt->Day=t->tm_mday; lt->Hour=t->tm_hour; lt->Minute=t->tm_min; lt->Second=t->tm_sec; lt->wDay=t->tm_wday; lt->yDay=t->tm_yday; #endif lt->Reminder=(itime % TICKS_PER_SECOND); } void RarTime::SetLocal(RarLocalTime *lt) { #ifdef _WIN_ALL SYSTEMTIME st; st.wYear=lt->Year; st.wMonth=lt->Month; st.wDay=lt->Day; st.wHour=lt->Hour; st.wMinute=lt->Minute; st.wSecond=lt->Second; st.wMilliseconds=0; st.wDayOfWeek=0; FILETIME lft; if (SystemTimeToFileTime(&st,&lft)) { FILETIME ft; if (WinNT() < WNT_VISTA) { // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP. LocalFileTimeToFileTime(&lft,&ft); } else { // Reverse procedure which we do in GetLocal. SYSTEMTIME st1,st2; FileTimeToSystemTime(&lft,&st2); // st2 might be unequal to st, because we added lt->Reminder to lft. TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1); SystemTimeToFileTime(&st1,&ft); // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. FILETIME rft; SystemTimeToFileTime(&st2,&rft); uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)- INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime); ft.dwLowDateTime=(DWORD)Corrected; ft.dwHighDateTime=(DWORD)(Corrected>>32); } SetWinFT(&ft); } else Reset(); #else struct tm t; t.tm_sec=lt->Second; t.tm_min=lt->Minute; t.tm_hour=lt->Hour; t.tm_mday=lt->Day; t.tm_mon=lt->Month-1; t.tm_year=lt->Year-1900; t.tm_isdst=-1; SetUnix(mktime(&t)); #endif itime+=lt->Reminder; } #ifdef _WIN_ALL void RarTime::GetWinFT(FILETIME *ft) { _ULARGE_INTEGER ul; ul.QuadPart=GetWin(); ft->dwLowDateTime=ul.LowPart; ft->dwHighDateTime=ul.HighPart; } void RarTime::SetWinFT(FILETIME *ft) { _ULARGE_INTEGER ul = {ft->dwLowDateTime, ft->dwHighDateTime}; SetWin(ul.QuadPart); } #endif // Get 64-bit representation of Windows FILETIME (100ns since 01.01.1601). uint64 RarTime::GetWin() { return itime/(TICKS_PER_SECOND/10000000); } // Set 64-bit representation of Windows FILETIME (100ns since 01.01.1601). void RarTime::SetWin(uint64 WinTime) { itime=WinTime*(TICKS_PER_SECOND/10000000); } time_t RarTime::GetUnix() { return time_t(GetUnixNS()/1000000000); } void RarTime::SetUnix(time_t ut) { if (sizeof(ut)>4) SetUnixNS(uint64(ut)*1000000000); else { // Convert 32-bit and possibly signed time_t to uint32 first, // uint64 cast is not enough. Otherwise sign can expand to 64 bits. SetUnixNS(uint64(uint32(ut))*1000000000); } } // Get the high precision Unix time in nanoseconds since 01-01-1970. uint64 RarTime::GetUnixNS() { // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); return itime*(1000000000/TICKS_PER_SECOND)-ushift; } // Set the high precision Unix time in nanoseconds since 01-01-1970. void RarTime::SetUnixNS(uint64 ns) { // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND); } uint RarTime::GetDos() { RarLocalTime lt; GetLocal(<); uint DosTime=(lt.Second/2)|(lt.Minute<<5)|(lt.Hour<<11)| (lt.Day<<16)|(lt.Month<<21)|((lt.Year-1980)<<25); return DosTime; } void RarTime::SetDos(uint DosTime) { RarLocalTime lt; lt.Second=(DosTime & 0x1f)*2; lt.Minute=(DosTime>>5) & 0x3f; lt.Hour=(DosTime>>11) & 0x1f; lt.Day=(DosTime>>16) & 0x1f; lt.Month=(DosTime>>21) & 0x0f; lt.Year=(DosTime>>25)+1980; lt.Reminder=0; SetLocal(<); } void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS) { if (IsSet()) { RarLocalTime lt; GetLocal(<); if (FullMS) swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u:%02u,%09u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute,lt.Second,lt.Reminder*(1000000000/TICKS_PER_SECOND)); else swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute); } else { // We use escape before '?' to avoid weird C trigraph characters. wcscpy(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?"); } } #ifndef SFX_MODULE void RarTime::SetIsoText(const wchar *TimeText) { int Field[6]; memset(Field,0,sizeof(Field)); for (uint DigitCount=0;*TimeText!=0;TimeText++) if (IsDigit(*TimeText)) { int FieldPos=DigitCount<4 ? 0:(DigitCount-4)/2+1; if (FieldPosOverwrite==OVERWRITE_NONE) return UIASKREP_R_SKIP; #if !defined(SFX_MODULE) && !defined(SILENT) // Must be before Cmd->AllYes check or -y switch would override -or. if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize)) return UIASKREP_R_REPLACE; #endif // This check must be after OVERWRITE_AUTORENAME processing or -y switch // would override -or. if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL) { PrepareToDelete(Name); return UIASKREP_R_REPLACE; } wchar NewName[NM]; wcsncpyz(NewName,Name,ASIZE(NewName)); UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags); if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) PrepareToDelete(Name); if (Choice==UIASKREP_R_REPLACEALL) { Cmd->Overwrite=OVERWRITE_ALL; return UIASKREP_R_REPLACE; } if (Choice==UIASKREP_R_SKIPALL) { Cmd->Overwrite=OVERWRITE_NONE; return UIASKREP_R_SKIP; } if (Choice==UIASKREP_R_RENAME) { if (PointToName(NewName)==NewName) SetName(Name,NewName,MaxNameSize); else wcsncpyz(Name,NewName,MaxNameSize); if (FileExist(Name)) return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags); return UIASKREP_R_REPLACE; } #if !defined(SFX_MODULE) && !defined(SILENT) if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize)) { Cmd->Overwrite=OVERWRITE_AUTORENAME; return UIASKREP_R_REPLACE; } #endif return Choice; } unrar/uiconsole.cpp000666 000000 000000 00000024302 13343205467 013073 0ustar00000000 000000 // Purely user interface function. Gets and returns user input. UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) { wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; FindData ExistingFD; memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails. FindFile::FastFind(Name,&ExistingFD); itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1)); ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false); if (FileSize==INT64NDF || FileTime==NULL) { eprintf(L"\n"); eprintf(St(MAskOverwrite),Name); } else { itoa(FileSize,SizeText2,ASIZE(SizeText2)); FileTime->GetText(DateStr2,ASIZE(DateStr2),false); eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2); } bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0; int Choice=0; do { Choice=Ask(St(AllowRename ? MYesNoAllRenQ : MYesNoAllQ)); } while (Choice==0); // 0 means invalid input. switch(Choice) { case 1: return UIASKREP_R_REPLACE; case 2: return UIASKREP_R_SKIP; case 3: return UIASKREP_R_REPLACEALL; case 4: return UIASKREP_R_SKIPALL; } if (AllowRename && Choice==5) { mprintf(St(MAskNewName)); if (getwstr(Name,MaxNameSize)) return UIASKREP_R_RENAME; else return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'. } return UIASKREP_R_CANCEL; } void uiStartArchiveExtract(bool Extract,const wchar *ArcName) { mprintf(St(Extract ? MExtracting : MExtrTest), ArcName); } bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) { return true; } void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) { int CurPercent=ToPercent(CurSize,TotalSize); mprintf(L"\b\b\b\b%3d%%",CurPercent); } void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) { int CurPercent=ToPercent(CurSize,TotalSize); mprintf(L"\b\b\b\b%3d%%",CurPercent); } void uiMsgStore::Msg() { switch(Code) { case UIERROR_SYSERRMSG: case UIERROR_GENERALERRMSG: Log(NULL,L"\n%ls",Str[0]); break; case UIERROR_CHECKSUM: Log(Str[0],St(MCRCFailed),Str[1]); break; case UIERROR_CHECKSUMENC: Log(Str[0],St(MEncrBadCRC),Str[1]); break; case UIERROR_CHECKSUMPACKED: Log(Str[0],St(MDataBadCRC),Str[1],Str[0]); break; case UIERROR_BADPSW: case UIWAIT_BADPSW: Log(Str[0],St(MWrongPassword)); break; case UIERROR_MEMORY: mprintf(L"\n"); Log(NULL,St(MErrOutMem)); break; case UIERROR_FILEOPEN: Log(Str[0],St(MCannotOpen),Str[1]); break; case UIERROR_FILECREATE: Log(Str[0],St(MCannotCreate),Str[1]); break; case UIERROR_FILECLOSE: Log(NULL,St(MErrFClose),Str[0]); break; case UIERROR_FILESEEK: Log(NULL,St(MErrSeek),Str[0]); break; case UIERROR_FILEREAD: Log(Str[0],St(MErrRead),Str[1]); break; case UIERROR_FILEWRITE: Log(Str[0],St(MErrWrite),Str[1]); break; #ifndef SFX_MODULE case UIERROR_FILEDELETE: Log(Str[0],St(MCannotDelete),Str[1]); break; case UIERROR_RECYCLEFAILED: Log(Str[0],St(MRecycleFailed)); break; case UIERROR_FILERENAME: Log(Str[0],St(MErrRename),Str[1],Str[2]); break; #endif case UIERROR_FILEATTR: Log(Str[0],St(MErrChangeAttr),Str[1]); break; case UIERROR_FILECOPY: Log(Str[0],St(MCopyError),Str[1],Str[2]); break; case UIERROR_FILECOPYHINT: Log(Str[0],St(MCopyErrorHint)); mprintf(L" "); // For progress percent. break; case UIERROR_DIRCREATE: Log(Str[0],St(MExtrErrMkDir),Str[1]); break; case UIERROR_SLINKCREATE: Log(Str[0],St(MErrCreateLnkS),Str[1]); break; case UIERROR_HLINKCREATE: Log(NULL,St(MErrCreateLnkH),Str[0]); break; case UIERROR_NOLINKTARGET: Log(NULL,St(MErrLnkTarget)); mprintf(L" "); // For progress percent. break; case UIERROR_NEEDADMIN: Log(NULL,St(MNeedAdmin)); break; case UIERROR_ARCBROKEN: Log(Str[0],St(MErrBrokenArc)); break; case UIERROR_HEADERBROKEN: Log(Str[0],St(MHeaderBroken)); break; case UIERROR_MHEADERBROKEN: Log(Str[0],St(MMainHeaderBroken)); break; case UIERROR_FHEADERBROKEN: Log(Str[0],St(MLogFileHead),Str[1]); break; case UIERROR_SUBHEADERBROKEN: Log(Str[0],St(MSubHeadCorrupt)); break; case UIERROR_SUBHEADERUNKNOWN: Log(Str[0],St(MSubHeadUnknown)); break; case UIERROR_SUBHEADERDATABROKEN: Log(Str[0],St(MSubHeadDataCRC),Str[1]); break; case UIERROR_RRDAMAGED: Log(Str[0],St(MRRDamaged)); break; case UIERROR_UNKNOWNMETHOD: Log(Str[0],St(MUnknownMeth),Str[1]); break; case UIERROR_UNKNOWNENCMETHOD: Log(Str[0],St(MUnkEncMethod),Str[1]); break; #ifndef SFX_MODULE case UIERROR_RENAMING: Log(Str[0],St(MRenaming),Str[1],Str[2]); break; case UIERROR_NEWERRAR: Log(Str[0],St(MNewerRAR)); break; #endif case UIERROR_RECVOLDIFFSETS: Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); break; case UIERROR_RECVOLALLEXIST: mprintf(St(MRecVolAllExist)); break; case UIERROR_RECONSTRUCTING: mprintf(St(MReconstructing)); break; case UIERROR_RECVOLCANNOTFIX: mprintf(St(MRecVolCannotFix)); break; case UIERROR_UNEXPEOF: Log(Str[0],St(MLogUnexpEOF)); break; case UIERROR_BADARCHIVE: Log(Str[0],St(MBadArc),Str[0]); break; case UIERROR_CMTBROKEN: Log(Str[0],St(MLogCommBrk)); break; case UIERROR_INVALIDNAME: Log(Str[0],St(MInvalidName),Str[1]); break; #ifndef SFX_MODULE case UIERROR_NEWRARFORMAT: Log(Str[0],St(MNewRarFormat)); break; #endif case UIERROR_NOFILESTOEXTRACT: mprintf(St(MExtrNoFiles)); break; case UIERROR_MISSINGVOL: Log(Str[0],St(MAbsNextVol),Str[0]); break; #ifndef SFX_MODULE case UIERROR_NEEDPREVVOL: Log(Str[0],St(MUnpCannotMerge),Str[1]); break; case UIERROR_UNKNOWNEXTRA: Log(Str[0],St(MUnknownExtra),Str[1]); break; case UIERROR_CORRUPTEXTRA: Log(Str[0],St(MCorruptExtra),Str[1],Str[2]); break; #endif #if !defined(SFX_MODULE) && defined(_WIN_ALL) case UIERROR_NTFSREQUIRED: Log(NULL,St(MNTFSRequired),Str[0]); break; #endif #if !defined(SFX_MODULE) && defined(_WIN_ALL) case UIERROR_ACLBROKEN: Log(Str[0],St(MACLBroken),Str[1]); break; case UIERROR_ACLUNKNOWN: Log(Str[0],St(MACLUnknown),Str[1]); break; case UIERROR_ACLSET: Log(Str[0],St(MACLSetError),Str[1]); break; case UIERROR_STREAMBROKEN: Log(Str[0],St(MStreamBroken),Str[1]); break; case UIERROR_STREAMUNKNOWN: Log(Str[0],St(MStreamUnknown),Str[1]); break; #endif case UIERROR_INCOMPATSWITCH: mprintf(St(MIncompatSwitch),Str[0],Num[0]); break; case UIERROR_PATHTOOLONG: Log(NULL,L"\n%ls%ls%ls",Str[0],Str[1],Str[2]); Log(NULL,St(MPathTooLong)); break; #ifndef SFX_MODULE case UIERROR_DIRSCAN: Log(NULL,St(MScanError),Str[0]); break; #endif case UIERROR_UOWNERBROKEN: Log(Str[0],St(MOwnersBroken),Str[1]); break; case UIERROR_UOWNERGETOWNERID: Log(Str[0],St(MErrGetOwnerID),Str[1]); break; case UIERROR_UOWNERGETGROUPID: Log(Str[0],St(MErrGetGroupID),Str[1]); break; case UIERROR_UOWNERSET: Log(Str[0],St(MSetOwnersError),Str[1]); break; case UIERROR_ULINKREAD: Log(NULL,St(MErrLnkRead),Str[0]); break; case UIERROR_ULINKEXIST: Log(NULL,St(MSymLinkExists),Str[0]); break; #ifndef SFX_MODULE case UIMSG_STRING: mprintf(L"\n%s",Str[0]); break; #endif case UIMSG_CORRECTINGNAME: Log(Str[0],St(MCorrectingName)); break; case UIMSG_BADARCHIVE: mprintf(St(MBadArc),Str[0]); break; case UIMSG_CREATING: mprintf(St(MCreating),Str[0]); break; case UIMSG_RENAMING: mprintf(St(MRenaming),Str[0],Str[1]); break; case UIMSG_RECVOLCALCCHECKSUM: mprintf(St(MCalcCRCAllVol)); break; case UIMSG_RECVOLFOUND: mprintf(St(MRecVolFound),Num[0]); break; case UIMSG_RECVOLMISSING: mprintf(St(MRecVolMissing),Num[0]); break; case UIMSG_MISSINGVOL: mprintf(St(MAbsNextVol),Str[0]); break; case UIMSG_RECONSTRUCTING: mprintf(St(MReconstructing)); break; case UIMSG_CHECKSUM: mprintf(St(MCRCFailed),Str[0]); break; case UIMSG_FAT32SIZE: mprintf(St(MFAT32Size)); mprintf(L" "); // For progress percent. break; case UIEVENT_RRTESTINGSTART: mprintf(L"%s ",St(MTestingRR)); break; } } bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) { // Unlike GUI we cannot provide Cancel button here, so we use the empty // password to abort. Otherwise user not knowing a password would need to // press Ctrl+C multiple times to quit from infinite password request loop. return GetConsolePassword(Type,FileName,Password) && Password->IsSet(); } void uiAlarm(UIALARM_TYPE Type) { if (uiSoundEnabled) { static clock_t LastTime=-10; // Negative to always beep first time. if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5) { #ifdef _WIN_ALL MessageBeep(-1); #else putwchar('\007'); #endif LastTime=MonoClock(); } } } bool uiAskNextVolume(wchar *VolName,size_t MaxSize) { eprintf(St(MAskNextVol),VolName); return Ask(St(MContinueQuit))!=2; } bool uiAskRepeatRead(const wchar *FileName) { mprintf(L"\n"); Log(NULL,St(MErrRead),FileName); return Ask(St(MRetryAbort))==1; } bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull) { mprintf(L"\n"); Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName); return Ask(St(MRetryAbort))==1; } #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month) { static MSGID MonthID[12]={ MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec }; return St(MonthID[Month]); } #endif unrar/uisilent.cpp000666 000000 000000 00000001553 13343205467 012732 0ustar00000000 000000 // Purely user interface function. Gets and returns user input. UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) { return UIASKREP_R_REPLACE; } void uiStartArchiveExtract(bool Extract,const wchar *ArcName) { } bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) { return true; } void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) { } void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) { } void uiMsgStore::Msg() { } bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) { return false; } void uiAlarm(UIALARM_TYPE Type) { } bool uiIsAborted() { return false; } void uiGiveTick() { } #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month) { return L""; } #endif unrar/ulinks.cpp000666 000000 000000 00000006755 13343205467 012414 0ustar00000000 000000 static bool UnixSymlink(const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { CreatePath(LinkName,true); DelFile(LinkName); char LinkNameA[NM]; WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); if (symlink(Target,LinkNameA)==-1) // Error. { if (errno==EEXIST) uiMsg(UIERROR_ULINKEXIST,LinkName); else { uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName); ErrHandler.SetErrorCode(RARX_WARNING); } return false; } #ifdef USE_LUTIMES #ifdef UNIX_TIME_NS timespec times[2]; times[0].tv_sec=fta->GetUnix(); times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; times[1].tv_sec=ftm->GetUnix(); times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW); #else struct timeval tv[2]; tv[0].tv_sec=fta->GetUnix(); tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000); tv[1].tv_sec=ftm->GetUnix(); tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000); lutimes(LinkNameA,tv); #endif #endif return true; } static bool IsFullPath(const char *PathA) // Unix ASCII version. { return *PathA==CPATHDIVIDER; } bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) { char Target[NM]; if (IsLink(Arc.FileHead.FileAttr)) { size_t DataSize=(size_t)Arc.FileHead.PackSize; if (DataSize>ASIZE(Target)-1) return false; if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize) return false; Target[DataSize]=0; DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); DataIO.UnpHash.Update(Target,strlen(Target)); DataIO.UnpHash.Result(&Arc.FileHead.FileHash); // Return true in case of bad checksum, so link will be processed further // and extraction routine will report the checksum error. if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL)) return true; wchar TargetW[NM]; CharToWide(Target,TargetW,ASIZE(TargetW)); // Check for *TargetW==0 to catch CharToWide failure. // Use Arc.FileHead.FileName instead of LinkName, since LinkName // can include the destination path as a prefix, which can // confuse IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (*TargetW==0 || IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) return false; return UnixSymlink(Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; } bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) { char Target[NM]; WideToChar(hd->RedirName,Target,ASIZE(Target)); if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) { // Cannot create Windows absolute path symlinks in Unix. Only relative path // Windows symlinks can be created here. RAR 5.0 used \??\ prefix // for Windows absolute symlinks, since RAR 5.1 /??/ is used. // We escape ? as \? to avoid "trigraph" warning if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0) return false; DosSlashToUnix(Target,Target,ASIZE(Target)); } // Use hd->FileName instead of LinkName, since LinkName can include // the destination path as a prefix, which can confuse // IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (IsFullPath(Target) || !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) return false; return UnixSymlink(Target,Name,&hd->mtime,&hd->atime); } unrar/unicode.cpp000666 000000 000000 00000036160 13343205467 012526 0ustar00000000 000000 #include "rar.hpp" #define MBFUNCTIONS #if defined(_UNIX) && defined(MBFUNCTIONS) static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success); static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success); // In Unix we map high ASCII characters which cannot be converted to Unicode // to 0xE000 - 0xE0FF private use Unicode area. static const uint MapAreaStart=0xE000; // Mapped string marker. Initially we used 0xFFFF for this purpose, // but it causes MSVC2008 swprintf to fail (it treats 0xFFFF as error marker). // While we could workaround it, it is safer to use another character. static const uint MappedStringMark=0xFFFE; #endif bool WideToChar(const wchar *Src,char *Dest,size_t DestSize) { bool RetCode=true; *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. #ifdef _WIN_ALL if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,(int)DestSize,NULL,NULL)==0) RetCode=false; // wcstombs is broken in Android NDK r9. #elif defined(_APPLE) WideToUtf(Src,Dest,DestSize); #elif defined(MBFUNCTIONS) if (!WideToCharMap(Src,Dest,DestSize,RetCode)) { mbstate_t ps; // Use thread safe external state based functions. memset (&ps, 0, sizeof(ps)); const wchar *SrcParam=Src; // wcsrtombs can change the pointer. // Some implementations of wcsrtombs can cause memory analyzing tools // like valgrind to report uninitialized data access. It happens because // internally these implementations call SSE4 based wcslen function, // which reads 16 bytes at once including those beyond of trailing 0. size_t ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); if (ResultingSize==(size_t)-1 && errno==EILSEQ) { // Aborted on inconvertible character not zero terminating the result. // EILSEQ helps to distinguish it from small output buffer abort. // We want to convert as much as we can, so we clean the output buffer // and repeat conversion. memset (&ps, 0, sizeof(ps)); SrcParam=Src; // wcsrtombs can change the pointer. memset(Dest,0,DestSize); ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); } if (ResultingSize==(size_t)-1) RetCode=false; if (ResultingSize==0 && *Src!=0) RetCode=false; } #else for (int I=0;I0) Dest[DestSize-1]=0; // We tried to return the empty string if conversion is failed, // but it does not work well. WideCharToMultiByte returns 'failed' code // and partially converted string even if we wanted to convert only a part // of string and passed DestSize smaller than required for fully converted // string. Such call is the valid behavior in RAR code and we do not expect // the empty string in this case. return RetCode; } bool CharToWide(const char *Src,wchar *Dest,size_t DestSize) { bool RetCode=true; *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. #ifdef _WIN_ALL if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,(int)DestSize)==0) RetCode=false; // mbstowcs is broken in Android NDK r9. #elif defined(_APPLE) UtfToWide(Src,Dest,DestSize); #elif defined(MBFUNCTIONS) mbstate_t ps; memset (&ps, 0, sizeof(ps)); const char *SrcParam=Src; // mbsrtowcs can change the pointer. size_t ResultingSize=mbsrtowcs(Dest,&SrcParam,DestSize,&ps); if (ResultingSize==(size_t)-1) RetCode=false; if (ResultingSize==0 && *Src!=0) RetCode=false; if (RetCode==false && DestSize>1) CharToWideMap(Src,Dest,DestSize,RetCode); #else for (int I=0;I0) Dest[DestSize-1]=0; // We tried to return the empty string if conversion is failed, // but it does not work well. MultiByteToWideChar returns 'failed' code // even if we wanted to convert only a part of string and passed DestSize // smaller than required for fully converted string. Such call is the valid // behavior in RAR code and we do not expect the empty string in this case. return RetCode; } #if defined(_UNIX) && defined(MBFUNCTIONS) // Convert and restore mapped inconvertible Unicode characters. // We use it for extended ASCII names in Unix. bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success) { // String with inconvertible characters mapped to private use Unicode area // must have the mark code somewhere. if (wcschr(Src,(wchar)MappedStringMark)==NULL) return false; // Seems to be that wcrtomb in some memory analyzing libraries // can produce uninitilized output while reporting success on garbage input. // So we clean the destination to calm analyzers. memset(Dest,0,DestSize); Success=true; uint SrcPos=0,DestPos=0; while (Src[SrcPos]!=0 && DestPos=MapAreaStart+0x80 && uint(Src[SrcPos])=0x80) { if (!MarkAdded) { Dest[DestPos++]=MappedStringMark; MarkAdded=true; if (DestPos>=DestSize) break; } Dest[DestPos++]=byte(Src[SrcPos++])+MapAreaStart; } else break; } else { memset(&ps,0,sizeof(ps)); int Length=mbrlen(Src+SrcPos,MB_CUR_MAX,&ps); SrcPos+=Max(Length,1); DestPos++; } } Dest[Min(DestPos,DestSize-1)]=0; } #endif // SrcSize is in wide characters, not in bytes. byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) { for (size_t I=0;I>8); if (*Src==0) break; } return Dest; } wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) { for (size_t I=0;I=0) { uint c=*(Src++); if (c<0x80) *(Dest++)=c; else if (c<0x800 && --dsize>=0) { *(Dest++)=(0xc0|(c>>6)); *(Dest++)=(0x80|(c&0x3f)); } else { if (c>=0xd800 && c<=0xdbff && *Src>=0xdc00 && *Src<=0xdfff) // Surrogate pair. { c=((c-0xd800)<<10)+(*Src-0xdc00)+0x10000; Src++; } if (c<0x10000 && (dsize-=2)>=0) { *(Dest++)=(0xe0|(c>>12)); *(Dest++)=(0x80|((c>>6)&0x3f)); *(Dest++)=(0x80|(c&0x3f)); } else if (c < 0x200000 && (dsize-=3)>=0) { *(Dest++)=(0xf0|(c>>18)); *(Dest++)=(0x80|((c>>12)&0x3f)); *(Dest++)=(0x80|((c>>6)&0x3f)); *(Dest++)=(0x80|(c&0x3f)); } } } *Dest=0; } size_t WideToUtfSize(const wchar *Src) { size_t Size=0; for (;*Src!=0;Src++) if (*Src<0x80) Size++; else if (*Src<0x800) Size+=2; else if ((uint)*Src<0x10000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. { if (Src[0]>=0xd800 && Src[0]<=0xdbff && Src[1]>=0xdc00 && Src[1]<=0xdfff) { Size+=4; // 4 output bytes for Unicode surrogate pair. Src++; } else Size+=3; } else if ((uint)*Src<0x200000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. Size+=4; return Size+1; // Include terminating zero. } bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize) { bool Success=true; long dsize=(long)DestSize; dsize--; while (*Src!=0) { uint c=byte(*(Src++)),d; if (c<0x80) d=c; else if ((c>>5)==6) { if ((*Src&0xc0)!=0x80) { Success=false; break; } d=((c&0x1f)<<6)|(*Src&0x3f); Src++; } else if ((c>>4)==14) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) { Success=false; break; } d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); Src+=2; } else if ((c>>3)==30) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) { Success=false; break; } d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); Src+=3; } else { Success=false; break; } if (--dsize<0) break; if (d>0xffff) { if (--dsize<0) break; if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. { Success=false; continue; } if (sizeof(*Dest)==2) // Use the surrogate pair. { *(Dest++)=((d-0x10000)>>10)+0xd800; *(Dest++)=(d&0x3ff)+0xdc00; } else *(Dest++)=d; } else *(Dest++)=d; } *Dest=0; return Success; } // For zero terminated strings. bool IsTextUtf8(const byte *Src) { return IsTextUtf8(Src,strlen((const char *)Src)); } // Source data can be both with and without UTF-8 BOM. bool IsTextUtf8(const byte *Src,size_t SrcSize) { while (SrcSize-- > 0) { byte C=*(Src++); int HighOne=0; // Number of leftmost '1' bits. for (byte Mask=0x80;Mask!=0 && (C & Mask)!=0;Mask>>=1) HighOne++; if (HighOne==1 || HighOne>6) return false; while (--HighOne > 0) if (SrcSize-- <= 0 || (*(Src++) & 0xc0)!=0x80) return false; } return true; } int wcsicomp(const wchar *s1,const wchar *s2) { #ifdef _WIN_ALL return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else while (true) { wchar u1 = towupper(*s1); wchar u2 = towupper(*s2); if (u1 != u2) return u1 < u2 ? -1 : 1; if (*s1==0) break; s1++; s2++; } return 0; #endif } int wcsnicomp(const wchar *s1,const wchar *s2,size_t n) { #ifdef _WIN_ALL // If we specify 'n' exceeding the actual string length, CompareString goes // beyond the trailing zero and compares garbage. So we need to limit 'n' // to real string length. size_t l1=Min(wcslen(s1)+1,n); size_t l2=Min(wcslen(s2)+1,n); return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; #else if (n==0) return 0; while (true) { wchar u1 = towupper(*s1); wchar u2 = towupper(*s2); if (u1 != u2) return u1 < u2 ? -1 : 1; if (*s1==0 || --n==0) break; s1++; s2++; } return 0; #endif } const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) { for (size_t i=0;str[i]!=0;i++) for (size_t j=0;;j++) { if (search[j]==0) return str+i; if (tolowerw(str[i+j])!=tolowerw(search[j])) break; } return NULL; } #ifndef SFX_MODULE wchar* wcslower(wchar *s) { #ifdef _WIN_ALL CharLower(s); #else for (wchar *c=s;*c!=0;c++) *c=towlower(*c); #endif return s; } #endif #ifndef SFX_MODULE wchar* wcsupper(wchar *s) { #ifdef _WIN_ALL CharUpper(s); #else for (wchar *c=s;*c!=0;c++) *c=towupper(*c); #endif return s; } #endif int toupperw(int ch) { #if defined(_WIN_ALL) // CharUpper is more reliable than towupper in Windows, which seems to be // C locale dependent even in Unicode version. For example, towupper failed // to convert lowercase Russian characters. return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)ch); #else return towupper(ch); #endif } int tolowerw(int ch) { #if defined(_WIN_ALL) // CharLower is more reliable than towlower in Windows. // See comment for towupper above. return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)ch); #else return towlower(ch); #endif } int atoiw(const wchar *s) { return (int)atoilw(s); } int64 atoilw(const wchar *s) { bool sign=false; if (*s=='-') // We do use signed integers here, for example, in GUI SFX. { s++; sign=true; } // Use unsigned type here, since long string can overflow the variable // and signed integer overflow is undefined behavior in C++. uint64 n=0; while (*s>='0' && *s<='9') { n=n*10+(*s-'0'); s++; } // Check int64(n)>=0 to avoid the signed overflow with undefined behavior // when negating 0x8000000000000000. return sign && int64(n)>=0 ? -int64(n) : int64(n); } #ifdef DBCS_SUPPORTED SupportDBCS gdbcs; SupportDBCS::SupportDBCS() { Init(); } void SupportDBCS::Init() { CPINFO CPInfo; GetCPInfo(CP_ACP,&CPInfo); DBCSMode=CPInfo.MaxCharSize > 1; for (uint I=0;INextWindow flag // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's // use 0x40000 for extra safety and possible filter area size expansion. const size_t MinAllocSize=0x40000; if (WinSize>16)>0x10000) // Window size must not exceed 4 GB. return; // Archiving code guarantees that window size does not grow in the same // solid stream. So if we are here, we are either creating a new window // or increasing the size of non-solid window. So we could safely reject // current window data without copying them to a new window, though being // extra cautious, we still handle the solid window grow case below. bool Grow=Solid && (Window!=NULL || Fragmented); // We do not handle growth for existing fragmented window. if (Grow && Fragmented) throw std::bad_alloc(); byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize); if (NewWindow==NULL) if (Grow || WinSize<0x1000000) { // We do not support growth for new fragmented window. // Also exclude RAR4 and small dictionaries. throw std::bad_alloc(); } else { if (Window!=NULL) // If allocated by preceding files. { free(Window); Window=NULL; } FragWindow.Init(WinSize); Fragmented=true; } if (!Fragmented) { // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access unused areas of sliding dictionary. memset(NewWindow,0,WinSize); // If Window is not NULL, it means that window size has grown. // In solid streams we need to copy data to a new window in such case. // RAR archiving code does not allow it in solid streams now, // but let's implement it anyway just in case we'll change it sometimes. if (Grow) for (size_t I=1;I<=MaxWinSize;I++) NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; if (Window!=NULL) free(Window); Window=NewWindow; } MaxWinSize=WinSize; MaxWinMask=MaxWinSize-1; } void Unpack::DoUnpack(uint Method,bool Solid) { // Methods <50 will crash in Fragmented mode when accessing NULL Window. // They cannot be called in such mode now, but we check it below anyway // just for extra safety. switch(Method) { #ifndef SFX_MODULE case 15: // rar 1.5 compression if (!Fragmented) Unpack15(Solid); break; case 20: // rar 2.x compression case 26: // files larger than 2GB if (!Fragmented) Unpack20(Solid); break; #endif case 29: // rar 3.x compression if (!Fragmented) Unpack29(Solid); break; case 50: // RAR 5.0 compression algorithm. #ifdef RAR_SMP if (MaxUserThreads>1) { // We do not use the multithreaded unpack routine to repack RAR archives // in 'suspended' mode, because unlike the single threaded code it can // write more than one dictionary for same loop pass. So we would need // larger buffers of unknown size. Also we do not support multithreading // in fragmented window mode. if (!Fragmented) { Unpack5MT(Solid); break; } } #endif Unpack5(Solid); break; } } void Unpack::UnpInitData(bool Solid) { if (!Solid) { memset(OldDist,0,sizeof(OldDist)); OldDistPtr=0; LastDist=LastLength=0; // memset(Window,0,MaxWinSize); memset(&BlockTables,0,sizeof(BlockTables)); UnpPtr=WrPtr=0; WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } // Filters never share several solid files, so we can safely reset them // even in solid archive. InitFilters(); Inp.InitBitInput(); WrittenFileSize=0; ReadTop=0; ReadBorder=0; memset(&BlockHeader,0,sizeof(BlockHeader)); BlockHeader.BlockSize=-1; // '-1' means not defined yet. #ifndef SFX_MODULE UnpInitData20(Solid); #endif UnpInitData30(Solid); UnpInitData50(Solid); } // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) { // Size of alphabet and DecodePos array. Dec->MaxNum=Size; // Calculate how many entries for every bit length in LengthTable we have. uint LengthCount[16]; memset(LengthCount,0,sizeof(LengthCount)); for (size_t I=0;IDecodeNum,0,Size*sizeof(*Dec->DecodeNum)); // Initialize not really used entry for zero length code. Dec->DecodePos[0]=0; // Start code for bit length 1 is 0. Dec->DecodeLen[0]=0; // Right aligned upper limit code for current bit length. uint UpperLimit=0; for (size_t I=1;I<16;I++) { // Adjust the upper limit code. UpperLimit+=LengthCount[I]; // Left aligned upper limit code. uint LeftAligned=UpperLimit<<(16-I); // Prepare the upper limit code for next bit length. UpperLimit*=2; // Store the left aligned upper limit code. Dec->DecodeLen[I]=(uint)LeftAligned; // Every item of this array contains the sum of all preceding items. // So it contains the start position in code list for every bit length. Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1]; } // Prepare the copy of DecodePos. We'll modify this copy below, // so we cannot use the original DecodePos. uint CopyDecodePos[ASIZE(Dec->DecodePos)]; memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); // For every bit length in the bit length table and so for every item // of alphabet. for (uint I=0;IDecodeNum[LastPos]=(ushort)I; // We'll use next position number for this bit length next time. // So we pass through the entire range of positions available // for every bit length. CopyDecodePos[CurBitLength]++; } } // Define the number of bits to process in quick mode. We use more bits // for larger alphabets. More bits means that more codes will be processed // in quick mode, but also that more time will be spent to preparation // of tables for quick decode. switch (Size) { case NC: case NC20: case NC30: Dec->QuickBits=MAX_QUICK_DECODE_BITS; break; default: Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; break; } // Size of tables for quick mode. uint QuickDataSize=1<QuickBits; // Bit length for current code, start from 1 bit codes. It is important // to use 1 bit instead of 0 for minimum code length, so we are moving // forward even when processing a corrupt archive. uint CurBitLength=1; // For every right aligned bit string which supports the quick decoding. for (uint Code=0;CodeQuickBits); // Prepare the table for quick decoding of bit lengths. // Find the upper limit for current bit field and adjust the bit length // accordingly if necessary. while (CurBitLengthDecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) CurBitLength++; // Translation of right aligned bit string to bit length. Dec->QuickLen[Code]=CurBitLength; // Prepare the table for quick translation of position in code list // to position in alphabet. // Calculate the distance from the start code for current bit length. uint Dist=BitField-Dec->DecodeLen[CurBitLength-1]; // Right align the distance. Dist>>=(16-CurBitLength); // Now we can calculate the position in the code list. It is the sum // of first position for current bit length and right aligned distance // between our bit field and start code for current bit length. uint Pos; if (CurBitLengthDecodePos) && (Pos=Dec->DecodePos[CurBitLength]+Dist)QuickNum[Code]=Dec->DecodeNum[Pos]; } else { // Can be here for length table filled with zeroes only (empty). Dec->QuickNum[Code]=0; } } } unrar/unpack15.cpp000666 000000 000000 00000025435 13343205467 012532 0ustar00000000 000000 #define STARTL1 2 static unsigned int DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, 0xee00,0xf000,0xf200,0xf200,0xffff}; static unsigned int PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; #define STARTL2 3 static unsigned int DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, 0xf000,0xf200,0xf240,0xffff}; static unsigned int PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; #define STARTHF0 4 static unsigned int DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, 0xf200,0xf200,0xffff}; static unsigned int PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; #define STARTHF1 5 static unsigned int DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, 0xf7e0,0xffff}; static unsigned int PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; #define STARTHF2 5 static unsigned int DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, 0xffff,0xffff}; static unsigned int PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; #define STARTHF3 6 static unsigned int DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, 0xffff}; static unsigned int PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; #define STARTHF4 8 static unsigned int DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; void Unpack::Unpack15(bool Solid) { UnpInitData(Solid); UnpInitData15(Solid); UnpReadBuf(); if (!Solid) { InitHuff(); UnpPtr=0; } else UnpPtr=WrPtr; --DestUnpSize; if (DestUnpSize>=0) { GetFlagsBuf(); FlagsCnt=8; } while (DestUnpSize>=0) { UnpPtr&=MaxWinMask; if (Inp.InAddr>ReadTop-30 && !UnpReadBuf()) break; if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) UnpWriteBuf20(); if (StMode) { HuffDecode(); continue; } if (--FlagsCnt < 0) { GetFlagsBuf(); FlagsCnt=7; } if (FlagBuf & 0x80) { FlagBuf<<=1; if (Nlzb > Nhfb) LongLZ(); else HuffDecode(); } else { FlagBuf<<=1; if (--FlagsCnt < 0) { GetFlagsBuf(); FlagsCnt=7; } if (FlagBuf & 0x80) { FlagBuf<<=1; if (Nlzb > Nhfb) HuffDecode(); else LongLZ(); } else { FlagBuf<<=1; ShortLZ(); } } } UnpWriteBuf20(); } #define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) #define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) void Unpack::ShortLZ() { static unsigned int ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; static unsigned int ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; static unsigned int ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; static unsigned int ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; unsigned int Length,SaveLength; unsigned int LastDistance; unsigned int Distance; int DistancePlace; NumHuf=0; unsigned int BitField=Inp.fgetbits(); if (LCount==2) { Inp.faddbits(1); if (BitField >= 0x8000) { CopyString15((unsigned int)LastDist,LastLength); return; } BitField <<= 1; LCount=0; } BitField>>=8; // not thread safe, replaced by GetShortLen1 and GetShortLen2 macro // ShortLen1[1]=ShortLen2[3]=Buf60+3; if (AvrLn1<37) { for (Length=0;;Length++) if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) break; Inp.faddbits(GetShortLen1(Length)); } else { for (Length=0;;Length++) if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) break; Inp.faddbits(GetShortLen2(Length)); } if (Length >= 9) { if (Length == 9) { LCount++; CopyString15((unsigned int)LastDist,LastLength); return; } if (Length == 14) { LCount=0; Length=DecodeNum(Inp.fgetbits(),STARTL2,DecL2,PosL2)+5; Distance=(Inp.fgetbits()>>1) | 0x8000; Inp.faddbits(15); LastLength=Length; LastDist=Distance; CopyString15(Distance,Length); return; } LCount=0; SaveLength=Length; Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2; if (Length==0x101 && SaveLength==10) { Buf60 ^= 1; return; } if (Distance > 256) Length++; if (Distance >= MaxDist3) Length++; OldDist[OldDistPtr++]=Distance; OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; CopyString15(Distance,Length); return; } LCount=0; AvrLn1 += Length; AvrLn1 -= AvrLn1 >> 4; DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; Distance=ChSetA[DistancePlace]; if (--DistancePlace != -1) { LastDistance=ChSetA[DistancePlace]; ChSetA[DistancePlace+1]=LastDistance; ChSetA[DistancePlace]=Distance; } Length+=2; OldDist[OldDistPtr++] = ++Distance; OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; CopyString15(Distance,Length); } void Unpack::LongLZ() { unsigned int Length; unsigned int Distance; unsigned int DistancePlace,NewDistancePlace; unsigned int OldAvr2,OldAvr3; NumHuf=0; Nlzb+=16; if (Nlzb > 0xff) { Nlzb=0x90; Nhfb >>= 1; } OldAvr2=AvrLn2; unsigned int BitField=Inp.fgetbits(); if (AvrLn2 >= 122) Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); else if (AvrLn2 >= 64) Length=DecodeNum(BitField,STARTL1,DecL1,PosL1); else if (BitField < 0x100) { Length=BitField; Inp.faddbits(16); } else { for (Length=0;((BitField<> 5; BitField=Inp.fgetbits(); if (AvrPlcB > 0x28ff) DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); else if (AvrPlcB > 0x6ff) DistancePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); else DistancePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); AvrPlcB += DistancePlace; AvrPlcB -= AvrPlcB >> 8; while (1) { Distance = ChSetB[DistancePlace & 0xff]; NewDistancePlace = NToPlB[Distance++ & 0xff]++; if (!(Distance & 0xff)) CorrHuff(ChSetB,NToPlB); else break; } ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; ChSetB[NewDistancePlace]=Distance; Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; Inp.faddbits(7); OldAvr3=AvrLn3; if (Length!=1 && Length!=4) if (Length==0 && Distance <= MaxDist3) { AvrLn3++; AvrLn3 -= AvrLn3 >> 8; } else if (AvrLn3 > 0) AvrLn3--; Length+=3; if (Distance >= MaxDist3) Length++; if (Distance <= 256) Length+=8; if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) MaxDist3=0x7f00; else MaxDist3=0x2001; OldDist[OldDistPtr++]=Distance; OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; CopyString15(Distance,Length); } void Unpack::HuffDecode() { unsigned int CurByte,NewBytePlace; unsigned int Length; unsigned int Distance; int BytePlace; unsigned int BitField=Inp.fgetbits(); if (AvrPlc > 0x75ff) BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); else if (AvrPlc > 0x5dff) BytePlace=DecodeNum(BitField,STARTHF3,DecHf3,PosHf3); else if (AvrPlc > 0x35ff) BytePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); else if (AvrPlc > 0x0dff) BytePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); else BytePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); BytePlace&=0xff; if (StMode) { if (BytePlace==0 && BitField > 0xfff) BytePlace=0x100; if (--BytePlace==-1) { BitField=Inp.fgetbits(); Inp.faddbits(1); if (BitField & 0x8000) { NumHuf=StMode=0; return; } else { Length = (BitField & 0x4000) ? 4 : 3; Inp.faddbits(1); Distance=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); Distance = (Distance << 5) | (Inp.fgetbits() >> 11); Inp.faddbits(5); CopyString15(Distance,Length); return; } } } else if (NumHuf++ >= 16 && FlagsCnt==0) StMode=1; AvrPlc += BytePlace; AvrPlc -= AvrPlc >> 8; Nhfb+=16; if (Nhfb > 0xff) { Nhfb=0x90; Nlzb >>= 1; } Window[UnpPtr++]=(byte)(ChSet[BytePlace]>>8); --DestUnpSize; while (1) { CurByte=ChSet[BytePlace]; NewBytePlace=NToPl[CurByte++ & 0xff]++; if ((CurByte & 0xff) > 0xa1) CorrHuff(ChSet,NToPl); else break; } ChSet[BytePlace]=ChSet[NewBytePlace]; ChSet[NewBytePlace]=CurByte; } void Unpack::GetFlagsBuf() { unsigned int Flags,NewFlagsPlace; unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); // Our Huffman table stores 257 items and needs all them in other parts // of code such as when StMode is on, so the first item is control item. // While normally we do not use the last item to code the flags byte here, // we need to check for value 256 when unpacking in case we unpack // a corrupt archive. if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0])) return; while (1) { Flags=ChSetC[FlagsPlace]; FlagBuf=Flags>>8; NewFlagsPlace=NToPlC[Flags++ & 0xff]++; if ((Flags & 0xff) != 0) break; CorrHuff(ChSetC,NToPlC); } ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace]; ChSetC[NewFlagsPlace]=Flags; } void Unpack::UnpInitData15(int Solid) { if (!Solid) { AvrPlcB=AvrLn1=AvrLn2=AvrLn3=NumHuf=Buf60=0; AvrPlc=0x3500; MaxDist3=0x2001; Nhfb=Nlzb=0x80; } FlagsCnt=0; FlagBuf=0; StMode=0; LCount=0; ReadTop=0; } void Unpack::InitHuff() { for (unsigned int I=0;I<256;I++) { ChSet[I]=ChSetB[I]=I<<8; ChSetA[I]=I; ChSetC[I]=((~I+1) & 0xff)<<8; } memset(NToPl,0,sizeof(NToPl)); memset(NToPlB,0,sizeof(NToPlB)); memset(NToPlC,0,sizeof(NToPlC)); CorrHuff(ChSetB,NToPlB); } void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) { int I,J; for (I=7;I>=0;I--) for (J=0;J<32;J++,CharSet++) *CharSet=(*CharSet & ~0xff) | I; memset(NumToPlace,0,sizeof(NToPl)); for (I=6;I>=0;I--) NumToPlace[I]=(7-I)*32; } void Unpack::CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; while (Length--) { Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; UnpPtr=(UnpPtr+1) & MaxWinMask; } } uint Unpack::DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) { int I; for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) StartPos++; Inp.faddbits(StartPos); return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); } unrar/unpack20.cpp000666 000000 000000 00000020634 13343205467 012522 0ustar00000000 000000 #include "rar.hpp" void Unpack::CopyString20(uint Length,uint Distance) { LastDist=OldDist[OldDistPtr++]=Distance; OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0. LastLength=Length; DestUnpSize-=Length; CopyString(Length,Distance); } void Unpack::Unpack20(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; static unsigned char DBits[]= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; uint Bits; if (Suspended) UnpPtr=WrPtr; else { UnpInitData(Solid); if (!UnpReadBuf()) return; if ((!Solid || !TablesRead2) && !ReadTables20()) return; --DestUnpSize; } while (DestUnpSize>=0) { UnpPtr&=MaxWinMask; if (Inp.InAddr>ReadTop-30) if (!UnpReadBuf()) break; if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) { UnpWriteBuf20(); if (Suspended) return; } if (UnpAudioBlock) { uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]); if (AudioNumber==256) { if (!ReadTables20()) break; continue; } Window[UnpPtr++]=DecodeAudio((int)AudioNumber); if (++UnpCurChannel==UnpChannels) UnpCurChannel=0; --DestUnpSize; continue; } uint Number=DecodeNumber(Inp,&BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; --DestUnpSize; continue; } if (Number>269) { uint Length=LDecode[Number-=270]+3; if ((Bits=LBits[Number])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } if (Distance>=0x2000) { Length++; if (Distance>=0x40000L) Length++; } CopyString20(Length,Distance); continue; } if (Number==269) { if (!ReadTables20()) break; continue; } if (Number==256) { CopyString20(LastLength,LastDist); continue; } if (Number<261) { uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); uint Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } if (Distance>=0x101) { Length++; if (Distance>=0x2000) { Length++; if (Distance>=0x40000) Length++; } } CopyString20(Length,Distance); continue; } if (Number<270) { uint Distance=SDDecode[Number-=261]+1; if ((Bits=SDBits[Number])>0) { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } CopyString20(2,Distance); continue; } } ReadLastTables(); UnpWriteBuf20(); } void Unpack::UnpWriteBuf20() { if (UnpPtr!=WrPtr) UnpSomeRead=true; if (UnpPtrUnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); UnpIO->UnpWrite(Window,UnpPtr); UnpAllBuf=true; } else UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); WrPtr=UnpPtr; } bool Unpack::ReadTables20() { byte BitLength[BC20]; byte Table[MC20*4]; if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) return false; uint BitField=Inp.getbits(); UnpAudioBlock=(BitField & 0x8000)!=0; if (!(BitField & 0x4000)) memset(UnpOldTable20,0,sizeof(UnpOldTable20)); Inp.addbits(2); uint TableSize; if (UnpAudioBlock) { UnpChannels=((BitField>>12) & 3)+1; if (UnpCurChannel>=UnpChannels) UnpCurChannel=0; Inp.addbits(2); TableSize=MC20*UnpChannels; } else TableSize=NC20+DC20+RC20; for (uint I=0;I> 12); Inp.addbits(4); } MakeDecodeTables(BitLength,&BlockTables.BD,BC20); for (uint I=0;IReadTop-5) if (!UnpReadBuf()) return false; uint Number=DecodeNumber(Inp,&BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable20[I]) & 0xf; I++; } else if (Number==16) { uint N=(Inp.getbits() >> 14)+3; Inp.addbits(2); if (I==0) return false; // We cannot have "repeat previous" code at the first position. else while (N-- > 0 && I> 13)+3; Inp.addbits(3); } else { N=(Inp.getbits() >> 9)+11; Inp.addbits(7); } while (N-- > 0 && IReadTop) return true; if (UnpAudioBlock) for (uint I=0;I=Inp.InAddr+5) if (UnpAudioBlock) { if (DecodeNumber(Inp,&MD[UnpCurChannel])==256) ReadTables20(); } else if (DecodeNumber(Inp,&BlockTables.LD)==269) ReadTables20(); } void Unpack::UnpInitData20(int Solid) { if (!Solid) { TablesRead2=false; UnpAudioBlock=false; UnpChannelDelta=0; UnpCurChannel=0; UnpChannels=1; memset(AudV,0,sizeof(AudV)); memset(UnpOldTable20,0,sizeof(UnpOldTable20)); memset(MD,0,sizeof(MD)); } } byte Unpack::DecodeAudio(int Delta) { struct AudioVariables *V=&AudV[UnpCurChannel]; V->ByteCount++; V->D4=V->D3; V->D3=V->D2; V->D2=V->LastDelta-V->D1; V->D1=V->LastDelta; int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; PCh=(PCh>>3) & 0xFF; uint Ch=PCh-Delta; int D=(signed char)Delta; // Left shift of negative value is undefined behavior in C++, // so we cast it to unsigned to follow the standard. D=(uint)D<<3; V->Dif[0]+=abs(D); V->Dif[1]+=abs(D-V->D1); V->Dif[2]+=abs(D+V->D1); V->Dif[3]+=abs(D-V->D2); V->Dif[4]+=abs(D+V->D2); V->Dif[5]+=abs(D-V->D3); V->Dif[6]+=abs(D+V->D3); V->Dif[7]+=abs(D-V->D4); V->Dif[8]+=abs(D+V->D4); V->Dif[9]+=abs(D-UnpChannelDelta); V->Dif[10]+=abs(D+UnpChannelDelta); UnpChannelDelta=V->LastDelta=(signed char)(Ch-V->LastChar); V->LastChar=Ch; if ((V->ByteCount & 0x1F)==0) { uint MinDif=V->Dif[0],NumMinDif=0; V->Dif[0]=0; for (uint I=1;IDif);I++) { if (V->Dif[I]Dif[I]; NumMinDif=I; } V->Dif[I]=0; } switch(NumMinDif) { case 1: if (V->K1>=-16) V->K1--; break; case 2: if (V->K1<16) V->K1++; break; case 3: if (V->K2>=-16) V->K2--; break; case 4: if (V->K2<16) V->K2++; break; case 5: if (V->K3>=-16) V->K3--; break; case 6: if (V->K3<16) V->K3++; break; case 7: if (V->K4>=-16) V->K4--; break; case 8: if (V->K4<16) V->K4++; break; case 9: if (V->K5>=-16) V->K5--; break; case 10: if (V->K5<16) V->K5++; break; } } return (byte)Ch; } unrar/unpack30.cpp000666 000000 000000 00000046421 13343205467 012525 0ustar00000000 000000 // We use it instead of direct PPM.DecodeChar call to be sure that // we reset PPM structures in case of corrupt data. It is important, // because these structures can be invalid after PPM.DecodeChar returned -1. inline int Unpack::SafePPMDecodeChar() { int Ch=PPM.DecodeChar(); if (Ch==-1) // Corrupt PPM data found. { PPM.CleanUp(); // Reset possibly corrupt PPM data structures. UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. } return(Ch); } void Unpack::Unpack29(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; static int DDecode[DC]; static byte DBits[DC]; static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; unsigned int Bits; if (DDecode[1]==0) { int Dist=0,BitLength=0,Slot=0; for (int I=0;IReadBorder) { if (!UnpReadBuf30()) break; } if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) { UnpWriteBuf30(); if (WrittenFileSize>DestUnpSize) return; if (Suspended) { FileExtracted=false; return; } } if (UnpBlockType==BLOCK_PPM) { // Here speed is critical, so we do not use SafePPMDecodeChar, // because sometimes even the inline function can introduce // some additional penalty. int Ch=PPM.DecodeChar(); if (Ch==-1) // Corrupt PPM data found. { PPM.CleanUp(); // Reset possibly corrupt PPM data structures. UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. break; } if (Ch==PPMEscChar) { int NextCh=SafePPMDecodeChar(); if (NextCh==0) // End of PPM encoding. { if (!ReadTables30()) break; continue; } if (NextCh==-1) // Corrupt PPM data found. break; if (NextCh==2) // End of file in PPM mode. break; if (NextCh==3) // Read VM code. { if (!ReadVMCodePPM()) break; continue; } if (NextCh==4) // LZ inside of PPM. { unsigned int Distance=0,Length; bool Failed=false; for (int I=0;I<4 && !Failed;I++) { int Ch=SafePPMDecodeChar(); if (Ch==-1) Failed=true; else if (I==3) Length=(byte)Ch; else Distance=(Distance<<8)+(byte)Ch; } if (Failed) break; CopyString(Length+32,Distance+2); continue; } if (NextCh==5) // One byte distance match (RLE) inside of PPM. { int Length=SafePPMDecodeChar(); if (Length==-1) break; CopyString(Length+4,1); continue; } // If we are here, NextCh must be 1, what means that current byte // is equal to our 'escape' byte, so we just store it to Window. } Window[UnpPtr++]=Ch; continue; } uint Number=DecodeNumber(Inp,&BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; continue; } if (Number>=271) { uint Length=LDecode[Number-=271]+3; if ((Bits=LBits[Number])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { if (DistNumber>9) { if (Bits>4) { Distance+=((Inp.getbits()>>(20-Bits))<<4); Inp.addbits(Bits-4); } if (LowDistRepCount>0) { LowDistRepCount--; Distance+=PrevLowDist; } else { uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); if (LowDist==16) { LowDistRepCount=LOW_DIST_REP_COUNT-1; Distance+=PrevLowDist; } else { Distance+=LowDist; PrevLowDist=LowDist; } } } else { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } } if (Distance>=0x2000) { Length++; if (Distance>=0x40000) Length++; } InsertOldDist(Distance); LastLength=Length; CopyString(Length,Distance); continue; } if (Number==256) { if (!ReadEndOfBlock()) break; continue; } if (Number==257) { if (!ReadVMCode()) break; continue; } if (Number==258) { if (LastLength!=0) CopyString(LastLength,OldDist[0]); continue; } if (Number<263) { uint DistNum=Number-259; uint Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); int Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } LastLength=Length; CopyString(Length,Distance); continue; } if (Number<272) { uint Distance=SDDecode[Number-=263]+1; if ((Bits=SDBits[Number])>0) { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } InsertOldDist(Distance); LastLength=2; CopyString(2,Distance); continue; } } UnpWriteBuf30(); } // Return 'false' to quit unpacking the current file or 'true' to continue. bool Unpack::ReadEndOfBlock() { uint BitField=Inp.getbits(); bool NewTable,NewFile=false; // "1" - no new file, new table just here. // "00" - new file, no new table. // "01" - new file, new table (in beginning of next file). if ((BitField & 0x8000)!=0) { NewTable=true; Inp.addbits(1); } else { NewFile=true; NewTable=(BitField & 0x4000)!=0; Inp.addbits(2); } TablesRead3=!NewTable; // Quit immediately if "new file" flag is set. If "new table" flag // is present, we'll read the table in beginning of next file // based on 'TablesRead3' 'false' value. if (NewFile) return false; return ReadTables30(); // Quit only if we failed to read tables. } bool Unpack::ReadVMCode() { // Entire VM code is guaranteed to fully present in block defined // by current Huffman table. Compressor checks that VM code does not cross // Huffman block boundaries. uint FirstByte=Inp.getbits()>>8; Inp.addbits(8); uint Length=(FirstByte & 7)+1; if (Length==7) { Length=(Inp.getbits()>>8)+7; Inp.addbits(8); } else if (Length==8) { Length=Inp.getbits(); Inp.addbits(16); } if (Length==0) return false; Array VMCode(Length); for (uint I=0;I=ReadTop-1 && !UnpReadBuf30() && I>8; Inp.addbits(8); } return AddVMCode(FirstByte,&VMCode[0],Length); } bool Unpack::ReadVMCodePPM() { uint FirstByte=SafePPMDecodeChar(); if ((int)FirstByte==-1) return false; uint Length=(FirstByte & 7)+1; if (Length==7) { int B1=SafePPMDecodeChar(); if (B1==-1) return false; Length=B1+7; } else if (Length==8) { int B1=SafePPMDecodeChar(); if (B1==-1) return false; int B2=SafePPMDecodeChar(); if (B2==-1) return false; Length=B1*256+B2; } if (Length==0) return false; Array VMCode(Length); for (uint I=0;IFilters30.Size() || FiltPos>OldFilterLengths.Size()) return false; LastFilter=FiltPos; bool NewFilter=(FiltPos==Filters30.Size()); UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. UnpackFilter30 *Filter; if (NewFilter) // New filter code, never used before since VM reset. { if (FiltPos>MAX3_UNPACK_FILTERS) { // Too many different filters, corrupt archive. delete StackFilter; return false; } Filters30.Add(1); Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; StackFilter->ParentFilter=(uint)(Filters30.Size()-1); // Reserve one item to store the data block length of our new filter // entry. We'll set it to real block length below, after reading it. // But we need to initialize it now, because when processing corrupt // data, we can access this item even before we set it to real value. OldFilterLengths.Push(0); } else // Filter was used in the past. { Filter=Filters30[FiltPos]; StackFilter->ParentFilter=FiltPos; } uint EmptyCount=0; for (uint I=0;I0) PrgStack[I]=NULL; } if (EmptyCount==0) { if (PrgStack.Size()>MAX3_UNPACK_FILTERS) { delete StackFilter; return false; } PrgStack.Add(1); EmptyCount=1; } size_t StackPos=PrgStack.Size()-EmptyCount; PrgStack[StackPos]=StackFilter; uint BlockStart=RarVM::ReadData(VMCodeInp); if ((FirstByte & 0x40)!=0) BlockStart+=258; StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); if ((FirstByte & 0x20)!=0) { StackFilter->BlockLength=RarVM::ReadData(VMCodeInp); // Store the last data block length for current filter. OldFilterLengths[FiltPos]=StackFilter->BlockLength; } else { // Set the data block size to same value as the previous block size // for same filter. It is possible for corrupt data to access a new // and not filled yet item of OldFilterLengths array here. This is why // we set new OldFilterLengths items to zero above. StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); StackFilter->Prg.InitR[4]=StackFilter->BlockLength; if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. { uint InitMask=VMCodeInp.fgetbits()>>9; VMCodeInp.faddbits(7); for (uint I=0;I<7;I++) if (InitMask & (1<Prg.InitR[I]=RarVM::ReadData(VMCodeInp); } if (NewFilter) { uint VMCodeSize=RarVM::ReadData(VMCodeInp); if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) return false; Array VMCode(VMCodeSize); for (uint I=0;I>8; VMCodeInp.faddbits(8); } VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); } StackFilter->Prg.Type=Filter->Prg.Type; return true; } bool Unpack::UnpReadBuf30() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) return false; if (Inp.InAddr>BitInput::MAX_SIZE/2) { // If we already processed more than half of buffer, let's move // remaining data into beginning to free more space for new data // and ensure that calling function does not cross the buffer border // even if we did not read anything here. Also it ensures that read size // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk // to make it zero. if (DataSize>0) memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); Inp.InAddr=0; ReadTop=DataSize; } else DataSize=ReadTop; int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); if (ReadCode>0) ReadTop+=ReadCode; ReadBorder=ReadTop-30; return ReadCode!=-1; } void Unpack::UnpWriteBuf30() { uint WrittenBorder=(uint)WrPtr; uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); for (size_t I=0;INextWindow) { flt->NextWindow=false; continue; } unsigned int BlockStart=flt->BlockStart; unsigned int BlockLength=flt->BlockLength; if (((BlockStart-WrittenBorder)&MaxWinMask)ParentFilter]->Prg; VM_PreparedProgram *Prg=&flt->Prg; ExecuteCode(Prg); byte *FilteredData=Prg->FilteredData; unsigned int FilteredDataSize=Prg->FilteredDataSize; delete PrgStack[I]; PrgStack[I]=NULL; while (I+1BlockStart!=BlockStart || NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) break; // Apply several filters to same data block. VM.SetMemory(0,FilteredData,FilteredDataSize); VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; VM_PreparedProgram *NextPrg=&NextFilter->Prg; ExecuteCode(NextPrg); FilteredData=NextPrg->FilteredData; FilteredDataSize=NextPrg->FilteredDataSize; I++; delete PrgStack[I]; PrgStack[I]=NULL; } UnpIO->UnpWrite(FilteredData,FilteredDataSize); UnpSomeRead=true; WrittenFileSize+=FilteredDataSize; WrittenBorder=BlockEnd; WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask); } else { // Current filter intersects the window write border, so we adjust // the window border to process this filter next time, not now. for (size_t J=I;JNextWindow) flt->NextWindow=false; } WrPtr=WrittenBorder; return; } } } UnpWriteArea(WrittenBorder,UnpPtr); WrPtr=UnpPtr; } void Unpack::ExecuteCode(VM_PreparedProgram *Prg) { Prg->InitR[6]=(uint)WrittenFileSize; VM.Execute(Prg); } bool Unpack::ReadTables30() { byte BitLength[BC]; byte Table[HUFF_TABLE_SIZE30]; if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf30()) return(false); Inp.faddbits((8-Inp.InBit)&7); uint BitField=Inp.fgetbits(); if (BitField & 0x8000) { UnpBlockType=BLOCK_PPM; return(PPM.DecodeInit(this,PPMEscChar)); } UnpBlockType=BLOCK_LZ; PrevLowDist=0; LowDistRepCount=0; if (!(BitField & 0x4000)) memset(UnpOldTable,0,sizeof(UnpOldTable)); Inp.faddbits(2); for (uint I=0;I> 12); Inp.faddbits(4); if (Length==15) { uint ZeroCount=(byte)(Inp.fgetbits() >> 12); Inp.faddbits(4); if (ZeroCount==0) BitLength[I]=15; else { ZeroCount+=2; while (ZeroCount-- > 0 && IReadTop-5) if (!UnpReadBuf30()) return(false); uint Number=DecodeNumber(Inp,&BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable[I]) & 0xf; I++; } else if (Number<18) { uint N; if (Number==16) { N=(Inp.fgetbits() >> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } if (I==0) return false; // We cannot have "repeat previous" code at the first position. else while (N-- > 0 && I> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } while (N-- > 0 && IReadTop) return false; MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); return true; } void Unpack::UnpInitData30(bool Solid) { if (!Solid) { TablesRead3=false; memset(UnpOldTable,0,sizeof(UnpOldTable)); PPMEscChar=2; UnpBlockType=BLOCK_LZ; } InitFilters30(Solid); } void Unpack::InitFilters30(bool Solid) { if (!Solid) { OldFilterLengths.SoftReset(); LastFilter=0; for (size_t I=0;I=ReadBorder) { bool FileDone=false; // We use 'while', because for empty block containing only Huffman table, // we'll be on the block border once again just after reading the table. while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && Inp.InBit>=BlockHeader.BlockBitSize) { if (BlockHeader.LastBlockInFile) { FileDone=true; break; } if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) return; } if (FileDone || !UnpReadBuf()) break; } if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) return; if (Suspended) { FileExtracted=false; return; } } uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); if (MainSlot<256) { if (Fragmented) FragWindow[UnpPtr++]=(byte)MainSlot; else Window[UnpPtr++]=(byte)MainSlot; continue; } if (MainSlot>=262) { uint Length=SlotToLength(Inp,MainSlot-262); uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { Distance+=((Inp.getbits32()>>(36-DBits))<<4); Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); Distance+=LowDist; } else { Distance+=Inp.getbits32()>>(32-DBits); Inp.addbits(DBits); } } if (Distance>0x100) { Length++; if (Distance>0x2000) { Length++; if (Distance>0x40000) Length++; } } InsertOldDist(Distance); LastLength=Length; if (Fragmented) FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); else CopyString(Length,Distance); continue; } if (MainSlot==256) { UnpackFilter Filter; if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) break; continue; } if (MainSlot==257) { if (LastLength!=0) if (Fragmented) FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); else CopyString(LastLength,OldDist[0]); continue; } if (MainSlot<262) { uint DistNum=MainSlot-258; uint Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); uint Length=SlotToLength(Inp,LengthSlot); LastLength=Length; if (Fragmented) FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); else CopyString(Length,Distance); continue; } } UnpWriteBuf(); } uint Unpack::ReadFilterData(BitInput &Inp) { uint ByteCount=(Inp.fgetbits()>>14)+1; Inp.addbits(2); uint Data=0; for (uint I=0;I>8)<<(I*8); Inp.addbits(8); } return Data; } bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter) { if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) if (!UnpReadBuf()) return false; Filter.BlockStart=ReadFilterData(Inp); Filter.BlockLength=ReadFilterData(Inp); if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) Filter.BlockLength=0; Filter.Type=Inp.fgetbits()>>13; Inp.faddbits(3); if (Filter.Type==FILTER_DELTA) { Filter.Channels=(Inp.fgetbits()>>11)+1; Inp.faddbits(5); } return true; } bool Unpack::AddFilter(UnpackFilter &Filter) { if (Filters.Size()>=MAX_UNPACK_FILTERS) { UnpWriteBuf(); // Write data, apply and flush filters. if (Filters.Size()>=MAX_UNPACK_FILTERS) InitFilters(); // Still too many filters, prevent excessive memory use. } // If distance to filter start is that large that due to circular dictionary // mode now it points to old not written yet data, then we set 'NextWindow' // flag and process this filter only after processing that older data. Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); Filters.Push(Filter); return true; } bool Unpack::UnpReadBuf() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) return false; BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; if (Inp.InAddr>BitInput::MAX_SIZE/2) { // If we already processed more than half of buffer, let's move // remaining data into beginning to free more space for new data // and ensure that calling function does not cross the buffer border // even if we did not read anything here. Also it ensures that read size // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk // to make it zero. if (DataSize>0) memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); Inp.InAddr=0; ReadTop=DataSize; } else DataSize=ReadTop; int ReadCode=0; if (BitInput::MAX_SIZE!=DataSize) ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); if (ReadCode>0) // Can be also -1. ReadTop+=ReadCode; ReadBorder=ReadTop-30; BlockHeader.BlockStart=Inp.InAddr; if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. { // We may need to quit from main extraction loop and read new block header // and trees earlier than data in input buffer ends. ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); } return ReadCode!=-1; } void Unpack::UnpWriteBuf() { size_t WrittenBorder=WrPtr; size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; size_t WriteSizeLeft=FullWriteSize; bool NotAllFiltersProcessed=false; for (size_t I=0;IType==FILTER_NONE) continue; if (flt->NextWindow) { // Here we skip filters which have block start in current data range // due to address wrap around in circular dictionary, but actually // belong to next dictionary block. If such filter start position // is included to current write range, then we reset 'NextWindow' flag. // In fact we can reset it even without such check, because current // implementation seems to guarantee 'NextWindow' flag reset after // buffer writing for all existing filters. But let's keep this check // just in case. Compressor guarantees that distance between // filter block start and filter storing position cannot exceed // the dictionary size. So if we covered the filter block start with // our write here, we can safely assume that filter is applicable // to next block on no further wrap arounds is possible. if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) flt->NextWindow=false; continue; } uint BlockStart=flt->BlockStart; uint BlockLength=flt->BlockLength; if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. { uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; FilterSrcMemory.Alloc(BlockLength); byte *Mem=&FilterSrcMemory[0]; if (BlockStartUnpWrite(OutMem,BlockLength); UnpSomeRead=true; WrittenFileSize+=BlockLength; WrittenBorder=BlockEnd; WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; } } else { // Current filter intersects the window write border, so we adjust // the window border to process this filter next time, not now. WrPtr=WrittenBorder; // Since Filter start position can only increase, we quit processing // all following filters for this data block and reset 'NextWindow' // flag for them. for (size_t J=I;JType!=FILTER_NONE) flt->NextWindow=false; } // Do not write data left after current filter now. NotAllFiltersProcessed=true; break; } } } // Remove processed filters from queue. size_t EmptyCount=0; for (size_t I=0;I0) Filters[I-EmptyCount]=Filters[I]; if (Filters[I].Type==FILTER_NONE) EmptyCount++; } if (EmptyCount>0) Filters.Alloc(Filters.Size()-EmptyCount); if (!NotAllFiltersProcessed) // Only if all filters are processed. { // Write data left after last filter. UnpWriteArea(WrittenBorder,UnpPtr); WrPtr=UnpPtr; } // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE // instead of potentially huge MaxWinSize blocks. It also allows us // to keep the size of Filters array reasonable. WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; // Choose the nearest among WriteBorder and WrPtr actual written border. // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. if (WriteBorder==UnpPtr || WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) WriteBorder=WrPtr; } byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) { byte *SrcData=Data; switch(Flt->Type) { case FILTER_E8: case FILTER_E8E9: { uint FileOffset=(uint)WrittenFileSize; const uint FileSize=0x1000000; byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" // to avoid overflow for DataSize<4. for (uint CurPos=0;CurPos+4=0 RawPut4(Addr+FileSize,Data); } else if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); D[2]=(byte)(Offset>>16); } } } return SrcData; case FILTER_DELTA: { // Unlike RAR3, we do not need to reject excessive channel // values here, since RAR5 uses only 5 bits to store channel. uint Channels=Flt->Channels,SrcPos=0; FilterDstMemory.Alloc(DataSize); byte *DstData=&FilterDstMemory[0]; // Bytes from same channels are grouped to continual data blocks, // so we need to place them back to their interleaving positions. for (uint CurChannel=0;CurChannel0) { size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); UnpWriteData(&FragWindow[StartPtr],BlockSize); SizeToWrite-=BlockSize; StartPtr=(StartPtr+BlockSize) & MaxWinMask; } } else if (EndPtr=DestUnpSize) return; size_t WriteSize=Size; int64 LeftToWrite=DestUnpSize-WrittenFileSize; if ((int64)WriteSize>LeftToWrite) WriteSize=(size_t)LeftToWrite; UnpIO->UnpWrite(Data,WriteSize); WrittenFileSize+=Size; } void Unpack::UnpInitData50(bool Solid) { if (!Solid) TablesRead5=false; } bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) { Header.HeaderSize=0; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) if (!UnpReadBuf()) return false; Inp.faddbits((8-Inp.InBit)&7); byte BlockFlags=Inp.fgetbits()>>8; Inp.faddbits(8); uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. if (ByteCount==4) return false; Header.HeaderSize=2+ByteCount; Header.BlockBitSize=(BlockFlags&7)+1; byte SavedCheckSum=Inp.fgetbits()>>8; Inp.faddbits(8); int BlockSize=0; for (uint I=0;I>8)<<(I*8); Inp.addbits(8); } Header.BlockSize=BlockSize; byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); if (CheckSum!=SavedCheckSum) return false; Header.BlockStart=Inp.InAddr; ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); Header.LastBlockInFile=(BlockFlags & 0x40)!=0; Header.TablePresent=(BlockFlags & 0x80)!=0; return true; } bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) { if (!Header.TablePresent) return true; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) return false; byte BitLength[BC]; for (uint I=0;I> 12); Inp.faddbits(4); if (Length==15) { uint ZeroCount=(byte)(Inp.fgetbits() >> 12); Inp.faddbits(4); if (ZeroCount==0) BitLength[I]=15; else { ZeroCount+=2; while (ZeroCount-- > 0 && IReadTop-5) if (!UnpReadBuf()) return false; uint Number=DecodeNumber(Inp,&Tables.BD); if (Number<16) { Table[I]=Number; I++; } else if (Number<18) { uint N; if (Number==16) { N=(Inp.fgetbits() >> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } if (I==0) { // We cannot have "repeat previous" code at the first position. // Multiple such codes would shift Inp position without changing I, // which can lead to reading beyond of Inp boundary in mutithreading // mode, where Inp.ExternalBuffer disables bounds check and we just // reserve a lot of buffer space to not need such check normally. return false; } else while (N-- > 0 && I> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } while (N-- > 0 && IReadTop) return false; MakeDecodeTables(&Table[0],&Tables.LD,NC); MakeDecodeTables(&Table[NC],&Tables.DD,DC); MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); return true; } void Unpack::InitFilters() { Filters.SoftReset(); } unrar/unpack50frag.cpp000666 000000 000000 00000004632 13343205467 013365 0ustar00000000 000000 FragmentedWindow::FragmentedWindow() { memset(Mem,0,sizeof(Mem)); memset(MemSize,0,sizeof(MemSize)); } FragmentedWindow::~FragmentedWindow() { Reset(); } void FragmentedWindow::Reset() { for (uint I=0;I=MinSize) { NewMem=(byte *)malloc(Size); if (NewMem!=NULL) break; Size-=Size/32; } if (NewMem==NULL) throw std::bad_alloc(); // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access to unused areas of sliding dictionary. memset(NewMem,0,Size); Mem[BlockNum]=NewMem; TotalSize+=Size; MemSize[BlockNum]=TotalSize; BlockNum++; } if (TotalSize 0) { (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; // We need to have masked UnpPtr after quit from loop, so it must not // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' UnpPtr=(UnpPtr+1) & MaxWinMask; } } void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size) { for (size_t I=0;IBlockCount;I++) DL->D->UnpackPtr->UnpackDecode(DL->D[I]); } void Unpack::InitMT() { if (ReadBufMT==NULL) { // Even getbits32 can read up to 3 additional bytes after current // and our block header and table reading code can look much further. // Let's allocate the additional space here, so we do not need to check // bounds for every bit field access. const size_t Overflow=1024; ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow]; memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow); } if (UnpThreadData==NULL) { uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD; UnpThreadData=new UnpackThreadData[MaxItems]; memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems); for (uint I=0;IDecoded==NULL) { // Typical number of items in RAR blocks does not exceed 0x4000. CurData->DecodedAllocated=0x4100; // It will be freed in the object destructor, not in this file. CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem)); if (CurData->Decoded==NULL) ErrHandler.MemoryError(); } } } } void Unpack::Unpack5MT(bool Solid) { InitMT(); UnpInitData(Solid); for (uint I=0;ILargeBlock=false; CurData->Incomplete=false; } UnpThreadData[0].BlockHeader=BlockHeader; UnpThreadData[0].BlockTables=BlockTables; uint LastBlockNum=0; int DataSize=0; int BlockStart=0; // 'true' if we found a block too large for multithreaded extraction, // so we switched to single threaded mode until the end of file. // Large blocks could cause too high memory use in multithreaded mode. bool LargeBlock=false; bool Done=false; while (!Done) { // Data amount, which is guaranteed to fit block header and tables, // so we can safely read them without additional checks. const int TooSmallToProcess=1024; int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf); if (ReadSize<0) break; DataSize+=ReadSize; if (DataSize==0) break; // First read chunk can be small if we are near the end of volume // and we want it to fit block header and tables. if (ReadSize>0 && DataSizeUnpackPtr=this; // 'Incomplete' thread is present. This is a thread processing block // in the end of buffer, split between two read operations. if (CurData->Incomplete) CurData->DataSize=DataSize; else { CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart); CurData->Inp.InitBitInput(); CurData->DataSize=DataSize-BlockStart; if (CurData->DataSize==0) break; CurData->DamagedData=false; CurData->HeaderRead=false; CurData->TableRead=false; } // We should not use 'last block in file' block flag here unless // we'll check the block size, because even if block is last in file, // it can exceed the current buffer and require more reading. CurData->NoDataLeft=(ReadSize==0); CurData->Incomplete=false; CurData->ThreadNumber=BlockNumber; if (!CurData->HeaderRead) { CurData->HeaderRead=true; if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) || !CurData->BlockHeader.TablePresent && !TablesRead5) { Done=true; break; } TablesRead5=true; } // To prevent too high memory use we switch to single threaded mode // if block exceeds this size. Typically RAR blocks do not exceed // 64 KB, so this protection should not affect most of valid archives. const int LargeBlockSize=0x20000; if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize) LargeBlock=CurData->LargeBlock=true; else BlockNumberMT++; // Number of normal blocks processed in MT mode. BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize; BlockNumber++; int DataLeft=DataSize-BlockStart; if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile) break; // For second and following threads we move smaller blocks to buffer // start to ensure that we have enough data to fit block header // and tables. if (DataLeftD=UnpThreadData+CurBlock; UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock); #ifdef USE_THREADS if (BlockNumber==1) UnpackDecode(*UTD->D); else UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD); #else for (uint I=0;IBlockCount;I++) UnpackDecode(UTD->D[I]); #endif } if (BlockNumber==0) break; #ifdef USE_THREADS UnpThreadPool->WaitDone(); #endif bool IncompleteThread=false; for (uint Block=0;BlockLargeBlock && !ProcessDecoded(*CurData) || CurData->LargeBlock && !UnpackLargeBlock(*CurData) || CurData->DamagedData) { Done=true; break; } if (CurData->Incomplete) { int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT); if (DataSize<=BufPos) // Thread exceeded input buffer boundary. { Done=true; break; } IncompleteThread=true; memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos); CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart; CurData->BlockHeader.HeaderSize=0; CurData->BlockHeader.BlockStart=0; CurData->Inp.InBuf=ReadBufMT; CurData->Inp.InAddr=0; if (Block!=0) { // Move the incomplete thread entry to the first position, // so we'll start processing from it. Preserve the original // buffer for decoded data. UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded; uint DecodedAllocated=UnpThreadData[0].DecodedAllocated; UnpThreadData[0]=*CurData; UnpThreadData[0].Decoded=Decoded; UnpThreadData[0].DecodedAllocated=DecodedAllocated; CurData->Incomplete=false; } BlockStart=0; DataSize-=BufPos; break; } else if (CurData->BlockHeader.LastBlockInFile) { Done=true; break; } } if (IncompleteThread || Done) break; // Current buffer is done, read more data or quit. else { int DataLeft=DataSize-BlockStart; if (DataLeft0) memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft); DataSize=DataLeft; BlockStart=0; break; // Current buffer is done, try to read more data. } } } } UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here. UnpWriteBuf(); BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; BlockTables=UnpThreadData[LastBlockNum].BlockTables; } // Decode Huffman block and save decoded data to memory. void Unpack::UnpackDecode(UnpackThreadData &D) { if (!D.TableRead) { D.TableRead=true; if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) { D.DamagedData=true; return; } } if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) { D.DamagedData=true; return; } D.DecodedSize=0; int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; // Reserve enough space even for filter entry. int DataBorder=D.DataSize-16; int ReadBorder=Min(BlockBorder,DataBorder); while (true) { if (D.Inp.InAddr>=ReadBorder) { if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && D.Inp.InBit>=D.BlockHeader.BlockBitSize) break; // If we do not have any more data in file to read, we must process // what we have until last byte. Otherwise we can return and append // more data to unprocessed few bytes. if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) { D.Incomplete=true; break; } } if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots. { D.DecodedAllocated=D.DecodedAllocated*2; void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem)); if (Decoded==NULL) ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor. D.Decoded=(UnpackDecodedItem *)Decoded; } UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++; uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); if (MainSlot<256) { if (D.DecodedSize>1) { UnpackDecodedItem *PrevItem=CurItem-1; if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3) { PrevItem->Length++; PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; D.DecodedSize--; continue; } } CurItem->Type=UNPDT_LITERAL; CurItem->Literal[0]=(byte)MainSlot; CurItem->Length=0; continue; } if (MainSlot>=262) { uint Length=SlotToLength(D.Inp,MainSlot-262); uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; } else { Distance+=D.Inp.getbits32()>>(32-DBits); D.Inp.addbits(DBits); } } if (Distance>0x100) { Length++; if (Distance>0x2000) { Length++; if (Distance>0x40000) Length++; } } CurItem->Type=UNPDT_MATCH; CurItem->Length=(ushort)Length; CurItem->Distance=Distance; continue; } if (MainSlot==256) { UnpackFilter Filter; ReadFilter(D.Inp,Filter); CurItem->Type=UNPDT_FILTER; CurItem->Length=Filter.Type; CurItem->Distance=Filter.BlockStart; CurItem=D.Decoded+D.DecodedSize++; CurItem->Type=UNPDT_FILTER; CurItem->Length=Filter.Channels; CurItem->Distance=Filter.BlockLength; continue; } if (MainSlot==257) { CurItem->Type=UNPDT_FULLREP; continue; } if (MainSlot<262) { CurItem->Type=UNPDT_REP; CurItem->Distance=MainSlot-258; uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); uint Length=SlotToLength(D.Inp,LengthSlot); CurItem->Length=(ushort)Length; continue; } } } // Process decoded Huffman block data. bool Unpack::ProcessDecoded(UnpackThreadData &D) { UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; while (ItemDestUnpSize) return false; } if (Item->Type==UNPDT_LITERAL) { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) if (Item->Length==3 && UnpPtrLiteral; UnpPtr+=4; } else #endif for (uint I=0;I<=Item->Length;I++) Window[UnpPtr++ & MaxWinMask]=Item->Literal[I]; } else if (Item->Type==UNPDT_MATCH) { InsertOldDist(Item->Distance); LastLength=Item->Length; CopyString(Item->Length,Item->Distance); } else if (Item->Type==UNPDT_REP) { uint Distance=OldDist[Item->Distance]; for (uint I=Item->Distance;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; LastLength=Item->Length; CopyString(Item->Length,Distance); } else if (Item->Type==UNPDT_FULLREP) { if (LastLength!=0) CopyString(LastLength,OldDist[0]); } else if (Item->Type==UNPDT_FILTER) { UnpackFilter Filter; Filter.Type=(byte)Item->Length; Filter.BlockStart=Item->Distance; Item++; Filter.Channels=(byte)Item->Length; Filter.BlockLength=Item->Distance; AddFilter(Filter); } Item++; } return true; } // For large blocks we decode and process in same function in single threaded // mode, so we do not need to store intermediate data in memory. bool Unpack::UnpackLargeBlock(UnpackThreadData &D) { if (!D.TableRead) { D.TableRead=true; if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) { D.DamagedData=true; return false; } } if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) { D.DamagedData=true; return false; } int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; // Reserve enough space even for filter entry. int DataBorder=D.DataSize-16; int ReadBorder=Min(BlockBorder,DataBorder); while (true) { UnpPtr&=MaxWinMask; if (D.Inp.InAddr>=ReadBorder) { if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && D.Inp.InBit>=D.BlockHeader.BlockBitSize) break; // If we do not have any more data in file to read, we must process // what we have until last byte. Otherwise we can return and append // more data to unprocessed few bytes. if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) { D.Incomplete=true; break; } } if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) return false; } uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); if (MainSlot<256) { Window[UnpPtr++]=(byte)MainSlot; continue; } if (MainSlot>=262) { uint Length=SlotToLength(D.Inp,MainSlot-262); uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; } else { Distance+=D.Inp.getbits32()>>(32-DBits); D.Inp.addbits(DBits); } } if (Distance>0x100) { Length++; if (Distance>0x2000) { Length++; if (Distance>0x40000) Length++; } } InsertOldDist(Distance); LastLength=Length; CopyString(Length,Distance); continue; } if (MainSlot==256) { UnpackFilter Filter; if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter)) break; continue; } if (MainSlot==257) { if (LastLength!=0) CopyString(LastLength,OldDist[0]); continue; } if (MainSlot<262) { uint DistNum=MainSlot-258; uint Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); uint Length=SlotToLength(D.Inp,LengthSlot); LastLength=Length; CopyString(Length,Distance); continue; } } return true; } unrar/unpackinline.cpp000666 000000 000000 00000007315 13343205467 013560 0ustar00000000 000000 _forceinline void Unpack::InsertOldDist(uint Distance) { OldDist[3]=OldDist[2]; OldDist[2]=OldDist[1]; OldDist[1]=OldDist[0]; OldDist[0]=Distance; } #ifdef _MSC_VER #define FAST_MEMCPY #endif _forceinline void Unpack::CopyString(uint Length,uint Distance) { size_t SrcPtr=UnpPtr-Distance; if (SrcPtr=8) { Dest[0]=Src[0]; Dest[1]=Src[1]; Dest[2]=Src[2]; Dest[3]=Src[3]; Dest[4]=Src[4]; Dest[5]=Src[5]; Dest[6]=Src[6]; Dest[7]=Src[7]; Src+=8; Dest+=8; Length-=8; } #ifdef FAST_MEMCPY else while (Length>=8) { // In theory we still could overlap here. // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH // always, so overlap here is impossible. // This memcpy expanded inline by MSVC. We could also use uint64 // assignment, which seems to provide about the same speed. memcpy(Dest,Src,8); Src+=8; Dest+=8; Length-=8; } #endif // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. if (Length>0) { Dest[0]=Src[0]; if (Length>1) { Dest[1]=Src[1]; if (Length>2) { Dest[2]=Src[2]; if (Length>3) { Dest[3]=Src[3]; if (Length>4) { Dest[4]=Src[4]; if (Length>5) { Dest[5]=Src[5]; if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. } else while (Length-- > 0) // Slow copying with all possible precautions. { Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask]; // We need to have masked UnpPtr after quit from loop, so it must not // be replaced with 'Window[UnpPtr++ & MaxWinMask]' UnpPtr=(UnpPtr+1) & MaxWinMask; } } _forceinline uint Unpack::DecodeNumber(BitInput &Inp,DecodeTable *Dec) { // Left aligned 15 bit length raw bit field. uint BitField=Inp.getbits() & 0xfffe; if (BitFieldDecodeLen[Dec->QuickBits]) { uint Code=BitField>>(16-Dec->QuickBits); Inp.addbits(Dec->QuickLen[Code]); return Dec->QuickNum[Code]; } // Detect the real bit length for current code. uint Bits=15; for (uint I=Dec->QuickBits+1;I<15;I++) if (BitFieldDecodeLen[I]) { Bits=I; break; } Inp.addbits(Bits); // Calculate the distance from the start code for current bit length. uint Dist=BitField-Dec->DecodeLen[Bits-1]; // Start codes are left aligned, but we need the normal right aligned // number. So we shift the distance to the right. Dist>>=(16-Bits); // Now we can calculate the position in the code list. It is the sum // of first position for current bit length and right aligned distance // between our bit field and start code for current bit length. uint Pos=Dec->DecodePos[Bits]+Dist; // Out of bounds safety check required for damaged archives. if (Pos>=Dec->MaxNum) Pos=0; // Convert the position in the code list to position in alphabet // and return it. return Dec->DecodeNum[Pos]; } _forceinline uint Unpack::SlotToLength(BitInput &Inp,uint Slot) { uint LBits,Length=2; if (Slot<8) { LBits=0; Length+=Slot; } else { LBits=Slot/4-1; Length+=(4 | (Slot & 3)) << LBits; } if (LBits>0) { Length+=Inp.getbits()>>(16-LBits); Inp.addbits(LBits); } return Length; } unrar/uowners.cpp000666 000000 000000 00000007157 13343205467 012606 0ustar00000000 000000 void ExtractUnixOwner20(Archive &Arc,const wchar *FileName) { char NameA[NM]; WideToChar(FileName,NameA,ASIZE(NameA)); if (Arc.BrokenHeader) { uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } struct passwd *pw; errno=0; // Required by getpwnam specification if we need to check errno. if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL) { uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName)); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_WARNING); return; } uid_t OwnerID=pw->pw_uid; struct group *gr; errno=0; // Required by getgrnam specification if we need to check errno. if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL) { uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName)); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CRC); return; } uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) if (lchown(NameA,OwnerID,GroupID)!=0) #else if (chown(NameA,OwnerID,GroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CREATE); } SetFileAttr(FileName,Attr); } void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { char NameA[NM]; WideToChar(FileName,NameA,ASIZE(NameA)); char *OwnerName=(char *)&Arc.SubHead.SubData[0]; int OwnerSize=strlen(OwnerName)+1; int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; char GroupName[NM]; strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize); GroupName[GroupSize]=0; struct passwd *pw; if ((pw=getpwnam(OwnerName))==NULL) { uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(OwnerName)); ErrHandler.SetErrorCode(RARX_WARNING); return; } uid_t OwnerID=pw->pw_uid; struct group *gr; if ((gr=getgrnam(GroupName))==NULL) { uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); ErrHandler.SetErrorCode(RARX_WARNING); return; } uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) if (lchown(NameA,OwnerID,GroupID)!=0) #else if (chown(NameA,OwnerID,GroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CREATE); } SetFileAttr(FileName,Attr); } void SetUnixOwner(Archive &Arc,const wchar *FileName) { char NameA[NM]; WideToChar(FileName,NameA,ASIZE(NameA)); // First, we try to resolve symbolic names. If they are missing or cannot // be resolved, we try to use numeric values if any. If numeric values // are missing too, function fails. FileHeader &hd=Arc.FileHead; if (*hd.UnixOwnerName!=0) { struct passwd *pw; if ((pw=getpwnam(hd.UnixOwnerName))==NULL) { if (!hd.UnixOwnerNumeric) { uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(hd.UnixOwnerName)); ErrHandler.SetErrorCode(RARX_WARNING); return; } } else hd.UnixOwnerID=pw->pw_uid; } if (*hd.UnixGroupName!=0) { struct group *gr; if ((gr=getgrnam(hd.UnixGroupName))==NULL) { if (!hd.UnixGroupNumeric) { uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(hd.UnixGroupName)); ErrHandler.SetErrorCode(RARX_WARNING); return; } } else hd.UnixGroupID=gr->gr_gid; } #if defined(SAVE_LINKS) && !defined(_APPLE) if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) #else if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CREATE); } } unrar/volume.cpp000666 000000 000000 00000017452 13343205467 012412 0ustar00000000 000000 #include "rar.hpp" #ifdef RARDLL static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); #endif bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { RAROptions *Cmd=Arc.GetRAROptions(); HEADER_TYPE HeaderType=Arc.GetHeaderType(); FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) && hd->SplitAfter; if (DataIO!=NULL && SplitHeader) { bool PackedHashPresent=Arc.Format==RARFMT50 || hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff; if (PackedHashPresent && !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL)) uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName); } int64 PosBeforeClose=Arc.Tell(); if (DataIO!=NULL) DataIO->ProcessedArcSize+=Arc.FileLength(); Arc.Close(); wchar NextName[NM]; wcscpy(NextName,Arc.FileName); NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif bool FailedOpen=false,OldSchemeTested=false; #if !defined(SILENT) // In -vp mode we force the pause before next volume even if it is present // and even if we are on the hard disk. It is important when user does not // want to process partially downloaded volumes preliminary. if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) FailedOpen=true; #endif uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; if (!FailedOpen) while (!Arc.Open(NextName,OpenMode)) { // We need to open a new volume which size was not calculated // in total size before, so we cannot calculate the total progress // anymore. Let's reset the total size to zero and stop // the total progress. if (DataIO!=NULL) DataIO->TotalArcSize=0; if (!OldSchemeTested) { // Checking for new style volumes renamed by user to old style // name format. Some users did it for unknown reason. wchar AltNextName[NM]; wcscpy(AltNextName,Arc.FileName); NextVolumeName(AltNextName,ASIZE(AltNextName),true); OldSchemeTested=true; if (Arc.Open(AltNextName,OpenMode)) { wcscpy(NextName,AltNextName); break; } } #ifdef RARDLL if (!DllVolChange(Cmd,NextName,ASIZE(NextName))) { FailedOpen=true; break; } #else // !RARDLL #ifndef SFX_MODULE if (!RecoveryDone) { RecVolumesRestore(Cmd,Arc.FileName,true); RecoveryDone=true; continue; } #endif if (!Cmd->VolumePause && !IsRemovable(NextName)) { FailedOpen=true; break; } #ifndef SILENT if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName))) #endif { FailedOpen=true; break; } #endif // RARDLL } if (FailedOpen) { uiMsg(UIERROR_MISSINGVOL,NextName); Arc.Open(Arc.FileName,OpenMode); Arc.Seek(PosBeforeClose,SEEK_SET); return false; } if (Command=='T' || Command=='X' || Command=='E') mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); Arc.CheckArc(true); #ifdef RARDLL if (!DllVolNotify(Cmd,NextName)) return false; #endif if (SplitHeader) Arc.SearchBlock(HeaderType); else Arc.ReadHeader(); if (Arc.GetHeaderType()==HEAD_FILE) { Arc.ConvertAttributes(); Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); } if (ShowFileName) { mprintf(St(MExtrPoints),Arc.FileHead.FileName); if (!Cmd->DisablePercentage) mprintf(L" "); } if (DataIO!=NULL) { if (HeaderType==HEAD_ENDARC) DataIO->UnpVolume=false; else { DataIO->UnpVolume=hd->SplitAfter; DataIO->SetPackedSizeToRead(hd->PackSize); } #ifdef SFX_MODULE DataIO->UnpArcSize=Arc.FileLength(); #endif // Reset the size of packed data read from current volume. It is used // to display the total progress and preceding volumes are already // compensated with ProcessedArcSize, so we need to reset this variable. DataIO->CurUnpRead=0; DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads); } return true; } #ifdef RARDLL #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Disable the run time stack check for unrar.dll, so we can manipulate // with ChangeVolProc call type below. Run time check would intercept // a wrong ESP before we restore it. #pragma runtime_checks( "s", off ) #endif bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) { bool DllVolChanged=false,DllVolAborted=false; if (Cmd->Callback!=NULL) { wchar OrgNextName[NM]; wcscpy(OrgNextName,NextName); if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) DllVolAborted=true; else if (wcscmp(OrgNextName,NextName)!=0) DllVolChanged=true; else { char NextNameA[NM],OrgNextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); strcpy(OrgNextNameA,NextNameA); if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1) DllVolAborted=true; else if (strcmp(OrgNextNameA,NextNameA)!=0) { // We can damage some Unicode characters by U->A->U conversion, // so set Unicode name only if we see that ANSI name is changed. CharToWide(NextNameA,NextName,NameSize); DllVolChanged=true; } } } if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) { char NextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); // Here we preserve ESP value. It is necessary for those developers, // who still define ChangeVolProc callback as "C" type function, // even though in year 2001 we announced in unrar.dll whatsnew.txt // that it will be PASCAL type (for compatibility with Visual Basic). #if defined(_MSC_VER) #ifndef _WIN_64 __asm mov ebx,esp #endif #elif defined(_WIN_ALL) && defined(__BORLANDC__) _EBX=_ESP; #endif int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); // Restore ESP after ChangeVolProc with wrongly defined calling // convention broken it. #if defined(_MSC_VER) #ifndef _WIN_64 __asm mov esp,ebx #endif #elif defined(_WIN_ALL) && defined(__BORLANDC__) _ESP=_EBX; #endif if (RetCode==0) DllVolAborted=true; else CharToWide(NextNameA,NextName,NameSize); } // We quit only on 'abort' condition, but not on 'name not changed'. // It is legitimate for program to return the same name when waiting // for currently non-existent volume. // Also we quit to prevent an infinite loop if no callback is defined. if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL) { Cmd->DllError=ERAR_EOPEN; return false; } return true; } #endif #ifdef RARDLL bool DllVolNotify(RAROptions *Cmd,wchar *NextName) { char NextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); if (Cmd->Callback!=NULL) { if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) return false; if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1) return false; } if (Cmd->ChangeVolProc!=NULL) { #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) _EBX=_ESP; #endif int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) _ESP=_EBX; #endif if (RetCode==0) return false; } return true; } #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Restore the run time stack check for unrar.dll. #pragma runtime_checks( "s", restore ) #endif #endif unrar/win32acl.cpp000666 000000 000000 00000006273 13343205467 012524 0ustar00000000 000000 static void SetACLPrivileges(); static bool ReadSacl=false; #ifndef SFX_MODULE void ExtractACL20(Archive &Arc,const wchar *FileName) { SetACLPrivileges(); if (Arc.BrokenHeader) { uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>VER_PACK) { uiMsg(UIERROR_ACLUNKNOWN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_WARNING); return; } ComprDataIO DataIO; Unpack Unpack(&DataIO); Unpack.Init(0x10000,false); Array UnpData(Arc.EAHead.UnpSize); DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,NULL); DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.EAHead.UnpSize); Unpack.DoUnpack(Arc.EAHead.UnpVer,false); if (Arc.EAHead.EACRC!=DataIO.UnpHash.GetCRC32()) { uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION; if (ReadSacl) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0]; int SetCode=SetFileSecurity(FileName,si,sd); if (!SetCode) { uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) uiMsg(UIERROR_NEEDADMIN); ErrHandler.SetErrorCode(RARX_WARNING); } } #endif void ExtractACL(Archive &Arc,const wchar *FileName) { Array SubData; if (!Arc.ReadSubData(&SubData,NULL)) return; SetACLPrivileges(); SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION; if (ReadSacl) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0]; int SetCode=SetFileSecurity(FileName,si,sd); if (!SetCode) { wchar LongName[NM]; if (GetWinLongPath(FileName,LongName,ASIZE(LongName))) SetCode=SetFileSecurity(LongName,si,sd); } if (!SetCode) { uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) uiMsg(UIERROR_NEEDADMIN); ErrHandler.SetErrorCode(RARX_WARNING); } } void SetACLPrivileges() { static bool InitDone=false; if (InitDone) return; if (SetPrivilege(SE_SECURITY_NAME)) ReadSacl=true; SetPrivilege(SE_RESTORE_NAME); InitDone=true; } bool SetPrivilege(LPCTSTR PrivName) { bool Success=false; HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) && AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && GetLastError() == ERROR_SUCCESS) Success=true; CloseHandle(hToken); } return Success; } unrar/win32lnk.cpp000666 000000 000000 00000013471 13343205467 012547 0ustar00000000 000000 #define SYMLINK_FLAG_RELATIVE 1 typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) { static bool PrivSet=false; if (!PrivSet) { SetPrivilege(SE_RESTORE_NAME); // Not sure if we really need it, but let's request anyway. SetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME); PrivSet=true; } const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024; Array Buf(BufSize); REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; wchar SubstName[NM]; wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName)); size_t SubstLength=wcslen(SubstName); wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName; bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0; if (WinPrefix) PrintNameSrc+=4; if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0) { *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name. PrintNameSrc+=3; } wcscpy(PrintNameDst,PrintNameSrc); size_t PrintLength=wcslen(PrintName); bool AbsPath=WinPrefix; // IsFullPath is not really needed here, AbsPath check is enough. // We added it just for extra safety, in case some Windows version would // allow to create absolute targets with SYMLINK_FLAG_RELATIVE. // Use hd->FileName instead of Name, since Name can include the destination // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) || !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) return false; CreatePath(Name,true); // 'DirTarget' check is important for Unix symlinks to directories. // Unix symlinks do not have their own 'directory' attribute. if (hd->Dir || hd->DirTarget) { if (!CreateDirectory(Name,NULL)) return false; } else { HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile == INVALID_HANDLE_VALUE) return false; CloseHandle(hFile); } if (hd->RedirType==FSREDIR_JUNCTION) { rdb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; rdb->ReparseDataLength=USHORT( sizeof(rdb->MountPointReparseBuffer.SubstituteNameOffset)+ sizeof(rdb->MountPointReparseBuffer.SubstituteNameLength)+ sizeof(rdb->MountPointReparseBuffer.PrintNameOffset)+ sizeof(rdb->MountPointReparseBuffer.PrintNameLength)+ (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); rdb->Reserved=0; rdb->MountPointReparseBuffer.SubstituteNameOffset=0; rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName); rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName); } else if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK) { rdb->ReparseTag=IO_REPARSE_TAG_SYMLINK; rdb->ReparseDataLength=USHORT( sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset)+ sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameLength)+ sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameOffset)+ sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameLength)+ sizeof(rdb->SymbolicLinkReparseBuffer.Flags)+ (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); rdb->Reserved=0; rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0; rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName); rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName); rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE; } else return false; HANDLE hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,0,NULL, OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT| FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) return false; DWORD Returned; if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb, FIELD_OFFSET(REPARSE_DATA_BUFFER,GenericReparseBuffer)+ rdb->ReparseDataLength,NULL,0,&Returned,NULL)) { CloseHandle(hFile); uiMsg(UIERROR_SLINKCREATE,UINULL,Name); DWORD LastError=GetLastError(); if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) && !IsUserAdmin()) uiMsg(UIERROR_NEEDADMIN); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CREATE); if (hd->Dir) RemoveDirectory(Name); else DeleteFile(Name); return false; } File LinkFile; LinkFile.SetHandle(hFile); LinkFile.SetOpenFileTime( Cmd->xmtime==EXTTIME_NONE ? NULL:&hd->mtime, Cmd->xctime==EXTTIME_NONE ? NULL:&hd->ctime, Cmd->xatime==EXTTIME_NONE ? NULL:&hd->atime); LinkFile.Close(); if (!Cmd->IgnoreGeneralAttr) SetFileAttr(Name,hd->FileAttr); return true; } unrar/win32stm.cpp000666 000000 000000 00000007575 13343205467 012576 0ustar00000000 000000 #if !defined(SFX_MODULE) && defined(_WIN_ALL) void ExtractStreams20(Archive &Arc,const wchar *FileName) { if (Arc.BrokenHeader) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) { uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_WARNING); return; } wchar StreamName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { wcscpy(StreamName,L".\\"); wcscpy(StreamName+2,FileName); } else wcscpy(StreamName,FileName); if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || Arc.StreamHead.StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } wchar StoredName[NM]; CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); ConvertPath(StoredName+1,StoredName+1); wcsncatz(StreamName,StoredName,ASIZE(StreamName)); FindData fd; bool Found=FindFile::FastFind(FileName,&fd); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(StreamName)) { ComprDataIO DataIO; Unpack Unpack(&DataIO); Unpack.Init(0x10000,false); DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,&CurFile); DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.StreamHead.UnpSize); Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); ErrHandler.SetErrorCode(RARX_CRC); } else CurFile.Close(); } File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr); } #endif #ifdef _WIN_ALL void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) { wchar FullName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { wcscpy(FullName,L".\\"); wcsncpyz(FullName+2,FileName,ASIZE(FullName)-2); } else wcsncpyz(FullName,FileName,ASIZE(FullName)); wchar StreamName[NM]; GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); if (*StreamName!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (TestMode) { Arc.ReadSubData(NULL,NULL); return; } wcsncatz(FullName,StreamName,ASIZE(FullName)); FindData fd; bool Found=FindFile::FastFind(FileName,&fd); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile)) CurFile.Close(); File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); // Restoring original file attributes. Important if file was read only // or did not have "Archive" attribute SetFileAttr(FileName,fd.FileAttr); } #endif void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize) { byte *Data=&Arc.SubHead.SubData[0]; size_t DataSize=Arc.SubHead.SubData.Size(); if (Arc.Format==RARFMT15) { size_t DestSize=Min(DataSize/2,MaxSize-1); RawToWide(Data,StreamName,DestSize); StreamName[DestSize]=0; } else { char UtfString[NM*4]; size_t DestSize=Min(DataSize,ASIZE(UtfString)-1); memcpy(UtfString,Data,DestSize); UtfString[DestSize]=0; UtfToWide(UtfString,StreamName,MaxSize); } } unrar/archive.hpp000666 000000 000000 00000010063 13343205463 012514 0ustar00000000 000000 #ifndef _RAR_ARCHIVE_ #define _RAR_ARCHIVE_ class PPack; class RawRead; class RawWrite; enum NOMODIFY_FLAGS { NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4 }; enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE}; enum ADDSUBDATA_FLAGS { ASDF_SPLIT = 1, // Allow to split archive just before header if necessary. ASDF_COMPRESS = 2, // Allow to compress data following subheader. ASDF_CRYPT = 4, // Encrypt data after subheader if password is set. ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode. }; // RAR5 headers must not exceed 2 MB. #define MAX_HEADER_SIZE_RAR5 0x200000 class Archive:public File { private: void UpdateLatestTime(FileHeader *CurBlock); void ConvertNameCase(wchar *Name); void ConvertFileHeader(FileHeader *hd); void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite); size_t ReadHeader14(); size_t ReadHeader15(); size_t ReadHeader50(); void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); void RequestArcPassword(); void UnexpEndArcMsg(); void BrokenHeaderMsg(); void UnkEncVerMsg(const wchar *Name); void UnkEncVerMsg(); bool ReadCommentData(Array *CmtData); #if !defined(RAR_NOCRYPT) CryptData HeadersCrypt; #endif ComprDataIO SubDataIO; bool DummyCmd; RAROptions *Cmd; RarTime LatestTime; int LastReadBlock; HEADER_TYPE CurHeaderType; bool SilentOpen; #ifdef USE_QOPEN QuickOpen QOpen; bool ProhibitQOpen; #endif public: Archive(RAROptions *InitCmd=NULL); ~Archive(); static RARFORMAT IsSignature(const byte *D,size_t Size); bool IsArchive(bool EnableBroken); size_t SearchBlock(HEADER_TYPE HeaderType); size_t SearchSubBlock(const wchar *Type); size_t SearchRR(); void WriteBlock(HEADER_TYPE HeaderType,BaseBlock *wb=NULL,bool OnlySetSize=false,bool NonFinalWrite=false); void SetBlockSize(HEADER_TYPE HeaderType,BaseBlock *wb=NULL) {WriteBlock(HeaderType,wb,true);} size_t ReadHeader(); void CheckArc(bool EnableBroken); void CheckOpen(const wchar *Name); bool WCheckOpen(const wchar *Name); bool GetComment(Array *CmtData); void ViewComment(); void SetLatestTime(RarTime *NewTime); void SeekToNext(); bool CheckAccess(); bool IsArcDir(); void ConvertAttributes(); void VolSubtractHeaderSize(size_t SubSize); uint FullHeaderSize(size_t Size); int64 GetStartPos(); void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, const wchar *Name,uint Flags); bool ReadSubData(Array *UnpData,File *DestFile); HEADER_TYPE GetHeaderType() {return CurHeaderType;}; RAROptions* GetRAROptions() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} #if 0 void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); #endif #ifdef USE_QOPEN bool Open(const wchar *Name,uint Mode=FMF_READ); int Read(void *Data,size_t Size); void Seek(int64 Offset,int Method); int64 Tell(); void QOpenUnload() {QOpen.Unload();} void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} #endif BaseBlock ShortBlock; MarkHeader MarkHead; MainHeader MainHead; CryptHeader CryptHead; FileHeader FileHead; EndArcHeader EndArcHead; SubBlockHeader SubBlockHead; FileHeader SubHead; CommentHeader CommHead; ProtectHeader ProtectHead; UnixOwnersHeader UOHead; EAHeader EAHead; StreamHeader StreamHead; int64 CurBlockPos; int64 NextBlockPos; RARFORMAT Format; bool Solid; bool Volume; bool MainComment; bool Locked; bool Signed; bool FirstVolume; bool NewNumbering; bool Protected; bool Encrypted; size_t SFXSize; bool BrokenHeader; bool FailedHeaderDecryption; #if !defined(RAR_NOCRYPT) byte ArcSalt[SIZE_SALT50]; #endif bool Splitting; uint VolNumber; int64 VolWrite; uint64 AddingFilesSize; uint64 AddingHeadersSize; bool NewArchive; wchar FirstVolumeName[NM]; }; #endif unrar/arcmem.hpp000666 000000 000000 00000000665 13251145606 012346 0ustar00000000 000000 #ifndef _RAR_ARCMEM_ #define _RAR_ARCMEM_ // Memory interface for software fuzzers. class ArcMemory { private: bool Loaded; Array ArcData; uint64 SeekPos; public: ArcMemory(); void Load(const byte *Data,size_t Size); bool Unload(); bool IsLoaded() {return Loaded;} bool Read(void *Data,size_t Size,size_t &Result); bool Seek(int64 Offset,int Method); bool Tell(int64 *Pos); }; #endif unrar/array.hpp000666 000000 000000 00000007116 13343205463 012216 0ustar00000000 000000 #ifndef _RAR_ARRAY_ #define _RAR_ARRAY_ extern ErrorHandler ErrHandler; template class Array { private: T *Buffer; size_t BufSize; size_t AllocSize; size_t MaxSize; bool Secure; // Clean memory if true. public: Array(); Array(size_t Size); Array(const Array &Src); // Copy constructor. ~Array(); inline void CleanData(); inline T& operator [](size_t Item) const; inline T* operator + (size_t Pos); inline size_t Size(); // Returns the size in items, not in bytes. void Add(size_t Items); void Alloc(size_t Items); void Reset(); void SoftReset(); void operator = (Array &Src); void Push(T Item); void Append(T *Item,size_t Count); T* Addr(size_t Item) {return Buffer+Item;} void SetMaxSize(size_t Size) {MaxSize=Size;} T* Begin() {return Buffer;} T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} void SetSecure() {Secure=true;} }; template void Array::CleanData() { Buffer=NULL; BufSize=0; AllocSize=0; MaxSize=0; Secure=false; } template Array::Array() { CleanData(); } template Array::Array(size_t Size) { CleanData(); Add(Size); } // Copy constructor in case we need to pass an object as value. template Array::Array(const Array &Src) { CleanData(); Alloc(Src.BufSize); if (Src.BufSize!=0) memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); } template Array::~Array() { if (Buffer!=NULL) { if (Secure) cleandata(Buffer,AllocSize*sizeof(T)); free(Buffer); } } template inline T& Array::operator [](size_t Item) const { return Buffer[Item]; } template inline T* Array::operator +(size_t Pos) { return Buffer+Pos; } template inline size_t Array::Size() { return BufSize; } template void Array::Add(size_t Items) { BufSize+=Items; if (BufSize>AllocSize) { if (MaxSize!=0 && BufSize>MaxSize) { ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); ErrHandler.MemoryError(); } size_t Suggested=AllocSize+AllocSize/4+32; size_t NewSize=Max(BufSize,Suggested); T *NewBuffer; if (Secure) { NewBuffer=(T *)malloc(NewSize*sizeof(T)); if (NewBuffer==NULL) ErrHandler.MemoryError(); if (Buffer!=NULL) { memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); cleandata(Buffer,AllocSize*sizeof(T)); free(Buffer); } } else { NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); if (NewBuffer==NULL) ErrHandler.MemoryError(); } Buffer=NewBuffer; AllocSize=NewSize; } } template void Array::Alloc(size_t Items) { if (Items>AllocSize) Add(Items-BufSize); else BufSize=Items; } template void Array::Reset() { if (Buffer!=NULL) { free(Buffer); Buffer=NULL; } BufSize=0; AllocSize=0; } // Reset buffer size, but preserve already allocated memory if any, // so we can reuse it without wasting time to allocation. template void Array::SoftReset() { BufSize=0; } template void Array::operator =(Array &Src) { Reset(); Alloc(Src.BufSize); if (Src.BufSize!=0) memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); } template void Array::Push(T Item) { Add(1); (*this)[Size()-1]=Item; } template void Array::Append(T *Items,size_t Count) { size_t CurSize=Size(); Add(Count); memcpy(Buffer+CurSize,Items,Count*sizeof(T)); } #endif unrar/blake2s.hpp000666 000000 000000 00000004361 13343205463 012422 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves #ifndef _RAR_BLAKE2_ #define _RAR_BLAKE2_ #define BLAKE2_DIGEST_SIZE 32 enum blake2s_constant { BLAKE2S_BLOCKBYTES = 64, BLAKE2S_OUTBYTES = 32 }; // Alignment to 64 improves performance of both SSE and non-SSE versions. // Alignment to n*16 is required for SSE version, so we selected 64. // We use the custom alignment scheme instead of __declspec(align(x)), // because it is less compiler dependent. Also the compiler directive // does not help if structure is a member of class allocated through // 'new' operator. struct blake2s_state { enum { BLAKE_ALIGNMENT = 64 }; // buffer and uint32 h[8], t[2], f[2]; enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES]. uint32 *h, *t, *f; // uint32 h[8], t[2], f[2]. size_t buflen; byte last_node; blake2s_state() { set_pointers(); } // Required when we declare and assign in the same command. blake2s_state(blake2s_state &st) { set_pointers(); *this=st; } void set_pointers() { // Set aligned pointers. Must be done in constructor, not in Init(), // so assignments like 'blake2sp_state res=blake2ctx' work correctly // even if blake2sp_init is not called for 'res'. buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT); h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES); t = h + 8; f = t + 2; } void init() { memset( ubuf, 0, sizeof( ubuf ) ); buflen = 0; last_node = 0; } // Since we use pointers, the default = would work incorrectly. blake2s_state& operator = (blake2s_state &st) { if (this != &st) { memcpy(buf, st.buf, BLAKE_DATA_SIZE); buflen = st.buflen; last_node = st.last_node; } return *this; } }; #ifdef RAR_SMP class ThreadPool; #endif struct blake2sp_state { blake2s_state S[8]; blake2s_state R; byte buf[8 * BLAKE2S_BLOCKBYTES]; size_t buflen; #ifdef RAR_SMP ThreadPool *ThPool; uint MaxThreads; #endif }; void blake2sp_init( blake2sp_state *S ); void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ); void blake2sp_final( blake2sp_state *S, byte *digest ); #endif unrar/cmddata.hpp000666 000000 000000 00000003465 13343205463 012500 0ustar00000000 000000 #ifndef _RAR_CMDDATA_ #define _RAR_CMDDATA_ #define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; class CommandData:public RAROptions { private: void ProcessSwitchesString(const wchar *Str); void ProcessSwitch(const wchar *Switch); void BadSwitch(const wchar *Switch); uint GetExclAttr(const wchar *Str); bool FileLists; bool NoMoreSwitches; RAR_CMD_LIST_MODE ListMode; bool BareOutput; public: CommandData(); void Init(); void ParseCommandLine(bool Preprocess,int argc, char *argv[]); void ParseArg(wchar *ArgW); void ParseDone(); void ParseEnvVar(); void ReadConfig(); void PreprocessArg(const wchar *Arg); void OutTitle(); void OutHelp(RAR_EXIT ExitCode); bool IsSwitch(int Ch); bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList); static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode); bool ExclDirByAttr(uint FileAttr); bool TimeCheck(RarTime &ft); bool SizeCheck(int64 Size); bool AnyFiltersActive(); int IsProcessFile(FileHeader &FileHead,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH, wchar *MatchedArg=NULL,uint MatchedArgSize=0); void ProcessCommand(); void AddArcName(const wchar *Name); bool GetArcName(wchar *Name,int MaxSize); bool CheckWinSize(); int GetRecoverySize(const wchar *Str,int DefSize); #ifndef SFX_MODULE void ReportWrongSwitches(RARFORMAT Format); #endif wchar Command[NM+16]; wchar ArcName[NM]; StringList FileArgs; StringList ExclArgs; StringList InclArgs; StringList ArcNames; StringList StoreArgs; }; #endif unrar/coder.hpp000666 000000 000000 00000001170 13343205463 012166 0ustar00000000 000000 /**************************************************************************** * Contents: 'Carryless rangecoder' by Dmitry Subbotin * ****************************************************************************/ class RangeCoder { public: void InitDecoder(Unpack *UnpackRead); inline int GetCurrentCount(); inline uint GetCurrentShiftCount(uint SHIFT); inline void Decode(); inline void PutChar(unsigned int c); inline unsigned int GetChar(); uint low, code, range; struct SUBRANGE { uint LowCount, HighCount, scale; } SubRange; Unpack *UnpackRead; }; unrar/compress.hpp000666 000000 000000 00000004122 13343205463 012725 0ustar00000000 000000 #ifndef _RAR_COMPRESS_ #define _RAR_COMPRESS_ // Combine pack and unpack constants to class to avoid polluting global // namespace with numerous short names. class PackDef { public: // Maximum LZ match length we can encode even for short distances. static const uint MAX_LZ_MATCH = 0x1001; // We increment LZ match length for longer distances, because shortest // matches are not allowed for them. Maximum length increment is 3 // for distances larger than 256KB (0x40000). Here we define the maximum // incremented LZ match. Normally packer does not use it, but we must be // ready to process it in corrupt archives. static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. static const uint LOW_DIST_REP_COUNT = 16; static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ static const uint DC = 64; static const uint LDC = 16; static const uint RC = 44; static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; static const uint BC = 20; static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ static const uint DC30 = 60; static const uint LDC30 = 17; static const uint RC30 = 28; static const uint BC30 = 20; static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ static const uint DC20 = 48; static const uint RC20 = 28; static const uint BC20 = 19; static const uint MC20 = 257; // Largest alphabet size among all values listed above. static const uint LARGEST_TABLE_SIZE = 306; enum { CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA }; }; enum FilterType { // These values must not be changed, because we use them directly // in RAR5 compression and decompression code. FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE }; #endif unrar/consio.hpp000666 000000 000000 00000001374 13343205463 012372 0ustar00000000 000000 #ifndef _RAR_CONSIO_ #define _RAR_CONSIO_ void InitConsole(); void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); void OutComment(const wchar *Comment,size_t Size); #ifndef SILENT bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); #endif #ifdef SILENT inline void mprintf(const wchar *fmt,...) {} inline void eprintf(const wchar *fmt,...) {} inline void Alarm() {} inline int Ask(const wchar *AskStr) {return 0;} inline bool getwstr(wchar *str,size_t n) {return false;} #else void mprintf(const wchar *fmt,...); void eprintf(const wchar *fmt,...); void Alarm(); int Ask(const wchar *AskStr); bool getwstr(wchar *str,size_t n); #endif #endif unrar/crc.hpp000666 000000 000000 00000000523 13343205463 011642 0ustar00000000 000000 #ifndef _RAR_CRC_ #define _RAR_CRC_ // This function is only to intialize external CRC tables. We do not need to // call it before calculating CRC32. void InitCRC32(uint *CRCTab); uint CRC32(uint StartCRC,const void *Addr,size_t Size); #ifndef SFX_MODULE ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size); #endif #endif unrar/crypt.hpp000666 000000 000000 00000005330 13343205463 012235 0ustar00000000 000000 #ifndef _RAR_CRYPT_ #define _RAR_CRYPT_ enum CRYPT_METHOD { CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50 }; #define SIZE_SALT50 16 #define SIZE_SALT30 8 #define SIZE_INITV 16 #define SIZE_PSWCHECK 8 #define SIZE_PSWCHECK_CSUM 4 #define CRYPT_BLOCK_SIZE 16 #define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf #define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. #define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. #define CRYPT_VERSION 0 // Supported encryption version. class CryptData { struct KDF5CacheItem { SecPassword Pwd; byte Salt[SIZE_SALT50]; byte Key[32]; uint Lg2Count; // Log2 of PBKDF2 repetition count. byte PswCheckValue[SHA256_DIGEST_SIZE]; byte HashKeyValue[SHA256_DIGEST_SIZE]; }; struct KDF3CacheItem { SecPassword Pwd; byte Salt[SIZE_SALT30]; byte Key[16]; byte Init[16]; bool SaltPresent; }; private: void SetKey13(const char *Password); void Decrypt13(byte *Data,size_t Count); void SetKey15(const char *Password); void Crypt15(byte *Data,size_t Count); void SetKey20(const char *Password); void Swap20(byte *Ch1,byte *Ch2); void UpdKeys20(byte *Buf); void EncryptBlock20(byte *Buf); void DecryptBlock20(byte *Buf); void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); KDF3CacheItem KDF3Cache[4]; uint KDF3CachePos; KDF5CacheItem KDF5Cache[4]; uint KDF5CachePos; CRYPT_METHOD Method; Rijndael rin; uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption. byte SubstTable20[256]; uint Key20[4]; byte Key13[3]; ushort Key15[4]; public: CryptData(); ~CryptData(); bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt, byte *HashKey,byte *PswCheck); void SetAV15Encryption(); void SetCmt13Encryption(); void EncryptBlock(byte *Buf,size_t Size); void DecryptBlock(byte *Buf,size_t Size); static void SetSalt(byte *Salt,size_t SaltSize); }; void GetRnd(byte *RndBuf,size_t BufSize); void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, size_t DataLength,byte *ResDigest); void pbkdf2(const byte *pass, size_t pass_len, const byte *salt, size_t salt_len,byte *key, byte *Value1, byte *Value2, uint rounds); void ConvertHashToMAC(HashValue *Value,byte *Key); #endif unrar/dll.hpp000666 000000 000000 00000011267 13343205464 011656 0ustar00000000 000000 #ifndef _UNRAR_DLL_ #define _UNRAR_DLL_ #pragma pack(1) #define ERAR_SUCCESS 0 #define ERAR_END_ARCHIVE 10 #define ERAR_NO_MEMORY 11 #define ERAR_BAD_DATA 12 #define ERAR_BAD_ARCHIVE 13 #define ERAR_UNKNOWN_FORMAT 14 #define ERAR_EOPEN 15 #define ERAR_ECREATE 16 #define ERAR_ECLOSE 17 #define ERAR_EREAD 18 #define ERAR_EWRITE 19 #define ERAR_SMALL_BUF 20 #define ERAR_UNKNOWN 21 #define ERAR_MISSING_PASSWORD 22 #define ERAR_EREFERENCE 23 #define ERAR_BAD_PASSWORD 24 #define RAR_OM_LIST 0 #define RAR_OM_EXTRACT 1 #define RAR_OM_LIST_INCSPLIT 2 #define RAR_SKIP 0 #define RAR_TEST 1 #define RAR_EXTRACT 2 #define RAR_VOL_ASK 0 #define RAR_VOL_NOTIFY 1 #define RAR_DLL_VERSION 8 #define RAR_HASH_NONE 0 #define RAR_HASH_CRC32 1 #define RAR_HASH_BLAKE2 2 #ifdef _UNIX #define CALLBACK #define PASCAL #define LONG long #define HANDLE void * #define LPARAM long #define UINT unsigned int #endif #define RHDF_SPLITBEFORE 0x01 #define RHDF_SPLITAFTER 0x02 #define RHDF_ENCRYPTED 0x04 #define RHDF_SOLID 0x10 #define RHDF_DIRECTORY 0x20 struct RARHeaderData { char ArcName[260]; char FileName[260]; unsigned int Flags; unsigned int PackSize; unsigned int UnpSize; unsigned int HostOS; unsigned int FileCRC; unsigned int FileTime; unsigned int UnpVer; unsigned int Method; unsigned int FileAttr; char *CmtBuf; unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; }; struct RARHeaderDataEx { char ArcName[1024]; wchar_t ArcNameW[1024]; char FileName[1024]; wchar_t FileNameW[1024]; unsigned int Flags; unsigned int PackSize; unsigned int PackSizeHigh; unsigned int UnpSize; unsigned int UnpSizeHigh; unsigned int HostOS; unsigned int FileCRC; unsigned int FileTime; unsigned int UnpVer; unsigned int Method; unsigned int FileAttr; char *CmtBuf; unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; unsigned int DictSize; unsigned int HashType; char Hash[32]; unsigned int RedirType; wchar_t *RedirName; unsigned int RedirNameSize; unsigned int DirTarget; unsigned int MtimeLow; unsigned int MtimeHigh; unsigned int CtimeLow; unsigned int CtimeHigh; unsigned int AtimeLow; unsigned int AtimeHigh; unsigned int Reserved[988]; }; struct RAROpenArchiveData { char *ArcName; unsigned int OpenMode; unsigned int OpenResult; char *CmtBuf; unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; }; typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); #define ROADF_VOLUME 0x0001 #define ROADF_COMMENT 0x0002 #define ROADF_LOCK 0x0004 #define ROADF_SOLID 0x0008 #define ROADF_NEWNUMBERING 0x0010 #define ROADF_SIGNED 0x0020 #define ROADF_RECOVERY 0x0040 #define ROADF_ENCHEADERS 0x0080 #define ROADF_FIRSTVOLUME 0x0100 struct RAROpenArchiveDataEx { char *ArcName; wchar_t *ArcNameW; unsigned int OpenMode; unsigned int OpenResult; char *CmtBuf; unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; unsigned int Flags; UNRARCALLBACK Callback; LPARAM UserData; unsigned int Reserved[28]; }; enum UNRARCALLBACK_MESSAGES { UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, UCM_NEEDPASSWORDW }; typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); #ifdef __cplusplus extern "C" { #endif HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); int PASCAL RARCloseArchive(HANDLE hArcData); int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); void PASCAL RARSetPassword(HANDLE hArcData,char *Password); int PASCAL RARGetDllVersion(); #ifdef __cplusplus } #endif #pragma pack() #endif unrar/encname.hpp000666 000000 000000 00000000634 13343205464 012505 0ustar00000000 000000 #ifndef _RAR_ENCNAME_ #define _RAR_ENCNAME_ class EncodeFileName { private: void AddFlags(int Value); byte *EncName; byte Flags; uint FlagBits; size_t FlagsPos; size_t DestSize; public: EncodeFileName(); size_t Encode(char *Name,wchar *NameW,byte *EncName); void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize); }; #endif unrar/errhnd.hpp000666 000000 000000 00000004463 13343205464 012365 0ustar00000000 000000 #ifndef _RAR_ERRHANDLER_ #define _RAR_ERRHANDLER_ enum RAR_EXIT // RAR exit code. { RARX_SUCCESS = 0, RARX_WARNING = 1, RARX_FATAL = 2, RARX_CRC = 3, RARX_LOCK = 4, RARX_WRITE = 5, RARX_OPEN = 6, RARX_USERERROR = 7, RARX_MEMORY = 8, RARX_CREATE = 9, RARX_NOFILES = 10, RARX_BADPWD = 11, RARX_USERBREAK = 255 }; class ErrorHandler { private: RAR_EXIT ExitCode; uint ErrCount; bool EnableBreak; bool Silent; bool DisableShutdown; // Shutdown is not suitable after last error. public: ErrorHandler(); void Clean(); void MemoryError(); void OpenError(const wchar *FileName); void CloseError(const wchar *FileName); void ReadError(const wchar *FileName); bool AskRepeatRead(const wchar *FileName); void WriteError(const wchar *ArcName,const wchar *FileName); void WriteErrorFAT(const wchar *FileName); bool AskRepeatWrite(const wchar *FileName,bool DiskFull); void SeekError(const wchar *FileName); void GeneralErrMsg(const wchar *fmt,...); void MemoryErrorMsg(); void OpenErrorMsg(const wchar *FileName); void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); void CreateErrorMsg(const wchar *FileName); void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); void ReadErrorMsg(const wchar *FileName); void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); void ArcBrokenMsg(const wchar *ArcName); void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); void Exit(RAR_EXIT ExitCode); void SetErrorCode(RAR_EXIT Code); RAR_EXIT GetErrorCode() {return ExitCode;} uint GetErrorCount() {return ErrCount;} void SetSignalHandlers(bool Enable); void Throw(RAR_EXIT Code); void SetSilent(bool Mode) {Silent=Mode;}; bool GetSysErrMsg(wchar *Msg,size_t Size); void SysErrMsg(); int GetSystemErrorCode(); void SetSystemErrorCode(int Code); void SetDisableShutdown() {DisableShutdown=true;} bool IsShutdownEnabled() {return !DisableShutdown;} bool UserBreak; // Ctrl+Break is pressed. bool MainExit; // main() is completed. }; #endif unrar/extinfo.hpp000666 000000 000000 00000001356 13343205464 012555 0ustar00000000 000000 #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); #ifdef _UNIX void SetUnixOwner(Archive &Arc,const wchar *FileName); #endif bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); #ifdef _WIN_ALL bool SetPrivilege(LPCTSTR PrivName); #endif void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); #endif unrar/extract.hpp000666 000000 000000 00000003617 13343205464 012555 0ustar00000000 000000 #ifndef _RAR_EXTRACT_ #define _RAR_EXTRACT_ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; class CmdExtract { private: EXTRACT_ARC_CODE ExtractArchive(); bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); #ifdef RARDLL bool ExtrDllGetPassword(); #else bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); #endif void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); bool ExtrCreateFile(Archive &Arc,File &CurFile); bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); RarTime StartTime; // time when extraction started CommandData *Cmd; ComprDataIO DataIO; Unpack *Unp; unsigned long TotalFileCount; unsigned long FileCount; unsigned long MatchedArgs; bool FirstFile; bool AllMatchesExact; bool ReconstructDone; // If any non-zero solid file was successfully unpacked before current. // If true and if current encrypted file is broken, obviously // the password is correct and we can report broken CRC without // any wrong password hints. bool AnySolidDataUnpackedWell; wchar ArcName[NM]; bool GlobalPassword; bool PrevProcessed; // If previous file was successfully extracted or tested. wchar DestFileName[NM]; bool PasswordCancelled; #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) bool Fat32,NotFat32; #endif public: CmdExtract(CommandData *Cmd); ~CmdExtract(); void DoExtract(); void ExtractArchiveInit(Archive &Arc); bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat); static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); }; #endif unrar/filcreat.hpp000666 000000 000000 00000000612 13343205464 012664 0ustar00000000 000000 #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize=INT64NDF, RarTime *FileTime=NULL,bool WriteOnly=false); bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); #if defined(_WIN_ALL) bool UpdateExistingShortName(const wchar *Name); #endif #endif unrar/file.hpp000666 000000 000000 00000007731 13343205464 012023 0ustar00000000 000000 #ifndef _RAR_FILE_ #define _RAR_FILE_ #define FILE_USE_OPEN #ifdef _WIN_ALL typedef HANDLE FileHandle; #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE #elif defined(FILE_USE_OPEN) typedef off_t FileHandle; #define FILE_BAD_HANDLE -1 #else typedef FILE* FileHandle; #define FILE_BAD_HANDLE NULL #endif class RAROptions; enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; enum FILE_MODE_FLAGS { // Request read only access to file. Default for Open. FMF_READ=0, // Request both read and write access to file. Default for Create. FMF_UPDATE=1, // Request write only access to file. FMF_WRITE=2, // Open files which are already opened for write by other programs. FMF_OPENSHARED=4, // Open files only if no other program is opened it even in shared mode. FMF_OPENEXCLUSIVE=8, // Provide read access to created file for other programs. FMF_SHAREREAD=16, // Use standard NTFS names without trailing dots and spaces. FMF_STANDARDNAMES=32, // Mode flags are not defined yet. FMF_UNDEFINED=256 }; class File { private: FileHandle hFile; bool LastWrite; FILE_HANDLETYPE HandleType; bool SkipClose; bool IgnoreReadErrors; bool NewFile; bool AllowDelete; bool AllowExceptions; #ifdef _WIN_ALL bool NoSequentialRead; uint CreateMode; #endif protected: bool OpenShared; // Set by 'Archive' class. public: wchar FileName[NM]; FILE_ERRORTYPE ErrorType; public: File(); virtual ~File(); void operator = (File &SrcFile); // Several functions below are 'virtual', because they are redefined // by Archive for QOpen and by MultiFile for split files in WinRAR. virtual bool Open(const wchar *Name,uint Mode=FMF_READ); void TOpen(const wchar *Name); bool WOpen(const wchar *Name); bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); virtual bool Close(); // 'virtual' for MultiFile class. bool Delete(); bool Rename(const wchar *NewName); bool Write(const void *Data,size_t Size); virtual int Read(void *Data,size_t Size); int DirectRead(void *Data,size_t Size); virtual void Seek(int64 Offset,int Method); bool RawSeek(int64 Offset,int Method); virtual int64 Tell(); void Prealloc(int64 Size); byte GetByte(); void PutByte(byte Byte); bool Truncate(); void Flush(); void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); void GetOpenFileTime(RarTime *ft); virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;}; // 'virtual' for MultiFile class. int64 FileLength(); void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} FILE_HANDLETYPE GetHandleType() {return HandleType;} bool IsDevice(); static bool RemoveCreated(); FileHandle GetHandle() {return hFile;} void SetHandle(FileHandle Handle) {Close();hFile=Handle;} void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;} int64 Copy(File &Dest,int64 Length=INT64NDF); void SetAllowDelete(bool Allow) {AllowDelete=Allow;} void SetExceptions(bool Allow) {AllowExceptions=Allow;} #ifdef _WIN_ALL void RemoveSequentialFlag() {NoSequentialRead=true;} #endif #ifdef _UNIX int GetFD() { #ifdef FILE_USE_OPEN return hFile; #else return fileno(hFile); #endif } #endif static size_t CopyBufferSize() { #ifdef _WIN_ALL // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it. // For copying from HDD to same HDD the best performance was with 256 KB // buffer in XP and with 1 MB buffer in Win10. return WinNT()==WNT_WXP ? 0x40000:0x100000; #else return 0x100000; #endif } }; #endif unrar/filefn.hpp000666 000000 000000 00000002614 13343205464 012342 0ustar00000000 000000 #ifndef _RAR_FILEFN_ #define _RAR_FILEFN_ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); bool CreatePath(const wchar *Path,bool SkipLastName); void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); bool IsRemovable(const wchar *Name); #ifndef SFX_MODULE int64 GetFreeDisk(const wchar *Name); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) bool IsFAT(const wchar *Root); #endif bool FileExist(const wchar *Name); bool WildFileExist(const wchar *Name); bool IsDir(uint Attr); bool IsUnreadable(uint Attr); bool IsLink(uint Attr); void SetSFXMode(const wchar *FileName); void EraseDiskContents(const wchar *FileName); bool IsDeleteAllowed(uint FileAttr); void PrepareToDelete(const wchar *Name); uint GetFileAttr(const wchar *Name); bool SetFileAttr(const wchar *Name,uint Attr); #if 0 wchar* MkTemp(wchar *Name,size_t MaxSize); #endif enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); bool RenameFile(const wchar *SrcName,const wchar *DestName); bool DelFile(const wchar *Name); bool DelDir(const wchar *Name); #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool SetFileCompression(const wchar *Name,bool State); #endif #endif unrar/filestr.hpp000666 000000 000000 00000000522 13343205464 012543 0ustar00000000 000000 #ifndef _RAR_FILESTR_ #define _RAR_FILESTR_ bool ReadTextFile( const wchar *Name, StringList *List, bool Config, bool AbortOnError=false, RAR_CHARSET SrcCharset=RCH_DEFAULT, bool Unquote=false, bool SkipComments=false, bool ExpandEnvStr=false ); RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize); #endif unrar/find.hpp000666 000000 000000 00000001623 13343205464 012016 0ustar00000000 000000 #ifndef _RAR_FINDDATA_ #define _RAR_FINDDATA_ enum FINDDATA_FLAGS { FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. }; struct FindData { wchar Name[NM]; uint64 Size; uint FileAttr; bool IsDir; bool IsLink; RarTime mtime; RarTime ctime; RarTime atime; #ifdef _WIN_ALL FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; #endif uint Flags; bool Error; }; class FindFile { private: #ifdef _WIN_ALL static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); #endif wchar FindMask[NM]; bool FirstCall; #ifdef _WIN_ALL HANDLE hFind; #else DIR *dirp; #endif public: FindFile(); ~FindFile(); void SetMask(const wchar *Mask); bool Next(FindData *fd,bool GetSymLink=false); static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); }; #endif unrar/getbits.hpp000666 000000 000000 00000003262 13343205464 012540 0ustar00000000 000000 #ifndef _RAR_GETBITS_ #define _RAR_GETBITS_ class BitInput { public: enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. int InAddr; // Curent byte position in the buffer. int InBit; // Current bit position in the current byte. bool ExternalBuffer; public: BitInput(bool AllocBuffer); ~BitInput(); byte *InBuf; // Dynamically allocated input buffer. void InitBitInput() { InAddr=InBit=0; } // Move forward by 'Bits' bits. void addbits(uint Bits) { Bits+=InBit; InAddr+=Bits>>3; InBit=Bits&7; } // Return 16 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. uint getbits() { uint BitField=(uint)InBuf[InAddr] << 16; BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+2]; BitField >>= (8-InBit); return BitField & 0xffff; } // Return 32 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. uint getbits32() { uint BitField=(uint)InBuf[InAddr] << 24; BitField|=(uint)InBuf[InAddr+1] << 16; BitField|=(uint)InBuf[InAddr+2] << 8; BitField|=(uint)InBuf[InAddr+3]; BitField <<= InBit; BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); return BitField & 0xffffffff; } void faddbits(uint Bits); uint fgetbits(); // Check if buffer has enough space for IncPtr bytes. Returns 'true' // if buffer will be overflown. bool Overflow(uint IncPtr) { return InAddr+IncPtr>=MAX_SIZE; } void SetExternalBuffer(byte *Buf); }; #endif unrar/global.hpp000666 000000 000000 00000000241 13343205464 012331 0ustar00000000 000000 #ifndef _RAR_GLOBAL_ #define _RAR_GLOBAL_ #ifdef INCLUDEGLOBAL #define EXTVAR #else #define EXTVAR extern #endif EXTVAR ErrorHandler ErrHandler; #endif unrar/hash.hpp000666 000000 000000 00000001747 13343205464 012030 0ustar00000000 000000 #ifndef _RAR_DATAHASH_ #define _RAR_DATAHASH_ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; struct HashValue { void Init(HASH_TYPE Type); bool operator == (const HashValue &cmp); bool operator != (const HashValue &cmp) {return !(*this==cmp);} HASH_TYPE Type; union { uint CRC32; byte Digest[SHA256_DIGEST_SIZE]; }; }; #ifdef RAR_SMP class ThreadPool; class DataHash; #endif class DataHash { private: HASH_TYPE HashType; uint CurCRC32; blake2sp_state *blake2ctx; #ifdef RAR_SMP ThreadPool *ThPool; uint MaxThreads; // Upper limit for maximum threads to prevent wasting threads in pool. static const uint MaxHashThreads=8; #endif public: DataHash(); ~DataHash(); void Init(HASH_TYPE Type,uint MaxThreads); void Update(const void *Data,size_t DataSize); void Result(HashValue *Result); uint GetCRC32(); bool Cmp(HashValue *CmpValue,byte *Key); HASH_TYPE Type() {return HashType;} }; #endif unrar/headers.hpp000666 000000 000000 00000020717 13343205464 012516 0ustar00000000 000000 #ifndef _RAR_HEADERS_ #define _RAR_HEADERS_ #define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header. #define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header. #define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. #define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. #define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. #define SIZEOF_SHORTBLOCKHEAD 7 #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 #define SIZEOF_PROTECTHEAD 26 #define SIZEOF_UOHEAD 18 #define SIZEOF_STREAMHEAD 26 #define VER_PACK 29 #define VER_PACK5 50 // It is stored as 0, but we subtract 50 when saving an archive. #define VER_UNPACK 29 #define VER_UNPACK5 50 // It is stored as 0, but we add 50 when reading an archive. #define MHD_VOLUME 0x0001U // Old style main archive comment embed into main archive header. Must not // be used in new archives anymore. #define MHD_COMMENT 0x0002U #define MHD_LOCK 0x0004U #define MHD_SOLID 0x0008U #define MHD_PACK_COMMENT 0x0010U #define MHD_NEWNUMBERING 0x0010U #define MHD_AV 0x0020U #define MHD_PROTECT 0x0040U #define MHD_PASSWORD 0x0080U #define MHD_FIRSTVOLUME 0x0100U #define LHD_SPLIT_BEFORE 0x0001U #define LHD_SPLIT_AFTER 0x0002U #define LHD_PASSWORD 0x0004U // Old style file comment embed into file header. Must not be used // in new archives anymore. #define LHD_COMMENT 0x0008U // For non-file subheaders it denotes 'subblock having a parent file' flag. #define LHD_SOLID 0x0010U #define LHD_WINDOWMASK 0x00e0U #define LHD_WINDOW64 0x0000U #define LHD_WINDOW128 0x0020U #define LHD_WINDOW256 0x0040U #define LHD_WINDOW512 0x0060U #define LHD_WINDOW1024 0x0080U #define LHD_WINDOW2048 0x00a0U #define LHD_WINDOW4096 0x00c0U #define LHD_DIRECTORY 0x00e0U #define LHD_LARGE 0x0100U #define LHD_UNICODE 0x0200U #define LHD_SALT 0x0400U #define LHD_VERSION 0x0800U #define LHD_EXTTIME 0x1000U #define SKIP_IF_UNKNOWN 0x4000U #define LONG_BLOCK 0x8000U #define EARC_NEXT_VOLUME 0x0001U // Not last volume. #define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). #define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. #define EARC_VOLNUMBER 0x0008U // Store a number of current volume. enum HEADER_TYPE { // RAR 5.0 header types. HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03, HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff, // RAR 1.5 - 4.x header types. HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75, HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79, HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b }; // RAR 2.9 and earlier. enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; // Internal implementation, depends on archive format version. enum HOST_SYSTEM { // RAR 5.0 host OS HOST5_WINDOWS=0,HOST5_UNIX=1, // RAR 3.0 host OS. HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, HOST_BEOS=5,HOST_MAX }; // Unified archive format independent implementation. enum HOST_SYSTEM_TYPE { HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN }; // We also use these values in extra field, so do not modify them. enum FILE_SYSTEM_REDIRECT { FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, FSREDIR_HARDLINK, FSREDIR_FILECOPY }; #define SUBHEAD_TYPE_CMT L"CMT" #define SUBHEAD_TYPE_QOPEN L"QO" #define SUBHEAD_TYPE_ACL L"ACL" #define SUBHEAD_TYPE_STREAM L"STM" #define SUBHEAD_TYPE_UOWNER L"UOW" #define SUBHEAD_TYPE_AV L"AV" #define SUBHEAD_TYPE_RR L"RR" #define SUBHEAD_TYPE_OS2EA L"EA2" /* new file inherits a subblock when updating a host file */ #define SUBHEAD_FLAGS_INHERITED 0x80000000 #define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 struct MarkHeader { byte Mark[8]; // Following fields are virtual and not present in real blocks. uint HeadSize; }; struct BaseBlock { uint HeadCRC; // 'ushort' for RAR 1.5. HEADER_TYPE HeaderType; // 1 byte for RAR 1.5. uint Flags; // 'ushort' for RAR 1.5. uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0. bool SkipIfUnknown; void Reset() { SkipIfUnknown=false; } }; struct BlockHeader:BaseBlock { uint DataSize; }; struct MainHeader:BaseBlock { ushort HighPosAV; uint PosAV; bool CommentInHeader; bool PackComment; // For RAR 1.4 archive format only. bool Locator; uint64 QOpenOffset; // Offset of quick list record. uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. uint64 RROffset; // Offset of recovery record. uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. void Reset(); }; struct FileHeader:BlockHeader { byte HostOS; byte UnpVer; byte Method; union { uint FileAttr; uint SubFlags; }; wchar FileName[NM]; Array SubData; RarTime mtime; RarTime ctime; RarTime atime; int64 PackSize; int64 UnpSize; int64 MaxSize; // Reserve size bytes for vint of this size. HashValue FileHash; uint FileFlags; bool SplitBefore; bool SplitAfter; bool UnknownUnpSize; bool Encrypted; CRYPT_METHOD CryptMethod; bool SaltSet; byte Salt[SIZE_SALT50]; byte InitV[SIZE_INITV]; bool UsePswCheck; byte PswCheck[SIZE_PSWCHECK]; // Use HMAC calculated from HashKey and checksum instead of plain checksum. bool UseHashKey; // Key to convert checksum to HMAC. Derived from password with PBKDF2 // using additional iterations. byte HashKey[SHA256_DIGEST_SIZE]; uint Lg2Count; // Log2 of PBKDF2 repetition count. bool Solid; bool Dir; bool CommentInHeader; // RAR 2.0 file comment. bool Version; // name.ext;ver file name containing the version number. size_t WinSize; bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. bool LargeFile; // 'true' for HEAD_SERVICE block, which is a child of preceding file block. // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. bool SubBlock; HOST_SYSTEM_TYPE HSType; FILE_SYSTEM_REDIRECT RedirType; wchar RedirName[NM]; bool DirTarget; bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; char UnixOwnerName[256],UnixGroupName[256]; #ifdef _UNIX uid_t UnixOwnerID; gid_t UnixGroupID; #else // Need these Unix fields in Windows too for 'list' command. uint UnixOwnerID; uint UnixGroupID; #endif void Reset(size_t SubDataSize=0); bool CmpName(const wchar *Name) { return(wcscmp(FileName,Name)==0); } FileHeader& operator = (FileHeader &hd); }; struct EndArcHeader:BaseBlock { // Optional CRC32 of entire archive up to start of EndArcHeader block. // Present in RAR 4.x archives if EARC_DATACRC flag is set. uint ArcDataCRC; uint VolNumber; // Optional number of current volume. // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. bool NextVolume; // Not last volume. bool DataCRC; bool RevSpace; bool StoreVolNumber; void Reset() { BaseBlock::Reset(); NextVolume=false; DataCRC=false; RevSpace=false; StoreVolNumber=false; } }; struct CryptHeader:BaseBlock { bool UsePswCheck; uint Lg2Count; // Log2 of PBKDF2 repetition count. byte Salt[SIZE_SALT50]; byte PswCheck[SIZE_PSWCHECK]; }; // SubBlockHeader and its successors were used in RAR 2.x format. // RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks. struct SubBlockHeader:BlockHeader { ushort SubType; byte Level; }; struct CommentHeader:BaseBlock { ushort UnpSize; byte UnpVer; byte Method; ushort CommCRC; }; struct ProtectHeader:BlockHeader { byte Version; ushort RecSectors; uint TotalBlocks; byte Mark[8]; }; struct UnixOwnersHeader:SubBlockHeader { ushort OwnerNameSize; ushort GroupNameSize; /* dummy */ char OwnerName[256]; char GroupName[256]; }; struct EAHeader:SubBlockHeader { uint UnpSize; byte UnpVer; byte Method; uint EACRC; }; struct StreamHeader:SubBlockHeader { uint UnpSize; byte UnpVer; byte Method; uint StreamCRC; ushort StreamNameSize; char StreamName[260]; }; #endif unrar/headers5.hpp000666 000000 000000 00000010222 13343205464 012571 0ustar00000000 000000 #ifndef _RAR_HEADERS5_ #define _RAR_HEADERS5_ #define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length. #define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size. // RAR 5.0 block flags common for all blocks. // Additional extra area is present in the end of block header. #define HFL_EXTRA 0x0001 // Additional data area is present in the end of block header. #define HFL_DATA 0x0002 // Unknown blocks with this flag must be skipped when updating an archive. #define HFL_SKIPIFUNKNOWN 0x0004 // Data area of this block is continuing from previous volume. #define HFL_SPLITBEFORE 0x0008 // Data area of this block is continuing in next volume. #define HFL_SPLITAFTER 0x0010 // Block depends on preceding file block. #define HFL_CHILD 0x0020 // Preserve a child block if host is modified. #define HFL_INHERITED 0x0040 // RAR 5.0 main archive header specific flags. #define MHFL_VOLUME 0x0001 // Volume. #define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first. #define MHFL_SOLID 0x0004 // Solid archive. #define MHFL_PROTECT 0x0008 // Recovery record is present. #define MHFL_LOCK 0x0010 // Locked archive. // RAR 5.0 file header specific flags. #define FHFL_DIRECTORY 0x0001 // Directory. #define FHFL_UTIME 0x0002 // Time field in Unix format is present. #define FHFL_CRC32 0x0004 // CRC32 field is present. #define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size. // RAR 5.0 end of archive header specific flags. #define EHFL_NEXTVOLUME 0x0001 // Not last volume. // RAR 5.0 archive encryption header specific flags. #define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present. // RAR 5.0 file compression flags. #define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. #define FCI_ALGO_BIT1 0x0002 // 0 .. 63. #define FCI_ALGO_BIT2 0x0004 #define FCI_ALGO_BIT3 0x0008 #define FCI_ALGO_BIT4 0x0010 #define FCI_ALGO_BIT5 0x0020 #define FCI_SOLID 0x0040 // Solid flag. #define FCI_METHOD_BIT0 0x0080 // Compression method. #define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). #define FCI_METHOD_BIT2 0x0200 #define FCI_DICT_BIT0 0x0400 // Dictionary size. #define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. #define FCI_DICT_BIT2 0x1000 #define FCI_DICT_BIT3 0x2000 // Main header extra field values. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. // Flags for MHEXTRA_LOCATOR. #define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. #define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. // File and service header extra field values. #define FHEXTRA_CRYPT 0x01 // Encryption parameters. #define FHEXTRA_HASH 0x02 // File hash. #define FHEXTRA_HTIME 0x03 // High precision file time. #define FHEXTRA_VERSION 0x04 // File version information. #define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.). #define FHEXTRA_UOWNER 0x06 // Unix owner and group information. #define FHEXTRA_SUBDATA 0x07 // Service header subdata array. // Hash type values for FHEXTRA_HASH. #define FHEXTRA_HASH_BLAKE2 0x00 // Flags for FHEXTRA_HTIME. #define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format. #define FHEXTRA_HTIME_MTIME 0x02 // mtime is present. #define FHEXTRA_HTIME_CTIME 0x04 // ctime is present. #define FHEXTRA_HTIME_ATIME 0x08 // atime is present. #define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision. // Flags for FHEXTRA_CRYPT. #define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data. #define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums. // Flags for FHEXTRA_REDIR. #define FHEXTRA_REDIR_DIR 0x01 // Link target is directory. // Flags for FHEXTRA_UOWNER. #define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present. #define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present. #define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present. #define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present. #endif unrar/isnt.hpp000666 000000 000000 00000000407 13343205464 012052 0ustar00000000 000000 #ifndef _RAR_ISNT_ #define _RAR_ISNT_ enum WINNT_VERSION { WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601, WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00 }; DWORD WinNT(); #endif unrar/list.hpp000666 000000 000000 00000000123 13343205464 012043 0ustar00000000 000000 #ifndef _RAR_LIST_ #define _RAR_LIST_ void ListArchive(CommandData *Cmd); #endif unrar/loclang.hpp000666 000000 000000 00000061066 13343205464 012524 0ustar00000000 000000 #define MYesNo L"_Yes_No" #define MYesNoAll L"_Yes_No_All" #define MYesNoAllQ L"_Yes_No_All_nEver_Quit" #define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit" #define MContinueQuit L"_Continue_Quit" #define MRetryAbort L"_Retry_Abort" #define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" #define MRegTo L"\nRegistered to %s\n" #define MShare L"\nTrial version Type 'rar -?' for help\n" #define MRegKeyWarning L"\nAvailable license key is valid only for %s\n" #define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n" #define MBeta L"beta" #define Mx86 L"x86" #define Mx64 L"x64" #define MMonthJan L"Jan" #define MMonthFeb L"Feb" #define MMonthMar L"Mar" #define MMonthApr L"Apr" #define MMonthMay L"May" #define MMonthJun L"Jun" #define MMonthJul L"Jul" #define MMonthAug L"Aug" #define MMonthSep L"Sep" #define MMonthOct L"Oct" #define MMonthNov L"Nov" #define MMonthDec L"Dec" #define MRARTitle1 L"\nUsage: rar - - " #define MUNRARTitle1 L"\nUsage: unrar - - " #define MRARTitle2 L"\n <@listfiles...> " #define MCHelpCmd L"\n\n" #define MCHelpCmdA L"\n a Add files to archive" #define MCHelpCmdC L"\n c Add archive comment" #define MCHelpCmdCH L"\n ch Change archive parameters" #define MCHelpCmdCW L"\n cw Write archive comment to file" #define MCHelpCmdD L"\n d Delete files from archive" #define MCHelpCmdE L"\n e Extract files without archived paths" #define MCHelpCmdF L"\n f Freshen files in archive" #define MCHelpCmdI L"\n i[par]= Find string in archives" #define MCHelpCmdK L"\n k Lock archive" #define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]" #define MCHelpCmdM L"\n m[f] Move to archive [files only]" #define MCHelpCmdP L"\n p Print file to stdout" #define MCHelpCmdR L"\n r Repair archive" #define MCHelpCmdRC L"\n rc Reconstruct missing volumes" #define MCHelpCmdRN L"\n rn Rename archived files" #define MCHelpCmdRR L"\n rr[N] Add data recovery record" #define MCHelpCmdRV L"\n rv[N] Create recovery volumes" #define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX" #define MCHelpCmdT L"\n t Test archive files" #define MCHelpCmdU L"\n u Update files in archive" #define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]" #define MCHelpCmdX L"\n x Extract files with full path" #define MCHelpSw L"\n\n" #define MCHelpSwm L"\n - Stop switches scanning" #define MCHelpSwAT L"\n @[+] Disable [enable] file lists" #define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction" #define MCHelpSwAD L"\n ad Append archive name to destination path" #define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" #define MCHelpSwAI L"\n ai Ignore file attributes" #define MCHelpSwAO L"\n ao Add files with Archive attribute set" #define MCHelpSwAP L"\n ap Set path inside archive" #define MCHelpSwAS L"\n as Synchronize archive contents" #define MCHelpSwCm L"\n c- Disable comments show" #define MCHelpSwCFGm L"\n cfg- Disable read configuration" #define MCHelpSwCL L"\n cl Convert names to lower case" #define MCHelpSwCU L"\n cu Convert names to upper case" #define MCHelpSwDF L"\n df Delete files after archiving" #define MCHelpSwDH L"\n dh Open shared files" #define MCHelpSwDR L"\n dr Delete files to Recycle Bin" #define MCHelpSwDS L"\n ds Disable name sort for solid archive" #define MCHelpSwDW L"\n dw Wipe files after archiving" #define MCHelpSwEa L"\n e[+] Set file exclude and include attributes" #define MCHelpSwED L"\n ed Do not add empty directories" #define MCHelpSwEN L"\n en Do not put 'end of archive' block" #define MCHelpSwEP L"\n ep Exclude paths from names" #define MCHelpSwEP1 L"\n ep1 Exclude base directory from names" #define MCHelpSwEP2 L"\n ep2 Expand paths to full" #define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter" #define MCHelpSwF L"\n f Freshen files" #define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" #define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" #define MCHelpSwIDP L"\n id[c,d,p,q] Disable messages" #define MCHelpSwIEML L"\n ieml[addr] Send archive by email" #define MCHelpSwIERR L"\n ierr Send all messages to stderr" #define MCHelpSwILOG L"\n ilog[name] Log errors to file" #define MCHelpSwINUL L"\n inul Disable all messages" #define MCHelpSwIOFF L"\n ioff[n] Turn PC off after completing an operation" #define MCHelpSwISND L"\n isnd Enable sound" #define MCHelpSwIVER L"\n iver Display the version number" #define MCHelpSwK L"\n k Lock archive" #define MCHelpSwKB L"\n kb Keep broken extracted files" #define MCHelpSwLog L"\n log[f][=name] Write names to log file" #define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)" #define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" #define MCHelpSwMC L"\n mc Set advanced compression parameters" #define MCHelpSwMD L"\n md[k,m,g] Dictionary size in KB, MB or GB" #define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store" #define MCHelpSwMT L"\n mt Set the number of threads" #define MCHelpSwN L"\n n Additionally filter included files" #define MCHelpSwNa L"\n n@ Read additional filter masks from stdin" #define MCHelpSwNal L"\n n@ Read additional filter masks from list file" #define MCHelpSwO L"\n o[+|-] Set the overwrite mode" #define MCHelpSwOC L"\n oc Set NTFS Compressed attribute" #define MCHelpSwOH L"\n oh Save hard links as the link instead of the file" #define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references" #define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]" #define MCHelpSwONI L"\n oni Allow potentially incompatible names" #define MCHelpSwOR L"\n or Rename files automatically" #define MCHelpSwOS L"\n os Save NTFS streams" #define MCHelpSwOW L"\n ow Save or restore file owner and group" #define MCHelpSwP L"\n p[password] Set password" #define MCHelpSwPm L"\n p- Do not query password" #define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]" #define MCHelpSwR L"\n r Recurse subdirectories" #define MCHelpSwRm L"\n r- Disable recursion" #define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only" #define MCHelpSwRI L"\n ri

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" #define MCHelpSwRR L"\n rr[N] Add data recovery record" #define MCHelpSwRV L"\n rv[N] Create recovery volumes" #define MCHelpSwS L"\n s[,v[-],e] Create solid archive" #define MCHelpSwSm L"\n s- Disable solid archiving" #define MCHelpSwSC L"\n sc[obj] Specify the character set" #define MCHelpSwSFX L"\n sfx[name] Create SFX archive" #define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" #define MCHelpSwSL L"\n sl Process files with size less than specified" #define MCHelpSwSM L"\n sm Process files with size more than specified" #define MCHelpSwT L"\n t Test files after archiving" #define MCHelpSwTK L"\n tk Keep original archive time" #define MCHelpSwTL L"\n tl Set archive time to latest file" #define MCHelpSwTN L"\n tn