unrar/arccmt.cpp000666 000000 000000 00000011453 15026203744 012343 0ustar00000000 000000 static bool IsAnsiEscComment(const wchar *Data,size_t Size); bool Archive::GetComment(std::wstring &CmtData) { if (!MainComment) return false; int64 SavePos=Tell(); bool Success=DoGetComment(CmtData); Seek(SavePos,SEEK_SET); return Success; } bool Archive::DoGetComment(std::wstring &CmtData) { #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); if (SearchSubBlock(SUBHEAD_TYPE_CMT)!=0) if (ReadCommentData(CmtData)) return true; else uiMsg(UIERROR_CMTBROKEN,FileName); return false; } #ifndef SFX_MODULE // Old style (RAR 2.9) comment header embedded into the main // archive header. if (BrokenHeader || CommHead.HeadSize 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); if (CmtLength<2) return false; 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); if (UnpDataSize>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. OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); #endif std::string UnpStr((char*)UnpData,UnpDataSize); CharToWide(UnpStr,CmtData); } } } else { if (CmtLength==0) return false; std::vector CmtRaw(CmtLength); int ReadSize=Read(CmtRaw.data(),CmtLength); if (ReadSize>=0 && (uint)ReadSizeresize(wcslen(CmtData->data())); } #endif return CmtData.size() > 0; } bool Archive::ReadCommentData(std::wstring &CmtData) { std::vector CmtRaw; if (!ReadSubData(&CmtRaw,NULL,false)) return false; size_t CmtSize=CmtRaw.size(); CmtRaw.push_back(0); // CmtData->resize(CmtSize+1); if (Format==RARFMT50) UtfToWide((char *)CmtRaw.data(),CmtData); else if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) { CmtData=RawToWide(CmtRaw); } else { CharToWide((const char *)CmtRaw.data(),CmtData); } // CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length. return true; } void Archive::ViewComment() { if (Cmd->DisableComment) return; std::wstring CmtBuf; if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments. { size_t CmtSize=CmtBuf.size(); auto EndPos=CmtBuf.find(0x1A); if (EndPos!=std::wstring::npos) CmtSize=EndPos; mprintf(St(MArcComment)); mprintf(L":\n"); OutComment(CmtBuf); } } unrar/archive.cpp000666 000000 000000 00000021077 15026203744 012516 0ustar00000000 000000 #include "rar.hpp" #include "arccmt.cpp" Archive::Archive(CommandData *InitCmd) { Cmd=NULL; // Just in case we'll have an exception in 'new' below. DummyCmd=(InitCmd==NULL); Cmd=DummyCmd ? (new CommandData):InitCmd; OpenShared=Cmd->OpenShared; Format=RARFMT_NONE; 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; CurHeaderType=HEAD_UNKNOWN; CurBlockPos=0; NextBlockPos=0; RecoveryPercent=-1; MainHead.Reset(); CryptHead={}; EndArcHead.Reset(); VolNumber=0; VolWrite=0; AddingFilesSize=0; AddingHeadersSize=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_BADARC); } } #if !defined(SFX_MODULE) void Archive::CheckOpen(const std::wstring &Name) { TOpen(Name); CheckArc(false); } #endif bool Archive::WCheckOpen(const std::wstring &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. #ifndef SFX_MODULE if (D[6]==0) Type=RARFMT15; else #endif 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 { std::vector Buffer(MAXSFXSIZE); long CurPos=(long)Tell(); int ReadSize=Read(Buffer.data(),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; } // 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) && IsSeekable()) { int64 SavePos=Tell(); 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; Seek(SavePos,SEEK_SET); } if (!Volume || FirstVolume) FirstVolumeName=FileName; 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 std::wstring &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 // Return 0 if dictionary size is invalid. If size is RAR7 only, return // the adjusted nearest bottom value. Return header flags in Flags. uint64 Archive::GetWinSize(uint64 Size,uint &Flags) { Flags=0; // Allow 128 KB - 1 TB range. if (Size<0x20000 || Size>0x10000000000ULL) return 0; uint64 Pow2=0x20000; // Power of 2 dictionary size. for (;2*Pow2<=Size;Pow2*=2) Flags+=FCI_DICT_BIT0; if (Size==Pow2) return Size; // If 'Size' is the power of 2, return it as is. // Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'. uint64 Fraction=(Size-Pow2)/(Pow2/32); Flags+=(uint)Fraction*FCI_DICT_FRACT0; return Pow2+Fraction*(Pow2/32); } unrar/arcread.cpp000666 000000 000000 00000140503 15026203744 012472 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(); // Other developers asked us to initialize it to suppress "may be used // uninitialized" warning in code below in some compilers. size_t ReadSize=0; switch(Format) { #ifndef SFX_MODULE case RARFMT14: ReadSize=ReadHeader14(); break; case RARFMT15: ReadSize=ReadHeader15(); break; #endif 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. // We can't replace this check by checking that read size is exactly 0 // in the beginning of file header, because in this case the read position // still can be beyond the end of archive. if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) { uiMsg(UIERROR_UNEXPEOF,FileName); if (CurHeaderType!=HEAD_FILE && CurHeaderType!=HEAD_UNKNOWN) uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName); ErrHandler.SetErrorCode(RARX_WARNING); } } void Archive::BrokenHeaderMsg() { uiMsg(UIERROR_HEADERBROKEN,FileName); BrokenHeader=true; ErrHandler.SetErrorCode(RARX_CRC); } void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info) { uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); ErrHandler.SetErrorCode(RARX_FATAL); } // 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; } #ifndef SFX_MODULE size_t Archive::ReadHeader15() { RawRead Raw(this); bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; if (Decrypt) { #ifdef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll. return 0; #else RequestArcPassword(NULL); 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(); hd->SetBaseBlock(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; // RAR versions earlier than 2.0 do not set the solid flag // in file header. They use only a global solid archive flag. hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; hd->Version=(hd->Flags & LHD_VERSION)!=0; hd->DataSize=Raw.Get4(); uint LowUnpSize=Raw.Get4(); hd->HostOS=Raw.Get1(); hd->FileHash.Type=HASH_CRC32; hd->FileHash.CRC32=Raw.Get4(); uint FileTime=Raw.Get4(); hd->UnpVer=Raw.Get1(); hd->Method=Raw.Get1()-0x30; size_t NameSize=Raw.Get2(); hd->FileAttr=Raw.Get4(); // RAR15 did not use the special dictionary size to mark dirs. if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) hd->Dir=true; 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.clear(); } 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; size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); std::string FileName(ReadNameSize,0); Raw.GetB((byte *)&FileName[0],ReadNameSize); if (FileBlock) { hd->FileName.clear(); if ((hd->Flags & LHD_UNICODE)!=0) { EncodeFileName NameCoder; size_t Length=strlen(FileName.data()); Length++; if (ReadNameSize>Length) NameCoder.Decode(FileName.data(),ReadNameSize, (byte *)&FileName[Length], ReadNameSize-Length,hd->FileName); } if (hd->FileName.empty()) ArcCharToWide(FileName.data(),hd->FileName,ACTW_OEM); #ifndef SFX_MODULE ConvertNameCase(hd->FileName); #endif ConvertFileHeader(hd); } else { CharToWide(FileName.data(),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.resize(DataSize); Raw.GetB(hd->SubData.data(),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; uint count=rmode&3; for (uint 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; uint 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: EndArcHead.SetBaseBlock(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: CommHead.SetBaseBlock(ShortBlock); CommHead.UnpSize=Raw.Get2(); CommHead.UnpVer=Raw.Get1(); CommHead.Method=Raw.Get1(); CommHead.CommCRC=Raw.Get2(); break; case HEAD3_PROTECT: ProtectHead.SetBaseBlock(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. SubBlockHead.SetBaseBlock(ShortBlock); SubBlockHead.DataSize=Raw.Get4(); NextBlockPos+=SubBlockHead.DataSize; SubBlockHead.SubType=Raw.Get2(); SubBlockHead.Level=Raw.Get1(); switch(SubBlockHead.SubType) { 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(); const size_t MaxStreamName20=260; // Maximum allowed stream name in RAR 2.x format. if (StreamHead.StreamNameSize>MaxStreamName20) StreamHead.StreamNameSize=MaxStreamName20; StreamHead.StreamName.resize(StreamHead.StreamNameSize); Raw.GetB(&StreamHead.StreamName[0],StreamHead.StreamNameSize); break; } break; #endif default: if (ShortBlock.Flags & LONG_BLOCK) NextBlockPos+=Raw.Get4(); break; } uint HeaderCRC=Raw.GetCRC15(false); // Old AV header does not have header CRC properly set. // Old Unix owners header didn't include string fields into header size, // but included them into CRC, so it couldn't be verified with generic // approach here. if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && ShortBlock.HeaderType!=HEAD3_AV && (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD)) { 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. 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(); } #endif // #ifndef SFX_MODULE size_t Archive::ReadHeader50() { RawRead Raw(this); bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; if (Decrypt) { #if defined(RAR_NOCRYPT) return 0; #else if (Cmd->SkipEncrypted) { uiMsg(UIMSG_SKIPENCARC,FileName); FailedHeaderDecryption=true; // Suppress error messages and quit quietly. return 0; } 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() || uiIsGlobalPasswordSet(); RarCheckPassword CheckPwd; if (CryptHead.UsePswCheck && !BrokenHeader) CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck); while (true) // Repeat the password prompt for wrong passwords. { RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); byte PswCheck[SIZE_PSWCHECK]; bool EncSet=HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); // Verify password validity. If header is damaged, we cannot rely on // password check value, because it can be damaged too. if (EncSet && CryptHead.UsePswCheck && !BrokenHeader && 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,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,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: { CryptHead.SetBaseBlock(ShortBlock); uint CryptVersion=(uint)Raw.GetV(); if (CryptVersion>CRYPT_VERSION) { UnkEncVerMsg(FileName,L"h" + std::to_wstring(CryptVersion)); FailedHeaderDecryption=true; 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,L"hc" + std::to_wstring(CryptHead.Lg2Count)); FailedHeaderDecryption=true; 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); // Exclude this code for rarext.dll, Setup.SFX and unrar_nocrypt.dll linked // without sha256. But still set Encrypted=true for rarext.dll here, // so it can recognize encrypted header archives in archive properties. #ifndef RAR_NOCRYPT byte Digest[SHA256_DIGEST_SIZE]; sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest); CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; #endif } Encrypted=true; } break; case HEAD_MAIN: { MainHead.Reset(); MainHead.SetBaseBlock(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(); // Clear hash, time fields and other stuff like flags. *(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. uint UnpVer=(CompInfo & 0x3f); if (UnpVer==0) hd->UnpVer=VER_PACK5; else if (UnpVer==1) hd->UnpVer=VER_PACK7; else hd->UnpVer=VER_UNKNOWN; 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; if (hd->Dir || UnpVer>1) hd->WinSize=0; else { hd->WinSize=0x20000ULL<<((CompInfo>>10)&(UnpVer==0 ? 0x0f:0x1f)); if (UnpVer==1) { hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f); // RAR7 header with RAR5 compression. Needed to append RAR7 files // to RAR5 solid stream if new dictionary is larger than existing. if ((CompInfo & FCI_RAR5_COMPAT)!=0) hd->UnpVer=VER_PACK5; if (hd->WinSize>UNPACK_MAX_DICT) hd->UnpVer=VER_UNKNOWN; } } size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); std::string FileName(ReadNameSize,0); Raw.GetB((byte *)&FileName[0],ReadNameSize); UtfToWide(FileName.data(),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; // For RAR5 format we read the user specified recovery percent here. if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.size()>0) { // It is stored as a single byte up to RAR 6.02 and as vint since // 6.10, where we extended the maximum RR size from 99% to 1000%. RawRead RawPercent; RawPercent.Read(hd->SubData.data(),hd->SubData.size()); RecoveryPercent=(int)RawPercent.GetV(); } if (BadCRC) // Add the file name to broken header message displayed above. uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } break; case HEAD_ENDARC: { EndArcHead.SetBaseBlock(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(RarCheckPassword *CheckPwd) { 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; CharToWide(PasswordA,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,CheckPwd)) { 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,const 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; switch(FieldType) { case 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; } } break; case MHEXTRA_METADATA: { uint Flags=(uint)Raw->GetV(); if ((Flags & MHEXTRA_METADATA_NAME)!=0) { uint64 NameSize=Raw->GetV(); if (NameSize>0 && NameSizeGetB(&NameU[0],(size_t)NameSize); // If starts from 0, the name was longer than reserved space // when saving this extra field. if (NameU[0]!=0) UtfToWide(&NameU[0],hd->OrigName); } } if ((Flags & MHEXTRA_METADATA_CTIME)!=0) if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0) if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0) hd->OrigTime.SetUnixNS(Raw->Get8()); else hd->OrigTime.SetUnix((time_t)Raw->Get4()); else hd->OrigTime.SetWin(Raw->Get8()); } break; } } if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) { FileHeader *hd=(FileHeader *)bb; switch(FieldType) { #ifndef RAR_NOCRYPT // Except rarext.dll, Setup.SFX and unrar_nocrypt.dll. case FHEXTRA_CRYPT: { FileHeader *hd=(FileHeader *)bb; uint EncVersion=(uint)Raw->GetV(); if (EncVersion>CRYPT_VERSION) { UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion)); hd->CryptMethod=CRYPT_UNKNOWN; } else { uint Flags=(uint)Raw->GetV(); hd->Lg2Count=Raw->Get1(); if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) { UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count)); hd->CryptMethod=CRYPT_UNKNOWN; } else { hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; 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); byte Digest[SHA256_DIGEST_SIZE]; sha256_get(hd->PswCheck, SIZE_PSWCHECK, 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; #endif 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; hd->FileName += L';' + std::to_wstring(Version); } } break; case FHEXTRA_REDIR: { FILE_SYSTEM_REDIRECT RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); uint Flags=(uint)Raw->GetV(); size_t NameSize=(size_t)Raw->GetV(); if (NameSize>0 && NameSizeRedirType=RedirType; hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; Raw->GetB(&UtfName[0],NameSize); UtfToWide(&UtfName[0],hd->RedirName); #ifdef _WIN_ALL UnixSlashToDos(hd->RedirName,hd->RedirName); #endif } } 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.resize((size_t)FieldSize); Raw->GetB(hd->SubData.data(),(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 0; 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 0; 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); // RAR 1.4 name size is stored in a single byte field and it can't // exceed 255, so additional checks are not needed. std::string FileName(NameSize,0); Raw.GetB((byte *)&FileName[0],NameSize); std::string NameA; OemToExt(FileName,NameA); CharToWide(NameA,FileHead.FileName); ConvertNameCase(FileHead.FileName); ConvertFileHeader(&FileHead); 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(std::wstring &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() { #ifdef _WIN_ALL 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); #endif for (uint I=0;IFileName.size();I++) { wchar *s=&hd->FileName[I]; #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 #ifdef _WIN_ALL // 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; } // Zeroes inside might be possible in broken Unicode names decoded with EncodeFileName::Decode. TruncateAtZero(hd->FileName); // Ensure there are no zeroes inside of string. } 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(std::vector *UnpData,File *DestFile,bool TestMode) { if (BrokenHeader) { uiMsg(UIERROR_SUBHEADERBROKEN,FileName); ErrHandler.SetErrorCode(RARX_CRC); return false; } if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK7: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) { // Prevent the excessive allocation. When reading to memory, normally // this function operates with reasonably small blocks, such as // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream. uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); return false; } if (UnpData==NULL) SubDataIO.SetTestMode(true); else { UnpData->resize((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.SetTestMode(TestMode); 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->clear(); return false; } return true; } unrar/blake2s.cpp000666 000000 000000 00000011440 15026203744 012411 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves #include "rar.hpp" 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 } , }; #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 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 if (_SSE_Version>=SSE_SSSE3) 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 00000007533 15026203744 012601 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( uint32 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 // We tried to _forceinline blake2s_update and blake2s_compress_sse, // but it didn't improve performance. 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 00000010555 15026203744 013271 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves // Initialization vector. static __m128i blake2s_IV_0_3, blake2s_IV_4_7; // Constants for cyclic rotation. static __m128i crotr8, crotr16; #ifdef __GNUC__ __attribute__((target("sse2"))) #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 ); 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 ); } #define LOAD(p) _mm_load_si128( (__m128i *)(p) ) #define STORE(p,r) _mm_store_si128((__m128i *)(p), r) #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 )) ) #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) ); // 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]); \ } #ifdef __GNUC__ __attribute__((target("ssse3"))) #endif 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 00000077350 15026203744 012477 0ustar00000000 000000 #include "rar.hpp" #include "cmdfilter.cpp" #include "cmdmix.cpp" CommandData::CommandData() { Init(); } void CommandData::Init() { RAROptions::Init(); Command.clear(); ArcName.clear(); ExtrPath.clear(); TempPath.clear(); SFXModule.clear(); CommentFile.clear(); ArcPath.clear(); ExclArcPath.clear(); LogName.clear(); EmailTo.clear(); UseStdin.clear(); FileLists=false; NoMoreSwitches=false; ListMode=RCLM_AUTO; BareOutput=false; FileArgs.Reset(); ExclArgs.Reset(); InclArgs.Reset(); ArcNames.Reset(); StoreArgs.Reset(); #ifdef PROPAGATE_MOTW MotwList.Reset(); #endif Password.Clean(); NextVolSizes.clear(); #ifdef RARDLL DllDestName.clear(); #endif } #if !defined(SFX_MODULE) void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) { Command.clear(); 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. std::wstring CmdLine=GetCommandLine(); std::wstring Param; std::wstring::size_type Pos=0; for (bool FirstParam=true;;FirstParam=false) { if (!GetCmdParam(CmdLine,Pos,Param)) break; if (!FirstParam) // First parameter is the executable name. if (Preprocess) PreprocessArg(Param.c_str()); else ParseArg(Param.c_str()); } #else for (int I=1;I0 && Arg[L-1]=='.' && (L==1 || L>=2 && (IsPathDiv(Arg[L-2]) || Arg[L-2]=='.' && (L==2 || L>=3 && IsPathDiv(Arg[L-3]))))) FolderArg=true; wchar CmdChar=toupperw(Command[0]); bool Add=wcschr(L"AFUM",CmdChar)!=NULL; bool Extract=CmdChar=='X' || CmdChar=='E'; bool Repair=CmdChar=='R' && Command[1]==0; if (FolderArg && !Add) ExtrPath=Arg; else if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS)) FileArgs.AddString(Arg); else { FindData FileData; bool Found=FindFile::FastFind(Arg,&FileData); if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1)) { FileLists=true; ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true); } else // We use 'destpath\' when extracting and reparing. if (Found && FileData.IsDir && (Extract || Repair) && ExtrPath.empty()) { ExtrPath=Arg; AddEndSlash(ExtrPath); } else FileArgs.AddString(Arg); } } } #endif void CommandData::ParseDone() { if (FileArgs.ItemsCount()==0 && !FileLists) FileArgs.AddString(MASKALL); wchar CmdChar=toupperw(Command[0]); bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P'; if (Test && Extract) Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands. // Suppress the copyright message and final end of line for 'lb' and 'vb'. if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B') BareOutput=true; } #if !defined(SFX_MODULE) void CommandData::ParseEnvVar() { char *EnvVar=getenv("RARINISWITCHES"); if (EnvVar!=NULL) { std::wstring EnvStr; CharToWide(EnvVar,EnvStr); ProcessSwitchesString(EnvStr); } } #endif #if !defined(SFX_MODULE) // Preprocess those parameters, which must be processed before the rest of // command line. 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) ProcessSwitch(Arg); 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.empty()) InitLogOptions(LogName,ErrlogCharset); } } else if (Command.empty()) Command=Arg; // 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.empty()) { wchar Cmd[16]; wcsncpyz(Cmd,Command.c_str(),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 std::wstring &Str) { std::wstring Par; std::wstring::size_type Pos=0; while (GetCmdParam(Str,Pos,Par)) { if (IsSwitch(Par[0])) ProcessSwitch(&Par[1]); else { mprintf(St(MSwSyntaxError),Par.c_str()); ErrHandler.Exit(RARX_USERERROR); } } } #endif #if !defined(SFX_MODULE) void CommandData::ProcessSwitch(const wchar *Switch) { if (LargePageAlloc::ProcessSwitch(this,Switch)) return; 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': if (Switch[2]==0) AppendArcNameToPath=APPENDARCNAME_DESTPATH; else if (Switch[2]=='1') AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR; else if (Switch[2]=='2') AppendArcNameToPath=APPENDARCNAME_OWNDIR; break; #ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else if (toupperw(Switch[2])=='F') wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask)); else { GenerateArcName=true; wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); } break; #endif case 'I': IgnoreGeneralAttr=true; break; case 'M': switch(toupperw(Switch[2])) { case 0: case 'S': ArcMetadata=ARCMETA_SAVE; break; case 'R': ArcMetadata=ARCMETA_RESTORE; break; default: BadSwitch(Switch); break; } break; case 'O': AddArcOnly=true; break; case 'P': // Convert slashes here than before every comparison. SlashToNative(Switch+2,ArcPath); break; case 'S': SyncFiles=true; break; default: BadSwitch(Switch); break; } break; case 'C': if (Switch[2]!=0) { if (wcsicomp(Switch+1,L"FG-")==0) ConfigDisabled=true; else BadSwitch(Switch); } else switch(toupperw(Switch[1])) { case '-': DisableComment=true; break; case 'U': ConvertNames=NAMES_UPPERCASE; break; case 'L': ConvertNames=NAMES_LOWERCASE; break; default: BadSwitch(Switch); break; } break; case 'D': if (Switch[2]!=0) BadSwitch(Switch); else switch(toupperw(Switch[1])) { case 'S': DisableSortSolid=true; break; case 'H': OpenShared=true; break; case 'F': DeleteFiles=true; break; default: BadSwitch(Switch); 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; case '4': // Convert slashes here than before every comparison. SlashToNative(Switch+3,ExclArcPath); break; default: BadSwitch(Switch); break; } break; default: if (Switch[1]=='+') { InclFileAttr|=GetExclAttr(Switch+2,InclDir); InclAttrSet=true; } else ExclFileAttr|=GetExclAttr(Switch+1,ExclDir); 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) { if (wcslen(Switch+2)>=MAXPASSWORD) uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); Password.Set(Switch+2); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } else if (!Password.IsSet()) { uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL); eprintf(L"\n"); } break; default : BadSwitch(Switch); break; } break; case 'I': if (wcsnicomp(Switch+1,L"LOG",3)==0) { LogName=Switch[4]!=0 ? Switch+4:DefLogName; break; } if (wcsnicomp(Switch+1,L"SND",3)==0) { Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON; 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) { EmailTo=Switch[4]!=0 ? Switch+4:L"@"; break; } if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv. { VerboseOutput=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; case 'N': DisableNames=true; break; case 'V': VerboseOutput=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;IPACK_MAX_DICT here, so we can use -md[x] to unpack // archives created by future versions with higher PACK_MAX_DICTþ uint Flags; if ((Size=Archive::GetWinSize(Size,Flags))==0 || Size<=0x100000000ULL && !IsPow2(Size)) BadSwitch(Switch); else if (SetDictLimit) WinSizeLimit=Size; else { WinSize=Size; } } break; case 'E': if (toupperw(Switch[2])=='S' && Switch[3]==0) SkipEncrypted=true; break; case 'L': if (toupperw(Switch[2])=='P') { UseLargePages=true; if (!LargePageAlloc::IsPrivilegeAssigned() && LargePageAlloc::AssignConfirmation()) { LargePageAlloc::AssignPrivilege(); // Quit immediately. We do not want to interrupt the current copy // archive processing with reboot after assigning privilege. SetupComplete=true; } } break; case 'M': break; case 'S': GetBriefMaskList(Switch[2]==0 ? DefaultStoreList:Switch+2,StoreArgs); break; #ifdef RAR_SMP case 'T': Threads=atoiw(Switch+2); if (Threads>MaxPoolThreads || Threads<1) BadSwitch(Switch); 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; for (uint I=2;Switch[I]!=0;I++) switch(toupperw(Switch[I])) { case 'A': AbsoluteLinks=true; break; case '-': SkipSymLinks=true; break; default: BadSwitch(Switch); break; } break; #endif #ifdef PROPAGATE_MOTW case 'M': { MotwAllFields=Switch[2]=='1'; const wchar *Sep=wcschr(Switch+2,'='); if (Switch[2]=='-') MotwList.Reset(); else GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,MotwList); } break; #endif #ifdef _WIN_ALL case 'N': if (toupperw(Switch[2])=='I') AllowIncompatNames=true; break; #endif case 'P': ExtrPath=Switch+2; AddEndSlash(ExtrPath); break; 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,L"",&Password,NULL); eprintf(L"\n"); } else { if (wcslen(Switch+1)>=MAXPASSWORD) uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); 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 'I': ProhibitConsoleInput(); UseStdin=Switch[2] ? Switch+2:L"stdin"; break; case 'L': if (IsDigit(Switch[2])) FileSizeLess=GetVolSize(Switch+2,1); break; case 'M': if (IsDigit(Switch[2])) FileSizeMore=GetVolSize(Switch+2,1); 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': SetTimeFilters(Switch+2,true,true); break; case 'N': SetTimeFilters(Switch+2,false,true); break; case 'B': SetTimeFilters(Switch+2,true,false); break; case 'A': SetTimeFilters(Switch+2,false,false); break; case 'S': SetStoreTimeMode(Switch+2); 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': TempPath=Switch+1; AddEndSlash(TempPath); break; case 'Y': AllYes=true; break; case 'Z': if (Switch[1]==0) { // If comment file is not specified, we read data from stdin. CommentFile=L"stdin"; } else CommentFile=Switch+1; 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::ProcessCommand() { #ifndef SFX_MODULE const wchar *SingleCharCommands=L"FUADPXETK"; // RAR -mlp command is the legitimate way to assign the required privilege. if (Command.empty() && UseLargePages || SetupComplete) return; if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || ArcName.empty()) OutHelp(Command.empty() ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. size_t ExtPos=GetExtPos(ArcName); #ifdef _UNIX // If we want to update an archive without extension, in Windows we can use // "arcname." and it will be treated as "arcname". In Unix "arcname" // and "arcname." are two different names, so we check if "arcname" exists // and do not append ".rar", allowing user to update such archive. if (ExtPos==std::wstring::npos && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) ArcName+=L".rar"; #else if (ExtPos==std::wstring::npos) ArcName+=L".rar"; #endif // Treat arcname.part1 as arcname.part1.rar. if (ExtPos!=std::wstring::npos && wcsnicomp(&ArcName[ExtPos],L".part",5)==0 && IsDigit(ArcName[ExtPos+5]) && !FileExist(ArcName)) { std::wstring Name=ArcName+L".rar"; if (FileExist(Name)) ArcName=Name; } if (wcschr(L"AFUMD",Command[0])==NULL && UseStdin.empty()) { if (GenerateArcName) { const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask; GenerateArchiveName(ArcName,Mask,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': { CmdExtract Extract(this); Extract.DoExtract(); } break; #ifndef SILENT case 'V': case 'L': ListArchive(this); break; default: OutHelp(RARX_USERERROR); #endif } // Since messages usually include '\n' in the beginning, we also issue // the final '\n'. It is especially important in Unix, where otherwise // the shell can display the prompt on the same line as the last message. // mprintf is blocked with -idq and if error messages had been displayed // in this mode, we use eprintf to separate them from shell prompt. // If nothing was displayed with -idq, we avoid the excessive empty line. if (!BareOutput) if (MsgStream==MSG_ERRONLY && IsConsoleOutputPresent()) eprintf(L"\n"); else mprintf(L"\n"); } void CommandData::AddArcName(const std::wstring &Name) { ArcNames.AddString(Name); } bool CommandData::GetArcName(wchar *Name,int MaxSize) { return ArcNames.GetString(Name,MaxSize); } bool CommandData::GetArcName(std::wstring &Name) { return ArcNames.GetString(Name); } bool CommandData::IsSwitch(int Ch) { #ifdef _WIN_ALL return Ch=='-' || Ch=='/'; #else return Ch=='-'; #endif } #ifndef SFX_MODULE uint CommandData::GetExclAttr(const wchar *Str,bool &Dir) { if (IsDigit(*Str)) return wcstol(Str,NULL,0); uint Attr=0; while (*Str!=0) { switch(toupperw(*Str)) { case 'D': Dir=true; break; #ifdef _UNIX case 'V': Attr|=S_IFCHR; break; #elif defined(_WIN_ALL) case 'R': Attr|=0x1; break; case 'H': Attr|=0x2; break; case 'S': Attr|=0x4; break; case 'A': Attr|=0x20; break; #endif } Str++; } return Attr; } #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 int64 CommandData::GetVolSize(const wchar *S,uint DefMultiplier) { int64 Size=0,FloatingDivider=0; for (uint I=0;S[I]!=0;I++) if (IsDigit(S[I])) { Size=Size*10+S[I]-'0'; FloatingDivider*=10; } else if (S[I]=='.') FloatingDivider=1; if (*S!=0) { const wchar *ModList=L"bBkKmMgGtT"; const wchar *Mod=wcschr(ModList,S[wcslen(S)-1]); if (Mod==NULL) Size*=DefMultiplier; else for (ptrdiff_t I=2;I<=Mod-ModList;I+=2) Size*=((Mod-ModList)&1)!=0 ? 1000:1024; } if (FloatingDivider!=0) Size/=FloatingDivider; return Size; } // Treat the list like rar;zip as *.rar;*.zip for -ms and similar switches. void CommandData::GetBriefMaskList(const std::wstring &Masks,StringList &Args) { size_t Pos=0; while (PosRewind(); while (Args->GetString(CurMask)) { wchar LastMaskChar=GetLastChar(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. CurMask.pop_back(); } else { // This code doesn't allow to apply -n and -x wildcard masks without // trailing slash to folders unless these masks are * and *.*. // See the changes history below. // 2023.03.26: Previously we removed this code completely to let // 'rar a arc dir -ndir\path\*' include empty folders in 'path' too. // But then we received an email from user not willing -x*.avi to // exclude folders like dir.avi with non-avi files. Also rar.txt // mentions that masks like *.avi exclude only files. Initially // we wanted masks like -npath\* or -xpath\* to match the entire // contents of path including empty folders and added the special // check for "*" and "*.*". But this is not very straightforward, // when *.* and *.avi are processed differently, especially taking // into account that we can specify the exact folder name without // wildcards to process it and masks like 'dir*\' can be used to // exclude folders. So we decided to skip all usual wildcard masks // for folders. // 2023.11.22: We returned the special check for "*" and "*.*", // because users expected 'rar a arc dir -xdir\*' to exclude // everything including subfolders in 'dir'. For now we returned it // both for -n and -x, but we can limit it to -x only if needed. std::wstring Name=PointToName(CurMask); if (IsWildcard(Name) && Name!=L"*" && Name!=L"*.*") 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) CurMask+=L"*"; } #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.empty()) ConvertNameToFull(CheckName,FullName); if (CmpName(CurMask,FullName,MatchMode)) return true; } else #endif { std::wstring CurName=Name; // Important to convert before "*\" check below, so masks like // d:*\something are processed properly. size_t MaskOffset=ConvertPath(&CurMask,nullptr); std::wstring CmpMask=CurMask.substr(MaskOffset); 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. CurName=L'.'; CurName+=CPATHDIVIDER; CurName+=Name; } 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 #if !defined(SFX_MODULE) void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age) { bool ModeOR=false,TimeMods=false; const wchar *S=Mod; // Check if any 'mca' modifiers are present, set OR mode if 'o' is present, // skip modifiers and set S to beginning of time string. Be sure to check // *S!=0, because termination 0 is a part of string for wcschr. for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++) if (*S=='o' || *S=='O') ModeOR=true; else TimeMods=true; if (!TimeMods) // Assume 'm' if no modifiers are specified. Mod=L"m"; // Set the specified time for every modifier. Be sure to check *Mod!=0, // because termination 0 is a part of string for wcschr. This check is // important when we set Mod to "m" above. for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++) switch(toupperw(*Mod)) { case 'M': if (Before) { Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S); FileMtimeBeforeOR=ModeOR; } else { Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S); FileMtimeAfterOR=ModeOR; } break; case 'C': if (Before) { Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S); FileCtimeBeforeOR=ModeOR; } else { Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S); FileCtimeAfterOR=ModeOR; } break; case 'A': if (Before) { Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S); FileAtimeBeforeOR=ModeOR; } else { Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S); FileAtimeAfterOR=ModeOR; } break; } } #endif #ifndef SFX_MODULE // Return 'true' if we need to exclude the file from processing. bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta) { bool FilterOR=false; if (FileMtimeBefore.IsSet()) // Filter present. if (ftm>=FileMtimeBefore) // Condition not matched. if (FileMtimeBeforeOR) FilterOR=true; // Not matched OR filter is present. else return true; // Exclude file in AND mode. else // Condition matched. if (FileMtimeBeforeOR) return false; // Include file in OR mode. if (FileMtimeAfter.IsSet()) // Filter present. if (ftm=FileCtimeBefore) // Condition not matched. if (FileCtimeBeforeOR) FilterOR=true; // Not matched OR filter is present. else return true; // Exclude file in AND mode. else // Condition matched. if (FileCtimeBeforeOR) return false; // Include file in OR mode. if (FileCtimeAfter.IsSet()) // Filter present. if (ftc=FileAtimeBefore) // Condition not matched. if (FileAtimeBeforeOR) FilterOR=true; // Not matched OR filter is present. else return true; // Exclude file in AND mode. else // Condition matched. if (FileAtimeBeforeOR) return false; // Include file in OR mode. if (FileAtimeAfter.IsSet()) // Filter present. if (fta=FileSizeLess) return true; if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) return true; return false; } #endif // Return 0 if file must not be processed or a number of matched parameter otherwise. int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, bool Flags,std::wstring *MatchedArg) { if (MatchedArg!=NULL) MatchedArg->clear(); bool Dir=FileHead.Dir; if (ExclCheck(FileHead.FileName,Dir,false,true)) return 0; #ifndef SFX_MODULE if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime)) return 0; if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) return 0; if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 && (!FileHead.Dir || !InclDir)) return 0; if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; #endif std::wstring ArgName; FileArgs.Rewind(); for (int StringCount=1;FileArgs.GetString(ArgName);StringCount++) { // Ensure that both parameters of CmpName are either C++ strings or // pointers, so we avoid time consuming string construction for one of // parameters in this expensive loop. if (CmpName(ArgName,FileHead.FileName,MatchType)) { if (ExactMatch!=NULL) *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; if (MatchedArg!=NULL) *MatchedArg=ArgName; return StringCount; } } return 0; } #if !defined(SFX_MODULE) void CommandData::SetStoreTimeMode(const wchar *S) { if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+') { // Apply -ts, -ts1, -ts-, -ts+ to all 3 times. // Handle obsolete -ts[2,3,4] as ts+. EXTTIME_MODE Mode=EXTTIME_MAX; if (*S=='-') Mode=EXTTIME_NONE; if (*S=='1') Mode=EXTTIME_1S; xmtime=xctime=xatime=Mode; S++; } while (*S!=0) { EXTTIME_MODE Mode=EXTTIME_MAX; if (S[1]=='-') Mode=EXTTIME_NONE; if (S[1]=='1') Mode=EXTTIME_1S; switch(toupperw(*S)) { case 'M': xmtime=Mode; break; case 'C': xctime=Mode; break; case 'A': xatime=Mode; break; case 'P': PreserveAtime=true; break; } S++; } } #endif unrar/cmdmix.cpp000666 000000 000000 00000006206 15026203744 012353 0ustar00000000 000000 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,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwMLP, MCHelpSwN,MCHelpSwNa,MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL, MCHelpSwOM,MCHelpSwOP,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,MCHelpSwR, MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA, MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr, MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY #endif }; for (uint I=0;IGetChar(); } void RangeCoder::InitDecoder(Unpack *UnpackRead) { RangeCoder::UnpackRead=UnpackRead; low=code=0; range=0xffffffff; for (uint 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 00000027112 15026203744 012363 0ustar00000000 000000 #include "rar.hpp" #include "log.cpp" static MESSAGE_TYPE MsgStream=MSG_STDOUT; static RAR_CHARSET RedirectCharset=RCH_DEFAULT; static bool ProhibitInput=false; static bool ConsoleOutputPresent=false; 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; } void ProhibitConsoleInput() { ProhibitInput=true; } #ifndef SILENT static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { ConsoleOutputPresent=true; // No need for PrintfPrepareFmt here, vwstrprintf calls it. std::wstring s=vwstrprintf(fmt,arglist); ReplaceEsc(s); #ifdef _WIN_ALL if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) { HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); DWORD Written; if (RedirectCharset==RCH_UNICODE) WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL); else { // Avoid Unicode for redirect in Windows, it does not work with pipes. std::string MsgA; if (RedirectCharset==RCH_UTF8) WideToUtf(s,MsgA); else WideToChar(s,MsgA); if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) CharToOemA(&MsgA[0],&MsgA[0]); // 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.data(),(DWORD)MsgA.size(),&Written,NULL); } return; } // MSVC2008 vfwprintf writes every character to console separately // and it is too slow. We use direct WriteConsole call instead. HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); DWORD Written; WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL); #else fputws(s.c_str(),dest); // 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 QuitIfInputProhibited() { // We cannot handle user prompts if -si is used to read file or archive data // from stdin. if (ProhibitInput) { mprintf(St(MStdinNoInput)); ErrHandler.Exit(RARX_FATAL); } } static void GetPasswordText(std::wstring &Str) { QuitIfInputProhibited(); if (StdinRedirected) getwstr(Str); // Read from pipe or redirected file. else { #ifdef _WIN_ALL HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); DWORD ConInMode; GetConsoleMode(hConIn,&ConInMode); SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. std::vector Buf(MAXPASSWORD); // We prefer ReadConsole to ReadFile, so we can read Unicode input. DWORD Read=0; ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL); Buf[Read]=0; Str=Buf.data(); cleandata(Buf.data(),Buf.size()*sizeof(Buf[0])); SetConsoleMode(hConIn,ConInMode); // 2023.03.12: Previously we checked for presence of "\n" in entered // passwords, supposing that truncated strings do not include it. // We did it to read the rest of excessively long string, so it is not // read later as the second password for -p switch. But this "\n" check // doesn't seem to work in Windows 10 anymore and "\r" is present even // in truncated strings. Also we increased MAXPASSWORD, so it is larger // than MAXPASSWORD_RAR. Thus we removed this check as not working // and not that necessary. Low level FlushConsoleInputBuffer doesn't help // for high level ReadConsole, which in line input mode seems to store // the rest of string in its own internal buffer. #else std::vector StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters. #ifdef __VMS fgets(StrA.data(),StrA.size()-1,stdin); #elif defined(__sun) strncpyz(StrA.data(),getpassphrase(""),StrA.size()); #else strncpyz(StrA.data(),getpass(""),StrA.size()); #endif CharToWide(StrA.data(),Str); cleandata(StrA.data(),StrA.size()*sizeof(StrA[0])); #endif } RemoveLF(Str); } #endif #ifndef SILENT bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &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.c_str()); std::wstring PlainPsw; GetPasswordText(PlainPsw); if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL) return false; if (PlainPsw.size()>=MAXPASSWORD) { PlainPsw.erase(MAXPASSWORD-1); uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); } if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) { eprintf(St(MReAskPsw)); std::wstring CmpStr; GetPasswordText(CmpStr); if (CmpStr.empty() || PlainPsw!=CmpStr) { eprintf(St(MNotMatchPsw)); cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); continue; } cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); } Password->Set(PlainPsw.c_str()); cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); break; } return true; } #endif #ifndef SILENT void getwstr(std::wstring &str) { // Print buffered prompt title function before waiting for input. fflush(stderr); QuitIfInputProhibited(); str.clear(); const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name. #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. std::vector StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t. File SrcFile; SrcFile.SetHandleType(FILE_HANDLESTD); SrcFile.SetLineInputMode(true); 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() or set an empty password, so let's better exit. ErrHandler.ReadError(L"stdin"); } StrA[ReadSize]=0; // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR, // such as send passwords, we get OEM encoding by default, unless we // use "chcp" in console. But we avoid OEM to ANSI conversion, // because we also want to handle ANSI files redirection correctly, // like "rar ... < ansifile.txt". CharToWide(&StrA[0],str); cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords. } else { std::vector Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t. DWORD SizeToRead=(DWORD)Buf.size()-1; // ReadConsole fails in Windows 7 for requested input exceeding 30 KB. // Not certain about Windows 8, so check for Windows 10 here. if (WinNT()<=WNT_W10) SizeToRead=Min(SizeToRead,0x4000); DWORD ReadSize=0; if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0) ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort. Buf[ReadSize]=0; str=Buf.data(); } #else std::vector Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t. if (fgetws(&Buf[0],Buf.size(),stdin)==NULL) ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop. str=Buf.data(); #endif RemoveLF(str); } #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 uint MaxItems=10; wchar Item[MaxItems][40]; uint ItemKeyPos[MaxItems],NumItems=0; for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_')) { wchar *CurItem=Item[NumItems]; wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); wchar *EndItem=wcschr(CurItem,'_'); if (EndItem!=nullptr) *EndItem=0; uint KeyPos=0,CurKey; while ((CurKey=CurItem[KeyPos])!=0) { bool Found=false; for (uint I=0;I3 ? L"\n":L" "):L", "); uint KeyPos=ItemKeyPos[I]; for (uint 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 std::wstring &Comment) { if (IsCommentUnsafe(Comment)) return; const size_t MaxOutSize=0x400; for (size_t I=0;I>1)^0xEDB88320 : (C>>1); CRCTab[I]=C; } #ifdef USE_NEON_CRC32 #ifdef _APPLE // getauxval isn't available in OS X uint Value=0; size_t Size=sizeof(Value); int RetCode=sysctlbyname("hw.optional.armv8_crc32",&Value,&Size,NULL,0); CRC_Neon=RetCode==0 && Value!=0; #else CRC_Neon=(getauxval(AT_HWCAP) & HWCAP_CRC32)!=0; #endif #endif } static void InitTables() { InitCRC32(crc_tables[0]); #ifdef USE_SLICING for (uint I=0;I<256;I++) // Build additional lookup tables. { uint C=crc_tables[0][I]; for (uint J=1;J<16;J++) { C=crc_tables[0][(byte)C]^(C>>8); crc_tables[J][I]=C; } } #endif } struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; uint CRC32(uint StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; #ifdef USE_NEON_CRC32 if (CRC_Neon) { for (;Size>=8;Size-=8,Data+=8) #ifdef __clang__ StartCRC = __builtin_arm_crc32d(StartCRC, RawGet8(Data)); #else StartCRC = __builtin_aarch64_crc32x(StartCRC, RawGet8(Data)); #endif for (;Size>0;Size--,Data++) // Process left data. #ifdef __clang__ StartCRC = __builtin_arm_crc32b(StartCRC, *Data); #else StartCRC = __builtin_aarch64_crc32b(StartCRC, *Data); #endif return StartCRC; } #endif #ifdef USE_SLICING // Align Data to 16 for better performance and to avoid ALLOW_MISALIGNED // check below. for (;Size>0 && ((size_t)Data & 15)!=0;Size--,Data++) StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); // 2023.12.06: We switched to slicing-by-16, which seems to be faster than // slicing-by-8 on modern CPUs. Slicing-by-32 would require 32 KB for tables // and could be limited by L1 cache size on some CPUs. for (;Size>=16;Size-=16,Data+=16) { #ifdef BIG_ENDIAN StartCRC ^= RawGet4(Data); uint D1 = RawGet4(Data+4); uint D2 = RawGet4(Data+8); uint D3 = RawGet4(Data+12); #else // We avoid RawGet4 here for performance reason, to access uint32 // directly even if ALLOW_MISALIGNED isn't defined. We can do it, // because we aligned 'Data' above. StartCRC ^= *(uint32 *) Data; uint D1 = *(uint32 *) (Data+4); uint D2 = *(uint32 *) (Data+8); uint D3 = *(uint32 *) (Data+12); #endif StartCRC = crc_tables[15][(byte) StartCRC ] ^ crc_tables[14][(byte)(StartCRC >> 8) ] ^ crc_tables[13][(byte)(StartCRC >> 16)] ^ crc_tables[12][(byte)(StartCRC >> 24)] ^ crc_tables[11][(byte) D1 ] ^ crc_tables[10][(byte)(D1 >> 8) ] ^ crc_tables[ 9][(byte)(D1 >> 16)] ^ crc_tables[ 8][(byte)(D1 >> 24)] ^ crc_tables[ 7][(byte) D2 ] ^ crc_tables[ 6][(byte)(D2 >> 8)] ^ crc_tables[ 5][(byte)(D2 >> 16)] ^ crc_tables[ 4][(byte)(D2 >> 24)] ^ crc_tables[ 3][(byte) D3 ] ^ crc_tables[ 2][(byte)(D3 >> 8)] ^ crc_tables[ 1][(byte)(D3 >> 16)] ^ crc_tables[ 0][(byte)(D3 >> 24)]; } #endif 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 #if 0 static void TestCRC(); struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC; void TestCRC() { // This function is invoked from global object and _SSE_Version is global // and can be initialized after this function. So we explicitly initialize // it here to enable SSE support in Blake2sp. _SSE_Version=GetSSEVersion(); const uint FirstSize=300; byte b[FirstSize]; if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84) mprintf(L"\nCRC32 test1 OK"); else mprintf(L"\nCRC32 test1 FAILED"); if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE) mprintf(L"\nCRC32 test2 OK"); else mprintf(L"\nCRC32 test2 FAILED"); for (uint I=0;I<14;I++) // Check for possible int sign extension. b[I]=(byte)0x7f+I; if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA) mprintf(L"\nCRC32 test3 OK"); else mprintf(L"\nCRC32 test3 FAILED"); for (uint I=0;IIsSet()) return false; CryptData::Method=Method; wchar PwdW[MAXPASSWORD]; Password->Get(PwdW,ASIZE(PwdW)); PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. char PwdA[MAXPASSWORD]; WideToChar(PwdW,PwdA,ASIZE(PwdA)); PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. bool Success=true; 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: Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); break; } cleandata(PwdA,sizeof(PwdA)); cleandata(PwdW,sizeof(PwdW)); return Success; } // 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) != FALSE; 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 00000002352 15026203744 012312 0ustar00000000 000000 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]+=ushort(P+(CRCTab[P]>>16)); } } 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]-=ushort(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 15026203744 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 00000004152 15026203744 012314 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 (uint I=0;I<4;I++) for (uint 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 00000020033 15026203744 012312 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 the 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; // Block index appened to salt. SaltData[SaltLength + 1] = 0; // SaltData[SaltLength + 2] = 0; // Since we do not request the key width SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 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)); } bool 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 false; byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; bool Found=false; for (uint I=0;IPwd==*Password && Item->Lg2Count==Lg2Cnt && 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); Value->CRC32&=0xffffffff; // In case the variable size is larger than 32-bit. } 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 00000033212 15026203744 011642 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{}; 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=nullptr; try { ErrHandler.Clean(); r->OpenResult=0; Data=new DataSet; Data->Cmd.DllError=0; Data->OpenMode=r->OpenMode; Data->Cmd.FileArgs.AddString(L"*"); Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0; std::string AnsiArcName; if (r->ArcName!=nullptr) { AnsiArcName=r->ArcName; #ifdef _WIN_ALL if (!AreFileApisANSI()) OemToExt(r->ArcName,AnsiArcName); #endif } std::wstring ArcName; if (r->ArcNameW!=nullptr && *r->ArcNameW!=0) ArcName=r->ArcNameW; else CharToWide(AnsiArcName,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 nullptr; } 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 nullptr; } r->Flags=0; if (Data->Arc.Volume) r->Flags|=ROADF_VOLUME; if (Data->Arc.MainComment) r->Flags|=ROADF_COMMENT; if (Data->Arc.Locked) r->Flags|=ROADF_LOCK; if (Data->Arc.Solid) r->Flags|=ROADF_SOLID; if (Data->Arc.NewNumbering) r->Flags|=ROADF_NEWNUMBERING; if (Data->Arc.Signed) r->Flags|=ROADF_SIGNED; if (Data->Arc.Protected) r->Flags|=ROADF_RECOVERY; if (Data->Arc.Encrypted) r->Flags|=ROADF_ENCHEADERS; if (Data->Arc.FirstVolume) r->Flags|=ROADF_FIRSTVOLUME; std::wstring CmtDataW; if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW)) { if (r->CmtBufW!=nullptr) { // CmtDataW.push_back(0); size_t Size=wcslen(CmtDataW.data())+1; r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; r->CmtSize=(uint)Min(Size,r->CmtBufSize); memcpy(r->CmtBufW,CmtDataW.data(),(r->CmtSize-1)*sizeof(*r->CmtBufW)); r->CmtBufW[r->CmtSize-1]=0; } else if (r->CmtBuf!=NULL) { std::vector CmtData(CmtDataW.size()*4+1); WideToChar(&CmtDataW[0],&CmtData[0],CmtData.size()-1); size_t Size=strlen(CmtData.data())+1; r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; r->CmtSize=(uint)Min(Size,r->CmtBufSize); memcpy(r->CmtBuf,CmtData.data(),r->CmtSize-1); r->CmtBuf[r->CmtSize-1]=0; } } else r->CmtState=r->CmtSize=0; #ifdef PROPAGATE_MOTW if (r->MarkOfTheWeb!=nullptr) { Data->Cmd.MotwAllFields=r->MarkOfTheWeb[0]=='1'; const wchar *Sep=wcschr(r->MarkOfTheWeb,'='); if (r->MarkOfTheWeb[0]=='-') Data->Cmd.MotwList.Reset(); else Data->Cmd.GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,Data->Cmd.MotwList); } #endif 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{}; 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; } wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW)); WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); if (D->ArcNameEx!=nullptr) wcsncpyz(D->ArcNameEx,Data->Arc.FileName.c_str(),D->ArcNameExSize); wcsncpyz(D->FileNameW,hd->FileName.c_str(),ASIZE(D->FileNameW)); WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); #ifdef _WIN_ALL CharToOemA(D->FileName,D->FileName); #endif if (D->FileNameEx!=nullptr) wcsncpyz(D->FileNameEx,hd->FileName.c_str(),D->FileNameExSize); 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; 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.c_str(),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.clear(); Data->Cmd.DllDestName.clear(); if (DestPath!=NULL) { std::string ExtrPathA=DestPath; #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. OemToExt(ExtrPathA,ExtrPathA); #endif CharToWide(ExtrPathA,Data->Cmd.ExtrPath); AddEndSlash(Data->Cmd.ExtrPath); } if (DestName!=NULL) { std::string DestNameA=DestName; #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. OemToExt(DestNameA,DestNameA); #endif CharToWide(DestNameA,Data->Cmd.DllDestName); } if (DestPathW!=NULL) { Data->Cmd.ExtrPath=DestPathW; AddEndSlash(Data->Cmd.ExtrPath); } if (DestNameW!=NULL) Data->Cmd.DllDestName=DestNameW; 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. // It is important to do it in the same ProcessFile(), because caller // app can rely on this behavior, for example, to overwrite // the extracted Mark of the Web with propagated from archive // immediately after ProcessFile() call. // // 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; } void PASCAL RARSetPassword(HANDLE hArcData,char *Password) { #ifndef RAR_NOCRYPT DataSet *Data=(DataSet *)hArcData; wchar PasswordW[MAXPASSWORD]; CharToWide(Password,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: case RARX_READ: 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. case RARX_BADARC: return ERAR_BAD_ARCHIVE; default: return ERAR_UNKNOWN; } } unrar/encname.cpp000666 000000 000000 00000003520 15026203744 012474 0ustar00000000 000000 #include "rar.hpp" EncodeFileName::EncodeFileName() { Flags=0; FlagBits=0; FlagsPos=0; DestSize=0; } void EncodeFileName::Decode(const char *Name,size_t NameSize, const byte *EncName,size_t EncSize, std::wstring &NameW) { size_t EncPos=0,DecPos=0; byte HighByte=EncPos>6) { case 0: if (EncPos>=EncSize) break; // We need DecPos also for ASCII "Name", so resize() instead of push_back(). NameW.resize(DecPos+1); NameW[DecPos++]=EncName[EncPos++]; break; case 1: if (EncPos>=EncSize) break; NameW.resize(DecPos+1); NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); break; case 2: if (EncPos+1>=EncSize) break; NameW.resize(DecPos+1); 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) if (Code!=RARX_SUCCESS) if (Code==RARX_USERERROR) // Do not write "aborted" when just displaying the online help. mprintf(L"\n"); // For consistency with other errors, which print the final "\n". else mprintf(L"\n%s\n",St(MProgAborted)); #endif SetErrorCode(Code); throw Code; } bool ErrorHandler::GetSysErrMsg(std::wstring &Msg) { #ifndef SILENT #ifdef _WIN_ALL int ErrType=GetLastError(); if (ErrType!=0) { wchar *Buf=nullptr; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM| FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR)&Buf,0,NULL)!=0) { Msg=Buf; LocalFree(Buf); return true; } } #endif #ifdef _UNIX if (errno!=0) { char *err=strerror(errno); if (err!=NULL) { CharToWide(err,Msg); return true; } } #endif #endif return false; } void ErrorHandler::SysErrMsg() { #ifndef SILENT std::wstring Msg; if (!GetSysErrMsg(Msg)) return; #ifdef _WIN_ALL // Print string with \r\n as several strings to multiple lines. size_t Pos=0; while (Pos!=std::wstring::npos) { while (Msg[Pos]=='\r' || Msg[Pos]=='\n') Pos++; if (Pos==Msg.size()) break; size_t EndPos=Msg.find_first_of(L"\r\n",Pos); std::wstring CurMsg=Msg.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos); uiMsg(UIERROR_SYSERRMSG,CurMsg); Pos=EndPos; } #endif #ifdef _UNIX 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 00000013335 15026203744 012547 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,const std::wstring &Name) { #ifdef _WIN_ALL if (Cmd->Test) return; switch(Arc.SubBlockHead.SubType) { 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,const std::wstring &Name) { #ifdef _UNIX if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) ExtractUnixOwner30(Arc,Name.c_str()); #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,const std::wstring &Name) { #ifdef _UNIX if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) SetUnixOwner(Arc,Name); #endif } // Calculate the number of path components except \. and \.. static int CalcAllowedDepth(const std::wstring &Name) { int AllowedDepth=0; for (size_t I=0;I0;I--) if (IsPathDiv(Path[I])) { Path.erase(I); FindData FD; if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) return true; } return false; } bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &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 (uint Pos=0;Pos "." first and "lnk1/lnk2" -> ".." next // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and // file "dir/lnk1/lnk2/poc.txt" last. // Do not confuse with link chains in target, this is in link source path. // It is important for Windows too, though this check can be omitted // if LinksToDirs is invoked in Windows as well. 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=Cmd->ExtrPath.size(); if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0) { while (IsPathDiv(PrepSrcName[ExtrPathLength])) ExtrPathLength++; PrepSrcName.erase(0,ExtrPathLength); } int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; } bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink) { // Returning true in Uplink indicates that link target might include ".." // and enables additional checks. It is ok to falsely return true here, // as it implies only the minor performance penalty. But we shall always // return true for links with ".." in target for security reason. UpLink=true; // Assume the target might include potentially unsafe "..". #if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL) if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows. UpLink=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos; #endif #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.c_str(),UpLink); if (Arc.Format==RARFMT50) return ExtractUnixLink50(Cmd,LinkName.c_str(),&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.c_str(),&Arc.FileHead); #endif return false; } unrar/extract.cpp000666 000000 000000 00000160652 15026203744 012552 0ustar00000000 000000 #include "rar.hpp" CmdExtract::CmdExtract(CommandData *Cmd) { CmdExtract::Cmd=Cmd; ArcAnalyzed=false; Analyze={}; TotalFileCount=0; // Common for all archives involved. Set here instead of DoExtract() // to use in unrar.dll too. // We enable it by default in Unix to care about the case when several // archives are unpacked to same directory with several independent RAR runs. // Worst case performance penalty for a lot of small files seems to be ~3%. // 2023.09.15: Windows performance impact seems to be negligible, // less than 0.5% when extracting mix of small files and folders. // So for extra security we enabled it for Windows too, even though // unlike Unix, Windows doesn't expand lnk1 in symlink targets like // "lnk1/../dir", but converts such path to "dir". ConvertSymlinkPaths=true; Unp=new Unpack(&DataIO); #ifdef RAR_SMP Unp->SetThreads(Cmd->Threads); #endif Unp->AllowLargePages(Cmd->UseLargePages); } CmdExtract::~CmdExtract() { FreeAnalyzeData(); delete Unp; } void CmdExtract::FreeAnalyzeData() { for (size_t I=0;ICommand[0]); if (Cmd->UseStdin.empty()) { FindData FD; while (Cmd->GetArcName(ArcName)) if (FindFile::FastFind(ArcName,&FD)) DataIO.TotalArcSize+=FD.Size; } Cmd->ArcNames.Rewind(); for (uint ArcCount=0;Cmd->GetArcName(ArcName);ArcCount++) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit(). UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit(). while (true) { // 2025.05.11: Add the empty line between tested archives here instead // of printing two leading "\n" in "\n\nExtracting from", which caused // the extra empty line after the copyright message. if (ArcCount>0) mprintf(L"\n"); EXTRACT_ARC_CODE Code=ExtractArchive(); if (Code!=EXTRACT_ARC_REPEAT) break; } DataIO.ProcessedArcSize+=DataIO.LastArcSize; } // 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 (!SuppressNoFilesMessage) 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) { if (Cmd->Command[0]=='T' || Cmd->Command[0]=='I') Cmd->Test=true; #ifdef PROPAGATE_MOTW // Invoke here, so it is also supported by unrar.dll. if (!Cmd->Test && Cmd->MotwList.ItemsCount()>0) Arc.Motw.ReadZoneIdStream(Arc.FileName,Cmd->MotwAllFields); #endif DataIO.AdjustTotalArcSize(&Arc); FileCount=0; MatchedArgs=0; #ifndef SFX_MODULE FirstFile=true; #endif GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); DataIO.UnpVolume=false; PrevProcessed=false; AllMatchesExact=true; AnySolidDataUnpackedWell=false; ArcAnalyzed=false; StartTime.SetCurrentTime(); LastCheckedSymlink.clear(); } EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); if (!Cmd->UseStdin.empty()) { Arc.SetHandleType(FILE_HANDLESTD); #ifdef USE_QOPEN Arc.SetProhibitQOpen(true); #endif } else { // We commented out "&& !defined(WINRAR)", because WinRAR GUI code resets // the cache for usual test command, but not for test after archiving. #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (Cmd->Command[0]=='T' || Cmd->Test) ResetFileCache(ArcName); // Reset the file cache when testing an archive. #endif if (!Arc.WOpen(ArcName)) return EXTRACT_ARC_NEXT; } if (!Arc.IsArchive(true)) { #if !defined(SFX_MODULE) && !defined(RARDLL) if (CmpExt(ArcName,L"rev")) { std::wstring FirstVolName; VolNameToFirstName(ArcName,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 bool RarExt=false; #ifndef SFX_MODULE RarExt=CmpExt(ArcName,L"rar"); #endif if (RarExt) uiMsg(UIERROR_BADARCHIVE,ArcName); // Non-archive .rar file. else mprintf(St(MNotRAR),ArcName.c_str()); // Non-archive not .rar file, likely in "rar x *.*". if (RarExt) ErrHandler.SetErrorCode(RARX_BADARC); return EXTRACT_ARC_NEXT; } if (Arc.FailedHeaderDecryption) // Bad archive password. return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName) { std::wstring FirstVolName; VolNameToFirstName(ArcName,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 Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT. int64 VolumeSetSize=0; // Total size of volumes after the current volume. #ifndef SFX_MODULE if (!ArcAnalyzed && Cmd->UseStdin.empty()) { AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering); ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT. } #endif if (Arc.Volume) { #ifndef SFX_MODULE // Try to speed up extraction for independent solid volumes by starting // extraction from non-first volume if we can. if (!Analyze.StartName.empty()) { ArcName=Analyze.StartName; Analyze.StartName.clear(); UseExactVolName=true; return EXTRACT_ARC_REPEAT; } #endif // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. std::wstring NextName=Arc.FileName; 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,!Arc.NewNumbering); FindData FD; if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Arc); if (Cmd->Command[0]=='I') { Cmd->DisablePercentage=true; } else uiStartArchiveExtract(!Cmd->Test,ArcName); #ifndef SFX_MODULE if (Analyze.StartPos!=0) { Arc.Seek(Analyze.StartPos,SEEK_SET); Analyze.StartPos=0; } #endif 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 set DataIO.TotalArcSize to size // of new first volume to display the total progress correctly. FindData NewArc; if (FindFile::FastFind(ArcName,&NewArc)) DataIO.TotalArcSize=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) { // Unlike Arc.FileName, ArcName might store an old volume name here. if (Analyze.EndPos!=0 && Analyze.EndPos==Arc.CurBlockPos && (Analyze.EndName.empty() || Analyze.EndName==Arc.FileName)) return false; } else { #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) { #ifdef NOVOLUME return false; #else if (!MergeArchive(Arc,&DataIO,false,Command)) { ErrHandler.SetErrorCode(RARX_WARNING); return false; } Arc.Seek(Arc.CurBlockPos,SEEK_SET); return true; #endif } 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 the 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; // This check duplicates Analyze.EndPos and Analyze.EndName // in all cases except volumes on removable media. if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) return false; int MatchType=MATCH_WILDSUBPATH; bool EqualNames=false; std::wstring MatchedArg; bool MatchFound=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,&MatchedArg)!=0; #ifndef SFX_MODULE if (Cmd->ExclPath==EXCL_BASEPATH) { Cmd->ArcPath=MatchedArg; GetPathWithSep(Cmd->ArcPath,Cmd->ArcPath); if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. Cmd->ArcPath.clear(); } #endif if (MatchFound && !EqualNames) AllMatchesExact=false; Arc.ConvertAttributes(); #if !defined(SFX_MODULE) && !defined(RARDLL) if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName) { std::wstring StartVolName; GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,StartVolName); if (StartVolName!=ArcName && FileExist(StartVolName)) { ArcName=StartVolName; Cmd->ArcName=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 } #endif std::wstring ArcFileName; 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 bool RefTarget=false; if (!MatchFound) for (size_t I=0;ITest) // While harmless, it is useless for 't'. { // If reference source isn't selected, but target is selected, // we unpack the source under the temporary name and then rename // or copy it to target name. We do not unpack it under the target // name immediately, because the same source can be used by multiple // targets and it is possible that first target isn't unpacked // for some reason. Also targets might have associated service blocks // like ACLs. All this would complicate processing a lot. DestFileName=!Cmd->TempPath.empty() ? Cmd->TempPath:Cmd->ExtrPath; AddEndSlash(DestFileName); DestFileName+=L"__tmp_reference_source_"; MkTemp(DestFileName,nullptr); MatchedRef.TmpName=DestFileName; } RefTarget=true; // Need it even for 't' to test the reference source. break; } if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) if (Arc.Solid) return false; // Abort the entire extraction for solid archive. else MatchFound=false; // Skip only the current file for non-solid archive. if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=false) { // 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; if (!RefTarget) ExtrPrepareName(Arc,ArcFileName,DestFileName); // DestFileName can be set empty in case of excessive -ap switch. ExtrFile=!SkipSolid && !DestFileName.empty() && !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. } #ifndef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll. if (Arc.FileHead.Encrypted) { RarCheckPassword CheckPwd; if (Arc.Format==RARFMT50 && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader) CheckPwd.Set(Arc.FileHead.Salt,Arc.FileHead.InitV,Arc.FileHead.Lg2Count,Arc.FileHead.PswCheck); while (true) // Repeat the password prompt for wrong and empty passwords. { // 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,CheckPwd.IsSet() ? &CheckPwd:NULL)) { SuppressNoFilesMessage=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]; bool EncSet=DataIO.SetEncryption(false,Arc.FileHead.CryptMethod, &FilePassword,Arc.FileHead.SaltSet ? Arc.FileHead.Salt:nullptr, 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 (EncSet && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. { // This message is used by Android GUI to reset cached passwords. // Update appropriate code if changed. uiMsg(UIERROR_BADPSW,Arc.FileName,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,Arc.FileName,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; } } else DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); #endif // RAR_NOCRYPT // Per file symlink conversion flag. Can be turned off in unrar.dll. bool CurConvertSymlinkPaths=ConvertSymlinkPaths; #ifdef RARDLL if (!Cmd->DllDestName.empty()) { DestFileName=Cmd->DllDestName; // If unrar.dll sets the entire destination pathname, there is no // destination path and we can't convert symlinks, because we would // risk converting important user or system symlinks in this case. // If DllDestName is set, it turns off our path processing and app // invoking the library cares about everything including safety. CurConvertSymlinkPaths=false; } #endif if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && CurConvertSymlinkPaths) ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); File CurFile; bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) { if (Cmd->SkipSymLinks && (Arc.FileHead.RedirType==FSREDIR_UNIXSYMLINK || Arc.FileHead.RedirType==FSREDIR_WINSYMLINK || Arc.FileHead.RedirType==FSREDIR_JUNCTION)) ExtrFile=false; if (ExtrFile && Command!='P' && !Cmd->Test) { // Overwrite prompt for symbolic and hard links and when we move // a temporary file to the file reference instead of copying it. bool UserReject=false; if (FileExist(DestFileName)) FileCreate(Cmd,NULL,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). { // Check the dictionary size before creating a file and issuing // any overwrite prompts. if (!CheckWinLimit(Arc,ArcFileName)) return false; // Read+write mode is required to set "Compressed" attribute. // Other than that prefer the write only mode to avoid // OpenIndiana NAS problem with SetFileTime and read+write files. #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool Compressed=Cmd->SetCompressedAttr && (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0; bool WriteOnly=!Compressed; #else bool WriteOnly=true; #endif ExtrFile=ExtrCreateFile(Arc,CurFile,WriteOnly); #if defined(_WIN_ALL) && !defined(SFX_MODULE) // 2024.03.12: Set early to compress written data immediately. // For 10 GB text file it was ~1.5x faster than when set after close. if (ExtrFile && Compressed) SetFileCompression(CurFile.GetHandle(),true); #endif } 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; // Check the dictionary size also for skipping files. if (!CheckWinLimit(Arc,ArcFileName)) 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' && !Cmd->DisableNames) if (SkipSolid) mprintf(St(MExtrSkipFile),ArcFileName.c_str()); else switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. { case 'T': mprintf(St(MExtrTestFile),ArcFileName.c_str()); break; #ifndef SFX_MODULE case 'P': mprintf(St(MExtrPrinting),ArcFileName.c_str()); break; #endif case 'X': case 'E': mprintf(St(MExtrFile),DestFileName.c_str()); break; } if (!Cmd->DisablePercentage && !Cmd->DisableNames) mprintf(L" "); if (Cmd->DisableNames) uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode. 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 uint64 Preallocated=0; if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() && (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) { CurFile.Prealloc(Arc.FileHead.UnpSize); Preallocated=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) { std::wstring RedirName; // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning // from 5.10 even Windows version uses / internally and converts // them to \ when reading FHEXTRA_REDIR. // We must perform this conversion before ConvertPath call, // so paths mixing different slashes like \dir1/dir2\file are // processed correctly. SlashToNative(Arc.FileHead.RedirName,RedirName); ConvertPath(&RedirName,&RedirName); std::wstring NameExisting; ExtrPrepareName(Arc,RedirName,NameExisting); if (FileCreateMode && !NameExisting.empty()) // *NameExisting can be empty in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting); else LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,Arc.FileHead.UnpSize); } else if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) { if (FileCreateMode) { bool UpLink; LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); ConvertSymlinkPaths|=LinkSuccess && UpLink; // We do not actually need to reset the cache here if we cache // only the single last checked path, because at this point // it will always contain the link own path and link can't // overwrite its parent folder. But if we ever decide to cache // several already checked paths, we'll need to reset them here. // Otherwise if no files were created in one of such paths, // let's say because of file create error, it might be possible // to overwrite the path with link and avoid checks. We keep this // code here as a reminder in case of possible modifications. LastCheckedSymlink.clear(); // Reset cache for safety reason. } } else { uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName); 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 { try { Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); } catch (std::bad_alloc) { if (Arc.FileHead.WinSize>=0x40000000) uiMsg(UIERROR_EXTRDICTOUTMEM,Arc.FileName,uint(Arc.FileHead.WinSize/0x40000000+(Arc.FileHead.WinSize%0x40000000!=0 ? 1 : 0))); throw; } Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE // RAR 1.3 - 1.5 archives do not set per file solid flag. 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' && !Cmd->DisableNames) 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 { // We check SkipSolid to remove percent for skipped solid files only. // We must not apply these \b to links with ShowChecksum==false // and their possible error messages. if (SkipSolid) mprintf(L"\b\b\b\b\b "); } if (!TestMode && (Command=='X' || Command=='E') && (!LinkEntry || LinkSuccess) && (!BrokenFile || Cmd->KeepBroken)) { // Set everything for usual files and file references. bool SetAll=!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY; // Set time and adjust size for usual files and references. // Symlink time requires the special treatment and it is set directly // after creating a symlink. bool SetTimeAndSize=SetAll; // Set file attributes for usual files, references and hard links. // Hard link shares the file metadata with link target, so we do not // need to set link time or owner. But when we overwrite an existing // link, we can call PrepareToDelete(), which affects link target // attributes too. So we set link attributes to restore both target // and link attributes if PrepareToDelete() has changed them. bool SetAttr=SetAll || Arc.FileHead.RedirType==FSREDIR_HARDLINK; // Call SetFileHeaderExtra to set Unix user and group for usual files, // references and symlinks. Unix symlink can have its own owner data. bool SetExtra=SetAll || Arc.FileHead.RedirType==FSREDIR_UNIXSYMLINK; // Below we use DestFileName instead of CurFile.FileName, // so we can set file attributes also for hard links, which do not // have the open CurFile. These strings are the same for other items. if (SetTimeAndSize) { // We could preallocate more space than really written to broken file // or file with crafted header. if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) CurFile.Truncate(); #ifdef PROPAGATE_MOTW Arc.Motw.CreateZoneIdStream(DestFileName,Cmd->MotwList); #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 (SetExtra) SetFileHeaderExtra(Cmd,Arc,DestFileName); if (SetTimeAndSize) CurFile.SetCloseFileTime( Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); if (SetAttr) { #if defined(_WIN_ALL) && !defined(SFX_MODULE) if (Cmd->ClearArc) Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; #endif if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr)) { uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName); // Android cannot set file attributes and while UIERROR_FILEATTR // above is handled by Android RAR silently, this call would cause // "Operation not permitted" message for every unpacked file. ErrHandler.SysErrMsg(); } } 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) { std::vector Buffer(File::CopyBufferSize()); while (true) { int ReadSize=DataIO.UnpRead(Buffer.data(),Buffer.size()); if (ReadSize<=0) break; int WriteSize=ReadSize0) { DataIO.UnpWrite(Buffer.data(),WriteSize); DestUnpSize-=WriteSize; } } } bool CmdExtract::ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize) { File Existing; if (!Existing.Open(NameExisting)) { std::wstring TmpExisting=NameExisting; // NameExisting is 'const', so copy it here. bool OpenFailed=true; // If we couldn't find the existing file, check if match is present // in temporary reference sources list. for (size_t I=0;IDllError=ERAR_EREFERENCE; #endif return false; } } std::vector Buffer(0x100000); int64 CopySize=0; while (true) { Wait(); int ReadSize=Existing.Read(Buffer.data(),Buffer.size()); if (ReadSize==0) break; // Update only the current file progress in WinRAR, set the total to 0 // to keep it as is. It looks better for WinRAR. uiExtractProgress(CopySize,UnpSize,0,0); New.Write(Buffer.data(),ReadSize); CopySize+=ReadSize; } return true; } void CmdExtract::ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName) { if (Cmd->Test) { // Destination name conversion isn't needed for simple archive test. // This check also allows to avoid issuing "Attempting to correct... // Renaming..." messages in MakeNameCompatible() below for problematic // names like aux.txt when testing an archive. DestName=ArcFileName; return; } DestName=Cmd->ExtrPath; if (!Cmd->ExtrPath.empty()) { wchar LastChar=GetLastChar(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/ // so we call IsPathDiv first instead of just calling AddEndSlash, // which checks for only one type of path separator. // 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); } } #ifndef SFX_MODULE if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE) { switch(Cmd->AppendArcNameToPath) { case APPENDARCNAME_DESTPATH: // To subdir of destination path. DestName+=PointToName(Arc.FirstVolumeName); RemoveExt(DestName); break; case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir. DestName=Arc.FirstVolumeName; RemoveExt(DestName); break; case APPENDARCNAME_OWNDIR: // To archive own dir. DestName=Arc.FirstVolumeName; RemoveNameFromPath(DestName); break; } AddEndSlash(DestName); } #endif // We need to modify the name below and ArcFileName is const. std::wstring CurName=ArcFileName; #ifndef SFX_MODULE std::wstring &ArcPath=!Cmd->ExclArcPath.empty() ? Cmd->ExclArcPath:Cmd->ArcPath; size_t ArcPathLength=ArcPath.size(); if (ArcPathLength>0) { size_t NameLength=CurName.size(); if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,CurName,ArcPathLength)==0 && (IsPathDiv(ArcPath[ArcPathLength-1]) || IsPathDiv(CurName[ArcPathLength]) || CurName[ArcPathLength]==0)) { size_t Pos=Min(ArcPathLength,NameLength); while (PosCommand[0]; // Use -ep3 only in systems, where disk letters are exist, not in Unix. bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); if (AbsPaths) { // We do not use a user specified destination path when extracting // absolute paths in -ep3 mode. wchar DiskLetter=toupperw(CurName[0]); if (CurName[1]=='_' && IsPathDiv(CurName[2]) && DiskLetter>='A' && DiskLetter<='Z') DestName=CurName.substr(0,1) + L':' + CurName.substr(2); else if (CurName[0]=='_' && CurName[1]=='_') DestName=std::wstring(2,CPATHDIVIDER) + CurName.substr(2); else AbsPaths=false; // Apply the destination path even with -ep3 for not absolute path. } if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) CurName=PointToName(CurName); if (!AbsPaths) DestName+=CurName; #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 } #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; CharToWide(PasswordA,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 std::wstring &ArcFileName,RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !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.c_str()); switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) { case -1: ErrHandler.Exit(RARX_USERBREAK); case 2: if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)) 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 std::wstring &ArcFileName) { if (Cmd->Test) { if (!Cmd->DisableNames) { mprintf(St(MExtrTestFile),ArcFileName.c_str()); 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,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); DirExist=false; } if (!DirExist) { CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName)) { uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); std::wstring OrigName=DestFileName; MakeNameUsable(DestFileName,true); #ifndef SFX_MODULE uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); if (!DirExist && (Cmd->AbsoluteLinks || !ConvertSymlinkPaths || LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink))) { CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); } } } } if (MDCode==MKDIR_SUCCESS) { if (!Cmd->DisableNames) { mprintf(St(MCreatDir),DestFileName.c_str()); 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()!=WNT_NONE) 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 WriteOnly) { 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; if (!FileCreate(Cmd,&CurFile,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,WriteOnly)) { Success=false; if (!UserReject) { ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName))) uiMsg(UIERROR_DIRNAMEEXISTS); #ifdef RARDLL Cmd->DllError=ERAR_ECREATE; #endif if (!IsNameUsable(DestFileName)) { uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); std::wstring OrigName=DestFileName; MakeNameUsable(DestFileName,true); if (Cmd->AbsoluteLinks || !ConvertSymlinkPaths || LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink)) { CreatePath(DestFileName,true,Cmd->DisableNames); if (FileCreate(Cmd,&CurFile,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 std::wstring &ArcFileName) { bool WrongVer; if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 and 7.0 archives. WrongVer=Arc.FileHead.UnpVer>VER_UNPACK7; 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; // Can't unpack the unknown encryption even for stored files. if (Arc.FileHead.CryptMethod==CRYPT_UNKNOWN) WrongVer=true; if (WrongVer) { ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); // No need to suggest a new version if it is just a broken archive. if (!Arc.BrokenHeader) uiMsg(UIERROR_NEWERRAR,Arc.FileName); } return !WrongVer; } #ifndef SFX_MODULE // Find non-matched reference sources in solid and non-solid archives. // Detect the optimal start position for semi-solid archives // and optimal start volume for independent solid volumes. // // Alternatively we could collect references while extracting an archive // and perform the second extraction pass for references only. // But it would be slower for solid archives than scaning headers // in first pass and extracting everything in second, as implemented now. // void CmdExtract::AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering) { FreeAnalyzeData(); // If processing non-first archive in multiple archives set. wchar *ArgName=Cmd->FileArgs.GetString(); Cmd->FileArgs.Rewind(); if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) return; // No need to check further for * and *.* masks. // Start search from first volume if all volumes preceding current are available. std::wstring NextName; if (Volume) GetFirstVolIfFullSet(ArcName,NewNumbering,NextName); else NextName=ArcName; bool MatchFound=false; bool PrevMatched=false; bool OpenNext=false; bool FirstVolume=true; // We shall set FirstFile once for all volumes and not for each volume. // So we do not reuse the outdated Analyze.StartPos from previous volume // if extracted file resides completely in the beginning of current one. bool FirstFile=true; while (true) { Archive Arc(Cmd); if (!Arc.Open(NextName) || !Arc.IsArchive(false)) { if (OpenNext) { // If we couldn't open trailing volumes, we can't set early exit // parameters. It is possible that some volume are on removable media // and will be provided by user when extracting. Analyze.EndName.clear(); Analyze.EndPos=0; } break; } OpenNext=false; while (Arc.ReadHeader()>0) { Wait(); HEADER_TYPE HeaderType=Arc.GetHeaderType(); if (HeaderType==HEAD_ENDARC) { OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume. break; } if (HeaderType==HEAD_FILE) { if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15) { // RAR versions earlier than 2.0 do not set per file solid flag. // They have only the global archive solid flag, so we can't // reliably analyze them here. OpenNext=false; break; } if (!Arc.FileHead.SplitBefore) { if (!MatchFound && !Arc.FileHead.Solid && !Arc.FileHead.Dir && Arc.FileHead.RedirType==FSREDIR_NONE && Arc.FileHead.Method!=0) { // Can start extraction from here. // We would gain nothing and unnecessarily complicate extraction // if we set StartName for first volume or StartPos for first // archived file. if (!FirstVolume) Analyze.StartName=NextName; // We shall set FirstFile once for all volumes for this code // to work properly. Alternatively we could append // "|| Analyze.StartPos!=0" to the condition, so we do not reuse // the outdated Analyze.StartPos value from previous volume. if (!FirstFile) Analyze.StartPos=Arc.CurBlockPos; } if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0) { MatchFound = true; PrevMatched = true; // Reset the previously set early exit position, if any, because // we found a new matched file. Analyze.EndPos=0; // Matched file reference pointing at maybe non-matched source file. // Even though we know RedirName, we can't check if source file // is certainly non-matched, because it can be filtered out by // date or attributes, which we do not know here. if (Arc.FileHead.RedirType==FSREDIR_FILECOPY) { bool AlreadyAdded=false; for (size_t I=0;IWinSizeLimit || Arc.FileHead.WinSize<=Cmd->WinSize) return true; if (uiDictLimit(Cmd,ArcFileName,Arc.FileHead.WinSize,Max(Cmd->WinSizeLimit,Cmd->WinSize))) { // No more prompts when extracting other files. Important for GUI versions, // where we might not have [Max]WinSize set permanently when extracting. Cmd->WinSizeLimit=Arc.FileHead.WinSize; } else { ErrHandler.SetErrorCode(RARX_FATAL); #ifdef RARDLL Cmd->DllError=ERAR_LARGE_DICT; #endif Arc.SeekToNext(); return false; } return true; } unrar/filcreat.cpp000666 000000 000000 00000012415 15026203744 012662 0ustar00000000 000000 #include "rar.hpp" // If NewFile==NULL, we delete created file after user confirmation. // It is useful if we need to overwrite an existing folder or file, // but need user confirmation for that. bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name, 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,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, // also as "Compressed" attribute and hard links. In GUI version we avoid // deleting an existing file for non-.rar archive formats as well. uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; if (NewFile!=NULL && NewFile->Create(Name,FileMode)) return true; CreatePath(Name,true,Cmd->DisableNames); return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } #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 std::wstring &Name) { DWORD Res=GetLongPathName(Name.c_str(),NULL,0); if (Res==0) return false; std::vector LongPathBuf(Res); Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size()); if (Res==0 || Res>=LongPathBuf.size()) return false; Res=GetShortPathName(Name.c_str(),NULL,0); if (Res==0) return false; std::vector ShortPathBuf(Res); Res=GetShortPathName(Name.c_str(),ShortPathBuf.data(),(DWORD)ShortPathBuf.size()); if (Res==0 || Res>=ShortPathBuf.size()) return false; std::wstring LongPathName=LongPathBuf.data(); std::wstring ShortPathName=ShortPathBuf.data(); std::wstring LongName=PointToName(LongPathName); std::wstring 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.empty() || wcsicomp(LongName,ShortName)==0 || wcsicomp(PointToName(Name),ShortName)!=0) return false; // Generate the temporary new name for existing file. std::wstring NewName; for (uint I=0;I<10000 && NewName.empty();I+=123) { // Here we copy the path part of file to create. We'll make the temporary // file in the same folder. NewName=Name; // Here we set the random name part. SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I)); // If such file is already exist, try next random name. if (FileExist(NewName)) NewName.clear(); } // If we could not generate the name not used by any other file, we return. if (NewName.empty()) 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. std::wstring FullName=Name; SetName(FullName,LongName); // Rename the existing file to randomly generated name. Normally it changes // the short name too. if (!MoveFile(FullName.c_str(),NewName.c_str())) 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.c_str(),FullName.c_str()); if (Created) { // Delete the temporary zero length file occupying the short name, KeepShortFile.Close(); KeepShortFile.Delete(); } // We successfully changed the short name. We do not use the simpler // SetFileShortName Windows API call, because it requires SE_RESTORE_NAME // privilege. return true; } #endif unrar/file.cpp000666 000000 000000 00000052663 15026203744 012021 0ustar00000000 000000 #include "rar.hpp" File::File() { hFile=FILE_BAD_HANDLE; NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; LineInput=false; SkipClose=false; ErrorType=FILE_SUCCESS; OpenShared=false; AllowDelete=true; AllowExceptions=true; PreserveAtime=false; #ifdef _WIN_ALL CreateMode=FMF_UNDEFINED; #endif ReadErrorMode=FREM_ASK; TruncatedAfterReadError=false; CurFilePos=0; } 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; TruncatedAfterReadError=SrcFile.TruncatedAfterReadError; FileName=SrcFile.FileName; SrcFile.SkipClose=true; } bool File::Open(const std::wstring &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=FILE_FLAG_SEQUENTIAL_SCAN; FindData FD; if (PreserveAtime) Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. hNewFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); DWORD LastError; if (hNewFile==FILE_BAD_HANDLE) { LastError=GetLastError(); std::wstring LongName; if (GetWinLongPath(Name,LongName)) { hNewFile=CreateFile(LongName.c_str(),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; if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE) { FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification. SetFileTime(hNewFile,NULL,&ft,NULL); } #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 // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+. #if defined(O_NOATIME) if (PreserveAtime) flags|=O_NOATIME; #endif std::string NameA; WideToChar(Name,NameA); int handle=open(NameA.c_str(),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; FileName=Name; TruncatedAfterReadError=false; } return Success; } #if !defined(SFX_MODULE) void File::TOpen(const std::wstring &Name) { if (!WOpen(Name)) ErrHandler.Exit(RARX_OPEN); } #endif bool File::WOpen(const std::wstring &Name) { if (Open(Name)) return true; ErrHandler.OpenErrorMsg(Name); return false; } bool File::Create(const std::wstring &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=GetLastChar(Name); bool Special=LastChar=='.' || LastChar==' '; if (Special && (Mode & FMF_STANDARDNAMES)==0) hFile=FILE_BAD_HANDLE; else hFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); if (hFile==FILE_BAD_HANDLE) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) hFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); } #else #ifdef FILE_USE_OPEN std::string NameA; WideToChar(Name,NameA); hFile=open(NameA.c_str(),(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); #else hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY); #endif #endif NewFile=true; HandleType=FILE_HANDLENORMAL; SkipClose=false; FileName=Name; return hFile!=FILE_BAD_HANDLE; } #if !defined(SFX_MODULE) void File::TCreate(const std::wstring &Name,uint Mode) { if (!WCreate(Name,Mode)) ErrHandler.Exit(RARX_FATAL); } #endif bool File::WCreate(const std::wstring &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)!=FALSE; #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 std::wstring &NewName) { // No need to rename if names are already same. bool Success=(NewName==FileName); if (!Success) Success=RenameFile(FileName,NewName); if (Success) 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(L"",FileName); } break; } LastWrite=true; return Success; // It can return false only if AllowExceptions is disabled. } int File::Read(void *Data,size_t Size) { if (TruncatedAfterReadError) return 0; int64 FilePos=0; // Initialized only to suppress some compilers warning. if (ReadErrorMode==FREM_IGNORE) FilePos=Tell(); int TotalRead=0; while (true) { int ReadSize=DirectRead(Data,Size); if (ReadSize==-1) { ErrorType=FILE_READERROR; if (AllowExceptions) if (ReadErrorMode==FREM_IGNORE) { ReadSize=0; for (size_t I=0;I0 && (uint)ReadSize0) // Can be -1 for error and AllowExceptions disabled. CurFilePos+=TotalRead; return TotalRead; // It can return -1 only if AllowExceptions is disabled. } // Returns -1 in case of error. int File::DirectRead(void *Data,size_t Size) { #ifdef _WIN_ALL const size_t MaxDeviceRead=20000; const size_t MaxLockedRead=32768; #endif if (HandleType==FILE_HANDLESTD) { #ifdef _WIN_ALL // if (Size>MaxDeviceRead) // 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 (!IsSeekable()) // To extract archives from stdin with -si. { // We tried to dynamically allocate 32 KB buffer here, but it improved // speed in Windows 10 by mere ~1.5%. byte Buf[4096]; if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos) { uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos; while (SkipSize>0) // Reading to emulate seek forward. { int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); if (ReadSize<=0) return false; SkipSize-=ReadSize; CurFilePos+=ReadSize; } return true; } // May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking // RAR 4.x archives without the end of archive block created with -en. if (Method==SEEK_END) { int ReadSize; while ((ReadSize=Read(Buf,ASIZE(Buf)))>0) CurFilePos+=ReadSize; return true; } return false; // Backward seek on unseekable file. } 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; if (!IsSeekable()) return CurFilePos; #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)!=FALSE; #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 std::wstring &Name,RarTime *ftm,RarTime *fta) { #ifdef _UNIX bool setm=ftm!=NULL && ftm->IsSet(); bool seta=fta!=NULL && fta->IsSet(); if (setm || seta) { std::string NameA; WideToChar(Name,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.c_str(),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.c_str(),&ut); #endif } #endif } #ifdef _UNIX void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta) { #ifdef UNIX_TIME_NS #if defined(_APPLE) if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); #else if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); #endif #else if (ftm!=NULL) ftm->SetUnix(st.st_mtime); if (ftc!=NULL) ftc->SetUnix(st.st_ctime); if (fta!=NULL) fta->SetUnix(st.st_atime); #endif } #endif void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) { #ifdef _WIN_ALL FILETIME ctime,atime,mtime; GetFileTime(hFile,&ctime,&atime,&mtime); if (ftm!=NULL) ftm->SetWinFT(&mtime); if (ftc!=NULL) ftc->SetWinFT(&ctime); if (fta!=NULL) fta->SetWinFT(&atime); #elif defined(_UNIX) struct stat st; fstat(GetFD(),&st); StatToRarTime(st,ftm,ftc,fta); #endif } int64 File::FileLength() { int64 SavePos=Tell(); Seek(0,SEEK_END); int64 Length=Tell(); Seek(SavePos,SEEK_SET); return Length; } 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) { bool CopyAll=(Length==INT64NDF); // Adjust the buffer size to data size. So we do not waste too much time // to vector initialization when copying many small data blocks like // when updating an archive with many small files. size_t BufSize=File::CopyBufferSize(); if (!CopyAll && Length<(int64)BufSize) BufSize=(size_t)Length; std::vector Buffer(BufSize); int64 CopySize=0; while (CopyAll || Length>0) { Wait(); size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size(); byte *Buf=Buffer.data(); 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 00000040323 15026203744 012333 0ustar00000000 000000 #include "rar.hpp" MKDIR_CODE MakeDir(const std::wstring &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=GetLastChar(Name); bool Special=LastChar=='.' || LastChar==' '; BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL); if (RetCode==0 && !FileExist(Name)) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) RetCode=CreateDirectory(LongName.c_str(),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) std::string NameA; WideToChar(Name,NameA); mode_t uattr=SetAttr ? (mode_t)Attr:0777; int ErrCode=mkdir(NameA.c_str(),uattr); if (ErrCode==-1) return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; return MKDIR_SUCCESS; #else return MKDIR_ERROR; #endif } // Simplified version of MakeDir(). bool CreateDir(const std::wstring &Name) { return MakeDir(Name,false,0)==MKDIR_SUCCESS; } bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent) { if (Path.empty()) return false; #ifdef _WIN_ALL uint DirAttr=0; #else uint DirAttr=0777; #endif bool Success=true; for (size_t I=0;I0 check avoids attempting // creating an empty directory for paths starting from path separator. if (IsPathDiv(Path[I]) && I>0) { #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 (I==2 && Path[1]==':') continue; #endif std::wstring DirName=Path.substr(0,I); Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; if (Success && !Silent) { mprintf(St(MCreatDir),DirName.c_str()); mprintf(L" %s",St(MOk)); } } } if (!SkipLastName && !IsPathDiv(GetLastChar(Path))) Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; return Success; } void SetDirTime(const std::wstring &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.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) hFile=CreateFile(LongName.c_str(),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 #ifdef _UNIX File::SetCloseFileTimeByName(Name,ftm,fta); #endif } bool IsRemovable(const std::wstring &Name) { #if defined(_WIN_ALL) std::wstring Root; GetPathRoot(Name,Root); int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str()); return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; #else return false; #endif } #ifndef SFX_MODULE int64 GetFreeDisk(const std::wstring &Name) { #ifdef _WIN_ALL std::wstring Root; GetPathWithSep(Name,Root); ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) && uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); return 0; #elif defined(_UNIX) std::wstring Root; GetPathWithSep(Name,Root); std::string RootA; WideToChar(Root,RootA); struct statvfs sfs; if (statvfs(RootA.empty() ? ".":RootA.c_str(),&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 std::wstring &Name) { std::wstring Root; GetPathRoot(Name,Root); wchar FileSystem[MAX_PATH+1]; // Root can be empty, when we create volumes with -v in the current folder. if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),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 std::wstring &Name) { #ifdef _WIN_ALL return GetFileAttr(Name)!=0xffffffff; #elif defined(ENABLE_ACCESS) std::string NameA; WideToChar(Name,NameA); return access(NameA.c_str(),0)==0; #else FindData FD; return FindFile::FastFind(Name,&FD); #endif } bool WildFileExist(const std::wstring &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); #else return false; #endif } 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 std::wstring &Name) { #ifdef _WIN_ALL SetFileAttr(Name,0); #endif #ifdef _UNIX std::string NameA; WideToChar(Name,NameA); chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR); #endif } uint GetFileAttr(const std::wstring &Name) { #ifdef _WIN_ALL DWORD Attr=GetFileAttributes(Name.c_str()); if (Attr==0xffffffff) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) Attr=GetFileAttributes(LongName.c_str()); } return Attr; #else std::string NameA; WideToChar(Name,NameA); struct stat st; if (stat(NameA.c_str(),&st)!=0) return 0; return st.st_mode; #endif } bool SetFileAttr(const std::wstring &Name,uint Attr) { #ifdef _WIN_ALL bool Success=SetFileAttributes(Name.c_str(),Attr)!=0; if (!Success) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) Success=SetFileAttributes(LongName.c_str(),Attr)!=0; } return Success; #elif defined(_UNIX) std::string NameA; WideToChar(Name,NameA); return chmod(NameA.c_str(),(mode_t)Attr)==0; #else return false; #endif } // Ext is the extension with the leading dot, like L".bat", or nullptr to use // the default extension. bool MkTemp(std::wstring &Name,const wchar *Ext) { 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 RandomExt=Random%50000+Attempt; if (Attempt==1000) return false; // User asked to specify the single extension for all temporary files, // so it can be added to server ransomware protection exceptions. // He wrote, this protection blocks temporary files when adding // a file to RAR archive with drag and drop. So unless a calling code // requires a specific extension, like .bat file when uninstalling, // we set the uniform extension here. if (Ext==nullptr) Ext=L".rartemp"; std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext; if (!FileExist(NewName)) { Name=NewName; break; } } return true; } #if !defined(SFX_MODULE) void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) { int64 SavePos=SrcFile->Tell(); #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; std::vector 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.data(),SizeToRead); if (ReadSize==0) break; TotalRead+=ReadSize; if ((++BlockCount & 0xf)==0) { #ifndef SILENT if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) { // Update only the current file progress in WinRAR, set the total to 0 // to keep it as is. It looks better for WinRAR. uiExtractProgress(TotalRead,FileLength,0,0); } else { if ((Flags & CALCFSUM_SHOWPERCENT)!=0) uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); } #endif Wait(); } if (CRC32!=NULL) HashCRC.Update(Data.data(),ReadSize); if (Blake2!=NULL) HashBlake2.Update(Data.data(),ReadSize); if (Size!=INT64NDF) Size-=ReadSize; } SrcFile->Seek(SavePos,SEEK_SET); 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 std::wstring &SrcName,const std::wstring &DestName) { #ifdef _WIN_ALL bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0; if (!Success) { std::wstring LongName1,LongName2; if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2)) Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0; } return Success; #else std::string SrcNameA,DestNameA; WideToChar(SrcName,SrcNameA); WideToChar(DestName,DestNameA); bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0; return Success; #endif } bool DelFile(const std::wstring &Name) { #ifdef _WIN_ALL bool Success=DeleteFile(Name.c_str())!=0; if (!Success) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) Success=DeleteFile(LongName.c_str())!=0; } return Success; #else std::string NameA; WideToChar(Name,NameA); bool Success=remove(NameA.c_str())==0; return Success; #endif } bool DelDir(const std::wstring &Name) { #ifdef _WIN_ALL bool Success=RemoveDirectory(Name.c_str())!=0; if (!Success) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) Success=RemoveDirectory(LongName.c_str())!=0; } return Success; #else std::string NameA; WideToChar(Name,NameA); bool Success=rmdir(NameA.c_str())==0; return Success; #endif } #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool SetFileCompression(const std::wstring &Name,bool State) { HANDLE hFile=CreateFile(Name.c_str(),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) { std::wstring LongName; if (GetWinLongPath(Name,LongName)) hFile=CreateFile(LongName.c_str(),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; } bool Success=SetFileCompression(hFile,State); CloseHandle(hFile); return Success; } bool SetFileCompression(HANDLE hFile,bool State) { 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); return RetCode!=0; } void ResetFileCache(const std::wstring &Name) { // To reset file cache in Windows it is enough to open it with // FILE_FLAG_NO_BUFFERING and then close it. HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); if (hSrc!=INVALID_HANDLE_VALUE) CloseHandle(hSrc); } #endif // Delete symbolic links in file path, if any, and replace them by directories. // Prevents extracting files outside of destination folder with symlink chains. bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked) { // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like // "lnk1/../dir", but converts the path to "dir". In Unix we need to call // this function to prevent placing unpacked files outside of destination // folder if previously we unpacked "dir/lnk1" -> "..", // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". // We may still need this function to prevent abusing symlink chains // in link source path if we remove detection of such chains // in IsRelativeSymlinkSafe. This function seems to make other symlink // related safety checks redundant, but for now we prefer to keep them too. // // 2022.12.01: the performance impact is minimized after adding the check // against the previous path and enabling this verification only after // extracting a symlink with ".." in target. So we enabled it for Windows // as well for extra safety. //#ifdef _UNIX std::wstring Path=SrcName; size_t SkipLength=SkipPart.size(); if (SkipLength>0 && Path.rfind(SkipPart,0)!=0) SkipLength=0; // Parameter validation, not really needed now. // Do not check parts already checked in previous path to improve performance. for (size_t I=0;ISkipLength) SkipLength=I; // Avoid converting symlinks in destination path part specified by user. while (SkipLength0) for (size_t I=Path.size()-1;I>SkipLength;I--) if (IsPathDiv(Path[I])) { Path.erase(I); FindData FD; if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) { #ifdef _WIN_ALL // Normally Windows symlinks to directory look like a directory // and are deleted with DelDir(). It is possible to create // a file-like symlink pointing at directory, which can be deleted // only with && DelFile, but such symlink isn't really functional. // Here we prefer to fail deleting such symlink and skip extracting // a file. if (!DelDir(Path)) #else if (!DelFile(Path)) #endif { ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory. return false; // Couldn't delete the symlink to replace it with directory. } } } LastChecked=SrcName; //#endif return true; } unrar/filestr.cpp000666 000000 000000 00000007766 15026203744 012556 0ustar00000000 000000 #include "rar.hpp" bool ReadTextFile( const std::wstring &Name, StringList *List, bool Config, bool AbortOnError, RAR_CHARSET SrcCharset, bool Unquote, bool SkipComments, bool ExpandEnvStr) { std::wstring FileName; if (Config) GetConfigName(Name,FileName,true,false); else FileName=Name; File SrcFile; if (!FileName.empty()) { 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); size_t DataSize=0,ReadSize; const int ReadBlock=4096; std::vector Data(ReadBlock); while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) { DataSize+=ReadSize; Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data. } // Set to really read size, so we can zero terminate it correctly. Data.resize(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.data(),DataSize); std::vector DataW(ReadBlock); if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) { Data.push_back(0); // Zero terminate. #if defined(_WIN_ALL) if (SrcCharset==RCH_OEM) OemToCharA((char *)Data.data(),(char *)Data.data()); #endif DataW.resize(Data.size()); CharToWide((char *)Data.data(),DataW.data(),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.resize(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. { std::wstring ExpName=CurStr; ExpandEnvironmentStr(ExpName); if (!ExpName.empty()) List->AddString(ExpName); Expanded=true; } #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.empty()) 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) { std::wstring DirName; DirName=FindMask; RemoveNameFromPath(DirName); if (DirName.empty()) DirName=L"."; std::string DirNameA; WideToChar(DirName,DirNameA); if ((dirp=opendir(DirNameA.c_str()))==NULL) { fd->Error=(errno!=ENOENT); return false; } } while (1) { std::wstring Name; struct dirent *ent=readdir(dirp); if (ent==NULL) return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; if (!CharToWide(std::string(ent->d_name),Name)) uiMsg(UIERROR_INVALIDNAME,L"",Name); if (CmpName(FindMask,Name,MATCH_NAMES)) { std::wstring FullName=FindMask; FullName.erase(GetNamePos(FullName)); if (FullName.size()+Name.size()>=MAXPATHSIZE) { uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); return false; } FullName+=Name; if (!FastFind(FullName,fd,GetSymLink)) { ErrHandler.OpenErrorMsg(FullName); continue; } fd->Name=FullName; break; } } #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); fd->IsLink=IsLink(fd->FileAttr); FirstCall=false; std::wstring NameOnly=PointToName(fd->Name); if (NameOnly==L"." || NameOnly==L"..") return Next(fd); return true; } bool FindFile::FastFind(const std::wstring &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); #elif defined(_UNIX) std::string FindMaskA; WideToChar(FindMask,FindMaskA); struct stat st; if (GetSymLink) { #ifdef SAVE_LINKS if (lstat(FindMaskA.c_str(),&st)!=0) #else if (stat(FindMaskA.c_str(),&st)!=0) #endif { fd->Error=(errno!=ENOENT); return false; } } else if (stat(FindMaskA.c_str(),&st)!=0) { fd->Error=(errno!=ENOENT); return false; } fd->FileAttr=st.st_mode; fd->Size=st.st_size; File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime); fd->Name=FindMask; #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 std::wstring &Mask,FindData *fd) { WIN32_FIND_DATA FindData; if (hFind==INVALID_HANDLE_VALUE) { hFind=FindFirstFile(Mask.c_str(),&FindData); if (hFind==INVALID_HANDLE_VALUE) { std::wstring LongMask; if (GetWinLongPath(Mask,LongMask)) hFind=FindFirstFile(LongMask.c_str(),&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) { fd->Name=Mask; SetName(fd->Name,FindData.cFileName); 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 00000002116 15026203744 012527 0ustar00000000 000000 #include "rar.hpp" BitInput::BitInput(bool AllocBuffer) { ExternalBuffer=false; if (AllocBuffer) { // getbits*() attempt to read data from InAddr, ... InAddr+8 positions. // So let's allocate 8 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 8 bytes, which contents we do not need. size_t BufSize=MAX_SIZE+8; 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=nullptr; } BitInput::~BitInput() { if (!ExternalBuffer) delete[] InBuf; } void BitInput::faddbits(uint Bits) { // Function wrapped version of inline addbits to reduce the code size. addbits(Bits); } uint BitInput::fgetbits() { // Function wrapped version of inline getbits to reduce the code size. return getbits(); } void BitInput::SetExternalBuffer(byte *Buf) { if (InBuf!=NULL && !ExternalBuffer) delete[] InBuf; InBuf=Buf; ExternalBuffer=true; } unrar/global.cpp000666 000000 000000 00000000122 15026203744 012321 0ustar00000000 000000 #define INCLUDEGLOBAL #ifdef _MSC_VER #pragma hdrstop #endif #include "rar.hpp" unrar/hardlinks.cpp000666 000000 000000 00000001664 15026203744 013054 0ustar00000000 000000 bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting) { if (!FileExist(NameExisting)) { uiMsg(UIERROR_HLINKCREATE,NameNew); uiMsg(UIERROR_NOLINKTARGET); ErrHandler.SetErrorCode(RARX_CREATE); return false; } CreatePath(NameNew,true,Cmd->DisableNames); #ifdef _WIN_ALL bool Success=CreateHardLink(NameNew.c_str(),NameExisting.c_str(),NULL)!=0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CREATE); } return Success; #elif defined(_UNIX) std::string NameExistingA,NameNewA; WideToChar(NameExisting,NameExistingA); WideToChar(NameNew,NameNewA); bool Success=link(NameExistingA.c_str(),NameNewA.c_str())==0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); ErrHandler.SysErrMsg(); ErrHandler.SetErrorCode(RARX_CREATE); } return Success; #else return false; #endif } unrar/hash.cpp000666 000000 000000 00000017404 15026203744 012017 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) const { 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 delete 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,HASH_POOL_THREADS); #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) { #ifdef RAR_SMP UpdateCRC32MT(Data,DataSize); #else CurCRC32=CRC32(CurCRC32,Data,DataSize); #endif } if (HashType==HASH_BLAKE2) { #ifdef RAR_SMP if (MaxThreads>1 && ThPool==nullptr) ThPool=new ThreadPool(HASH_POOL_THREADS); blake2ctx->ThPool=ThPool; blake2ctx->MaxThreads=MaxThreads; #endif blake2sp_update( blake2ctx, (byte *)Data, DataSize); } } #ifdef RAR_SMP THREAD_PROC(BuildCRC32Thread) { DataHash::CRC32ThreadData *td=(DataHash::CRC32ThreadData *)Data; // Use 0 initial value to simplify combining the result with existing CRC32. // It doesn't affect the first initial 0xffffffff in the data beginning. // If we used 0xffffffff here, we would need to shift 0xffffffff left to // block width and XOR it with block CRC32 to reset its initial value to 0. td->DataCRC=CRC32(0,td->Data,td->DataSize); } // CRC is linear and distributive over addition, so CRC(a+b)=CRC(a)+CRC(b). // Since addition in finite field is XOR, we have CRC(a^b)=CRC(a)^CRC(b). // So CRC(aaabbb) = CRC(aaa000) ^ CRC(000bbb) = CRC(aaa000) ^ CRC(bbb), // because CRC ignores leading zeroes. Thus to split CRC calculations // to "aaa" and "bbb" blocks and then to threads we need to be able to // find CRC(aaa000) knowing "aaa" quickly. We use Galois finite field to // calculate the power of 2 to get "1000" and multiply it by "aaa". void DataHash::UpdateCRC32MT(const void *Data,size_t DataSize) { const size_t MinBlock=0x4000; if (DataSize<2*MinBlock || MaxThreads<2) { CurCRC32=CRC32(CurCRC32,Data,DataSize); return; } if (ThPool==nullptr) ThPool=new ThreadPool(HASH_POOL_THREADS); size_t Threads=MaxThreads; size_t BlockSize=DataSize/Threads; if (BlockSizeAddTask(BuildCRC32Thread,(void*)&td[I]); #else BuildCRC32Thread((void*)&td[I]); #endif } #ifdef USE_THREADS ThPool->WaitDone(); #endif // USE_THREADS uint StdShift=gfExpCRC(uint(8*td[0].DataSize)); for (size_t I=0;I>=1) Reversed|=(N & 1)<<(31-I); return Reversed; } // Galois field multiplication modulo POLY. uint DataHash::gfMulCRC(uint A, uint B) { // For reversed 0xEDB88320 polynomial we bit reverse CRC32 before passing // to this function, so we must use the normal polynomial here. // We set the highest polynomial bit 33 for proper multiplication // in case uint is larger than 32-bit. const uint POLY=uint(0x104c11db7); uint R = 0 ; // Multiplication result. while (A != 0 && B != 0) // If any of multipliers becomes 0, quit early. { // For non-zero lowest B bit, add A to result. R ^= (B & 1)!=0 ? A : 0; // Make A twice larger before the next iteration. // Subtract POLY to keep it modulo POLY if high bit is set. A = (A << 1) ^ ((A & 0x80000000)!=0 ? POLY : 0); B >>= 1; // Move next B bit to lowest position. } return R; } // Calculate 2 power N with square-and-multiply algorithm. uint DataHash::gfExpCRC(uint N) { uint S = 2; // Starts from base value and contains the current square. uint R = 1; // Exponentiation result. while (N > 1) { if ((N & 1)!=0) // If N is odd. R = gfMulCRC(R, S); S = gfMulCRC(S, S); // Next square. N >>= 1; } // We could change the loop condition to N > 0 and return R at expense // of one additional gfMulCRC(S, S). return gfMulCRC(R, S); } 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); #ifndef RAR_NOCRYPT if (Key!=nullptr) ConvertHashToMAC(&Final,Key); #endif return Final==*CmpValue; } unrar/headers.cpp000666 000000 000000 00000001533 15026203744 012503 0ustar00000000 000000 #include "rar.hpp" void FileHeader::Reset(size_t SubDataSize) { SubData.resize(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() { *this={}; } unrar/isnt.cpp000666 000000 000000 00000004705 15026203745 012052 0ustar00000000 000000 #include "rar.hpp" 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; } // Replace it with documented Windows 11 check when available. #include #include #pragma comment(lib, "wbemuuid.lib") static bool WMI_IsWindows10() { IWbemLocator *pLoc = NULL; HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER, IID_IWbemLocator,(LPVOID *)&pLoc); if (FAILED(hres)) return false; IWbemServices *pSvc = NULL; hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,0,NULL,NULL,&pSvc); if (FAILED(hres)) { pLoc->Release(); return false; } hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL, RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); return false; } IEnumWbemClassObject *pEnumerator = NULL; hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres) || pEnumerator==NULL) { pSvc->Release(); pLoc->Release(); return false; } bool Win10=false; IWbemClassObject *pclsObj = NULL; ULONG uReturn = 0; pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (pclsObj!=NULL && uReturn>0) { VARIANT vtProp; pclsObj->Get(L"Name", 0, &vtProp, 0, 0); Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL; VariantClear(&vtProp); pclsObj->Release(); } pSvc->Release(); pLoc->Release(); pEnumerator->Release(); return Win10; } // Replace it with actual check when available. bool IsWindows11OrGreater() { static bool IsSet=false,IsWin11=false; if (!IsSet) { OSVERSIONINFO WinVer; WinVer.dwOSVersionInfoSize=sizeof(WinVer); GetVersionEx(&WinVer); IsWin11=WinVer.dwMajorVersion>10 || WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); IsSet=true; } return IsWin11; } unrar/largepage.cpp000666 000000 000000 00000011671 15026203745 013024 0ustar00000000 000000 #include "rar.hpp" /* To enable, disable or check Large Memory pages manually: - open "Local Security Policy" from "Start Menu"; - open "Lock Pages in Memory" in "Local Policies\User Rights Assignment"; - add or remove the user and sign out and sign in or restart Windows. */ #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL) #define ALLOW_LARGE_PAGES #endif LargePageAlloc::LargePageAlloc() { UseLargePages=false; #ifdef ALLOW_LARGE_PAGES PageSize=0; #endif } void LargePageAlloc::AllowLargePages(bool Allow) { #ifdef ALLOW_LARGE_PAGES if (Allow && PageSize==0) { HMODULE hKernel=GetModuleHandle(L"kernel32.dll"); if (hKernel!=nullptr) { typedef SIZE_T (*GETLARGEPAGEMINIMUM)(); GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum"); if (pGetLargePageMinimum!=nullptr) PageSize=pGetLargePageMinimum(); } if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME)) { UseLargePages=false; return; } } UseLargePages=Allow; #endif } bool LargePageAlloc::IsPrivilegeAssigned() { #ifdef ALLOW_LARGE_PAGES return SetPrivilege(SE_LOCK_MEMORY_NAME); #else return true; #endif } bool LargePageAlloc::AssignPrivilege() { #ifdef ALLOW_LARGE_PAGES HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) return false; // Get the required buffer size. DWORD BufSize=0; GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize); if (BufSize==0 || BufSize>1000000) // Sanity check for returned value. { CloseHandle(hToken); return false; } TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize); // Get the current user token information. if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0) { CloseHandle(hToken); return false; } // Get SID string for the current user. LPWSTR ApiSidStr; ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr); // Convert SID to C++ string and release API based buffer. std::wstring SidStr=ApiSidStr; LocalFree(ApiSidStr); CloseHandle(hToken); if (IsUserAdmin()) AssignPrivilegeBySid(SidStr); else { // Define here, so they survive until ShellExecuteEx call. std::wstring ExeName=GetModuleFileStr(); std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr; SHELLEXECUTEINFO shExecInfo{}; shExecInfo.cbSize = sizeof(shExecInfo); shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode. shExecInfo.lpVerb = L"runas"; shExecInfo.lpFile = ExeName.c_str(); shExecInfo.lpParameters = Param.c_str(); shExecInfo.nShow = SW_SHOWNORMAL; BOOL Result=ShellExecuteEx(&shExecInfo); } #endif return true; } bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid) { #ifdef ALLOW_LARGE_PAGES LSA_HANDLE PolicyHandle; LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it. #ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR. // We define STATUS_SUCCESS here instead of including ntstatus.h to avoid // macro redefinition warnings. We tried UMDF_USING_NTSTATUS define // and other workarounds, but it didn't help. const uint STATUS_SUCCESS=0; #endif if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT| POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS) return false; PSID UserSid; ConvertStringSidToSid(Sid.c_str(),&UserSid); LSA_UNICODE_STRING LsaString; LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME; // It must be in bytes, so multiple it to sizeof(wchar_t). LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]); LsaString.MaximumLength=LsaString.Length; bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS; LocalFree(UserSid); LsaClose(PolicyHandle); mprintf(St(MPrivilegeAssigned)); if (Ask(St(MYesNo)) == 1) Shutdown(POWERMODE_RESTART); return Success; #else return true; #endif } bool LargePageAlloc::AssignConfirmation() { #ifdef ALLOW_LARGE_PAGES mprintf(St(MLockInMemoryNeeded)); return Ask(St(MYesNo)) == 1; #else return false; #endif } void* LargePageAlloc::new_large(size_t Size) { void *Allocated=nullptr; #ifdef ALLOW_LARGE_PAGES if (UseLargePages && Size>=PageSize) { // VirtualAlloc fails if allocation size isn't multiple of page size. SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize; Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE); if (Allocated!=nullptr) LargeAlloc.push_back(Allocated); } #endif return Allocated; } bool LargePageAlloc::delete_large(void *Addr) { #ifdef ALLOW_LARGE_PAGES if (Addr!=nullptr) for (size_t I=0;ICommand[1]=='T'); bool ShowService=Technical && Cmd->Command[2]=='A'; bool Bare=(Cmd->Command[1]=='B'); bool Verbose=(Cmd->Command[0]=='V'); std::wstring ArcName; while (Cmd->GetArcName(ArcName)) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. Archive Arc(Cmd); if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; while (true) { 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.c_str()); mprintf(L"\n%s: ",St(MListDetails)); const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 1.5":L"RAR 5"); mprintf(L"%s", Fmt); if (Arc.Solid) mprintf(L", %s", St(MListSolid)); if (Arc.SFXSize>0) mprintf(L", %s", 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. mprintf(L", "); mprintf(St(MVolumeNumber),Arc.VolNumber+1); } else mprintf(L", %s", St(MListVolume)); if (Arc.Protected) mprintf(L", %s", St(MListRR)); if (Arc.Locked) mprintf(L", %s", St(MListLock)); if (Arc.Encrypted) mprintf(L", %s", St(MListEncHead)); if (!Arc.MainHead.OrigName.empty()) mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str()); if (Arc.MainHead.OrigTime.IsSet()) { wchar DateStr[50]; Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical); mprintf(L"\n%s: %s",St(MOriginalTime),DateStr); } mprintf(L"\n"); } wchar VolNumText[50]; *VolNumText=0; while (Arc.ReadHeader()>0) { Wait(); // Allow quit listing with Ctrl+C. 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,NULL,MATCH_WILDSUBPATH,0,NULL)!=0; if (FileMatched) { ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames); if (!Arc.FileHead.SplitBefore) { TotalUnpSize+=Arc.FileHead.UnpSize; FileCount++; } TotalPackSize+=Arc.FileHead.PackSize; } break; case HEAD_SERVICE: // For service blocks dependent on previous block, such as ACL // or NTFS stream, we use "file matched" flag of host file. // Independent blocks like RR are matched separately, // so we can list them by their name. Also we match even // dependent blocks separately if "vta -idn" are set. User may // want to see service blocks only in this case. if (!Arc.SubHead.SubBlock || Cmd->DisableNames) FileMatched=Cmd->IsProcessFile(Arc.SubHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0; if (FileMatched && !Bare) { // Here we set DisableNames parameter to true regardless of // Cmd->DisableNames. If "vta -idn" are set together, user // wants to see service blocks like RR only. if (Technical && ShowService) ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,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==VOLSIZE_AUTO && (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.c_str()); 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,bool DisableNames) { if (!TitleShown && !Technical && !Bare) { if (Verbose) { mprintf(L"\n%ls",St(MListTitleV)); if (!DisableNames) mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); } else { mprintf(L"\n%ls",St(MListTitleL)); if (!DisableNames) mprintf(L"\n----------- --------- ---------- ----- ----"); } // Must be set even in DisableNames mode to suppress "0 files" output // unless no files are matched. TitleShown=true; } if (DisableNames) return; const wchar *Name=hd.FileName.c_str(); RARFORMAT Format=Arc.Format; if (Bare) { mprintf(L"%s\n",Name); return; } wchar UnpSizeText[30],PackSizeText[30]; if (hd.UnpSize==INT64NDF) wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText)); 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) wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr)); else if (hd.SplitBefore) wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr)); else if (hd.SplitAfter) wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr)); else swprintf(RatioStr,ASIZE(RatioStr),L"%u%%",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)); std::wstring StreamName=GetStreamNameNTFS(Arc); mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName.c_str()); } 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) { std::string LinkTargetA; if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. LinkTargetA="*<-?->"; } else { size_t DataSize=(size_t)Min(hd.PackSize,MAXPATHSIZE); std::vector Buf(DataSize+1); Arc.Read(Buf.data(),DataSize); Buf[DataSize] = 0; LinkTargetA=Buf.data(); } std::wstring LinkTarget; CharToWide(LinkTargetA,LinkTarget); mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget.c_str()); } else mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName.c_str()); } 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 (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_RR)) { // Display the original -rrN percent if available. int RecoveryPercent=Arc.GetRecoveryPercent(); if (RecoveryPercent>0) // It can be -1 if failed to detect. mprintf(L"\n%12ls: %u%%",L"RR%", RecoveryPercent); } } bool WinTitles=false; #ifdef _WIN_ALL WinTitles=true; #endif if (hd.mtime.IsSet()) mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr); if (hd.ctime.IsSet()) { hd.ctime.GetText(DateStr,ASIZE(DateStr),true); mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr); } if (hd.atime.IsSet()) { hd.atime.GetText(DateStr,ASIZE(DateStr),true); mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed: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) { std::wstring BlakeStr; BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,BlakeStr); mprintf(L"\n%12ls: %ls", hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", BlakeStr.c_str()); } 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=1024) WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"k"; else WinSize=L" -md=?"; mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo), Format==RARFMT15 ? L"1.5":L"5.0", hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,WinSize.c_str()); 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(hd.FileName,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).c_str()); else if (hd.UnixOwnerNumeric) mprintf(L"#%d",hd.UnixOwnerID); mprintf(L":"); if (*hd.UnixGroupName!=0) mprintf(L"%ls",GetWide(hd.UnixGroupName).c_str()); else 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(hd.Dir ? L" ":L"???????? "); // Missing checksum is ok for folder, not for file. } mprintf(L"%ls",Name); } 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: wcsncpyz(AttrStr,L"?",AttrSize); break; } } unrar/log.cpp000666 000000 000000 00000001045 15026203745 011650 0ustar00000000 000000 #include "rar.hpp" void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet) { } void CloseLogOptions() { } #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); va_list arglist; va_start(arglist,fmt); std::wstring s=vwstrprintf(fmt,arglist); ReplaceEsc(s); va_end(arglist); eprintf(L"%ls",s.c_str()); ErrHandler.SetSystemErrorCode(Code); } #endif unrar/match.cpp000666 000000 000000 00000011313 15026203745 012162 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); static bool IsWildcard(const wchar *Str,size_t CheckSize); 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,uint CmpMode) { bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0; CmpMode&=MATCH_MODEMASK; wchar *Name1=PointToName(Wildcard); wchar *Name2=PointToName(Name); if (CmpMode!=MATCH_NAMES) { size_t WildLength=wcslen(Wildcard); if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD && mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0) { // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD, // "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; // 2023.08.29: We tried std::wstring Path1 and Path2 here, but performance // impact for O(n^2) complexity loop in CmdExtract::AnalyzeArchive() // was rather noticeable, 1.7s instead of 0.9s when extracting ~300 files // with @listfile from archive with ~7000 files. // This function can be invoked from other O(n^2) loops. So for now // we prefer to avoid wstring and use pointers and path sizes here. // Another option could be using std::wstring_view. size_t Path1Size=Name1-Wildcard; size_t Path2Size=Name2-Name; if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && (Path1Size!=Path2Size || mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)) return false; if (CmpMode==MATCH_ALLWILD) return match(Wildcard,Name,ForceCase); if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH) if (IsWildcard(Wildcard,Path1Size)) return match(Wildcard,Name,ForceCase); else if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard)) { if (Path1Size>0 && mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0) return false; } else if (Path1Size!=Path2Size || mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=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 } bool IsWildcard(const wchar *Str,size_t CheckSize) { size_t CheckPos=0; #ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\') CheckPos+=4; #endif for (size_t I=CheckPos;I 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 += (ushort)cf; } p=pc->U.Stats+ns1; p->Successor=Successor; p->Symbol = fs.Symbol; p->Freq = (byte)cf; pc->NumStats=(ushort)++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; // 2022.12.02: we removed pps-- here and changed the code below to avoid // "array subscript -1 is outside array bounds" warning in some compilers. do { if (pps>=ps+ASIZE(ps)) // Extra safety check. return false; Model->CharMask[(*pps)->Symbol]=Model->EscCount; pps++; } while ( --i ); psee2c->Summ += (ushort)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/motw.cpp000666 000000 000000 00000007264 15026203745 012066 0ustar00000000 000000 #include "rar.hpp" /* Zone.Identifier stream can include the text like: [ZoneTransfer] ZoneId=3 HostUrl=https://site/path/file.ext ReferrerUrl=d:\path\archive.ext Where ZoneId can be: 0 = My Computer 1 = Local intranet 2 = Trusted sites 3 = Internet 4 = Restricted sites */ MarkOfTheWeb::MarkOfTheWeb() { ZoneIdValue=-1; // -1 indicates the missing MOTW. AllFields=false; } void MarkOfTheWeb::Clear() { ZoneIdValue=-1; } void MarkOfTheWeb::ReadZoneIdStream(const std::wstring &FileName,bool AllFields) { MarkOfTheWeb::AllFields=AllFields; ZoneIdValue=-1; ZoneIdStream.clear(); std::wstring StreamName=FileName+MOTW_STREAM_NAME; File SrcFile; if (SrcFile.Open(StreamName)) { ZoneIdStream.resize(MOTW_STREAM_MAX_SIZE); int BufSize=SrcFile.Read(&ZoneIdStream[0],ZoneIdStream.size()); ZoneIdStream.resize(BufSize<0 ? 0:BufSize); if (BufSize<=0) return; ZoneIdValue=ParseZoneIdStream(ZoneIdStream); } } // 'Stream' contains the raw "Zone.Identifier" NTFS stream data on input // and either raw or cleaned stream data on output. int MarkOfTheWeb::ParseZoneIdStream(std::string &Stream) { if (Stream.rfind("[ZoneTransfer]",0)==std::string::npos) return -1; // Not a valid Mark of the Web. Prefer the archive MOTW if any. std::string::size_type ZoneId=Stream.find("ZoneId=",0); if (ZoneId==std::string::npos || !IsDigit(Stream[ZoneId+7])) return -1; // Not a valid Mark of the Web. int ZoneIdValue=atoi(&Stream[ZoneId+7]); if (ZoneIdValue<0 || ZoneIdValue>4) return -1; // Not a valid Mark of the Web. if (!AllFields) Stream="[ZoneTransfer]\r\nZoneId=" + std::to_string(ZoneIdValue) + "\r\n"; return ZoneIdValue; } void MarkOfTheWeb::CreateZoneIdStream(const std::wstring &Name,StringList &MotwList) { if (ZoneIdValue==-1) return; size_t ExtPos=GetExtPos(Name); const wchar *Ext=ExtPos==std::wstring::npos ? L"":&Name[ExtPos+1]; bool Matched=false; const wchar *CurMask; MotwList.Rewind(); while ((CurMask=MotwList.GetString())!=nullptr) { // Perform the fast extension comparison for simple *.ext masks. // Also we added the fast path to wcsicomp for English only strings. // When extracting 100000 files with "Exe and office" masks set // this loop spent 85ms with this optimization and wcsicomp optimized // for English strings, 415ms with this optimization only, 475ms with // wcsicomp optimized only and 795ms without both optimizations. bool FastCmp=CurMask[0]=='*' && CurMask[1]=='.' && wcspbrk(CurMask+2,L"*?")==NULL; if (FastCmp && wcsicomp(Ext,CurMask+2)==0 || !FastCmp && CmpName(CurMask,Name,MATCH_NAMES)) { Matched=true; break; } } if (!Matched) return; std::wstring StreamName=Name+MOTW_STREAM_NAME; File StreamFile; if (StreamFile.Create(StreamName)) // Can fail on FAT. { // We got a report that write to stream failed on Synology 2411+ NAS drive. // So we handle it silently instead of aborting. StreamFile.SetExceptions(false); if (StreamFile.Write(&ZoneIdStream[0],ZoneIdStream.size())) StreamFile.Close(); } } bool MarkOfTheWeb::IsNameConflicting(const std::wstring &StreamName) { // We must use the case insensitive comparison for L":Zone.Identifier" // to catch specially crafted archived streams like L":zone.identifier". return wcsicomp(StreamName,MOTW_STREAM_NAME)==0 && ZoneIdValue!=-1; } // Return true and prepare the file stream to write if its ZoneId is stricter // than archive ZoneId. If it is missing, less or equally strict, return false. bool MarkOfTheWeb::IsFileStreamMoreSecure(std::string &FileStream) { int StreamZone=ParseZoneIdStream(FileStream); return StreamZone>ZoneIdValue; } unrar/options.cpp000666 000000 000000 00000000732 15026203745 012564 0ustar00000000 000000 #include "rar.hpp" RAROptions::RAROptions() { Init(); } void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); WinSize=0x2000000; WinSizeLimit=0x100000000; Overwrite=OVERWRITE_DEFAULT; Method=3; MsgStream=MSG_STDOUT; ConvertNames=NAMES_ORIGINALCASE; xmtime=EXTTIME_MAX; 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 00000075070 15026203745 012360 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!=0 && IsDriveDiv(Path[1])) ? Path+2:Path); } std::wstring PointToName(const std::wstring &Path) { return std::wstring(Path.substr(GetNamePos(Path))); } size_t GetNamePos(const std::wstring &Path) { for (int I=(int)Path.size()-1;I>=0;I--) if (IsPathDiv(Path[I])) return I+1; return IsDriveLetter(Path) ? 2 : 0; } wchar* PointToLastChar(const wchar *Path) { size_t Length=wcslen(Path); return (wchar*)(Length>0 ? Path+Length-1:Path); } wchar GetLastChar(const std::wstring &Path) { return Path.empty() ? 0:Path.back(); } size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath) { const std::wstring &S=*SrcPath; // To avoid *SrcPath[] everywhere. size_t DestPos=0; // Prevent \..\ in any part of path string and \.. at the end of string for (size_t I=0;I:\ and any sequence of . and \ in the beginning of path string. while (DestPos='A' && Letter<='Z' && IsDriveDiv(Path[1]); } int GetPathDisk(const std::wstring &Path) { if (IsDriveLetter(Path)) return etoupperw(Path[0])-'A'; else return -1; } void AddEndSlash(std::wstring &Path) { if (!Path.empty() && Path.back()!=CPATHDIVIDER) Path+=CPATHDIVIDER; } void MakeName(const std::wstring &Path,const std::wstring &Name,std::wstring &Pathname) { // 'Path', 'Name' and 'Pathname' can point to same string. So we use // the temporary buffer instead of constructing the name in 'Pathname'. std::wstring OutName=Path; // Do not add slash to d:, we want to allow relative paths like d:filename. if (!IsDriveLetter(Path) || Path.size()>2) AddEndSlash(OutName); OutName+=Name; Pathname=OutName; } // Returns the file path including the trailing path separator symbol. // It is allowed for both parameters to point to the same string. void GetPathWithSep(const std::wstring &FullName,std::wstring &Path) { if (std::addressof(FullName)!=std::addressof(Path)) Path=FullName; Path.erase(GetNamePos(FullName)); } // Removes name and returns file path without the trailing path separator. // But for names like d:\name return d:\ with trailing path separator. void RemoveNameFromPath(std::wstring &Path) { auto NamePos=GetNamePos(Path); if (NamePos>=2 && (!IsDriveDiv(Path[1]) || NamePos>=4)) NamePos--; Path.erase(NamePos); } #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool GetAppDataPath(std::wstring &Path,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; Path.clear(); bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathStrFromIDList(ppidl,Path) && !Path.empty()) { AddEndSlash(Path); Path+=L"WinRAR"; Success=FileExist(Path); if (!Success && Create) Success=CreateDir(Path); } g_pMalloc->Free(ppidl); return Success; } #endif #if defined(_WIN_ALL) bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path) { std::vector Buf(MAX_PATH); bool Success=SHGetPathFromIDList(pidl,Buf.data())!=FALSE; Path=Buf.data(); return Success; } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void GetRarDataPath(std::wstring &Path,bool Create) { Path.clear(); // This option to change %AppData% location is documented in wunrar.chm. HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { DWORD DataSize; LSTATUS Code=RegQueryValueEx(hKey,L"AppData",NULL,NULL,NULL,&DataSize); if (Code==ERROR_SUCCESS) { std::vector PathBuf(DataSize/sizeof(wchar)); RegQueryValueEx(hKey,L"AppData",0,NULL,(BYTE *)PathBuf.data(),&DataSize); Path=PathBuf.data(); RegCloseKey(hKey); } } if (Path.empty() || !FileExist(Path)) if (!GetAppDataPath(Path,Create)) { Path=GetModuleFileStr(); RemoveNameFromPath(Path); } } #endif #ifndef SFX_MODULE bool EnumConfigPaths(uint Number,std::wstring &Path,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); else Path=ConfPath[0]; return true; } Number--; if (Number>=ASIZE(ConfPath)) return false; Path=ConfPath[Number]; return true; #elif defined(_WIN_ALL) if (Number>1) return false; if (Number==0) GetRarDataPath(Path,Create); else { Path=GetModuleFileStr(); RemoveNameFromPath(Path); } return true; #else return false; #endif } #endif #ifndef SFX_MODULE void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create) { FullName.clear(); for (uint I=0;;I++) { std::wstring ConfPath; if (!EnumConfigPaths(I,ConfPath,Create)) break; MakeName(ConfPath,Name,FullName); if (!CheckExist || WildFileExist(FullName)) break; } } #endif // Returns the position to rightmost digit of volume number or beginning // of file name if numeric part is missing. size_t GetVolNumPos(const std::wstring &ArcName) { // We do not want to increment any characters in path component. size_t NamePos=GetNamePos(ArcName); if (NamePos==ArcName.size()) return NamePos; // Pointing to last name character. size_t Pos=ArcName.size()-1; // Skipping the archive extension. while (!IsDigit(ArcName[Pos]) && Pos>NamePos) Pos--; // Skipping the numeric part of name. size_t NumPos=Pos; while (IsDigit(ArcName[NumPos]) && NumPos>NamePos) NumPos--; // Searching for first numeric part in names like name.part##of##.rar. // Stop search on the first dot. while (NumPos>NamePos && ArcName[NumPos]!='.') { if (IsDigit(ArcName[NumPos])) { // Validate the first numeric part only if it has a dot somewhere // before it. auto DotPos=ArcName.find('.',NamePos); if (DotPos!=std::wstring::npos && DotPos|\"")==std::wstring::npos; } void MakeNameUsable(std::wstring &Name,bool Extended) { for (size_t I=0;I|\"":L"?*",Name[I])!=NULL || Extended && (uint)Name[I]<32) Name[I]='_'; #ifdef _UNIX // We were asked to apply Windows-like conversion in Linux in case // files are unpacked to Windows share. This code is invoked only // if file failed to be created, so it doesn't affect extraction // of Unix compatible names to native Unix drives. if (Extended) { // Windows shares in Unix do not allow the drive letter, // so unlike Windows version, we check all characters here. if (Name[I]==':') Name[I]='_'; // No spaces or dots before the path separator are allowed on Windows // shares. But they are allowed and automatically removed at the end of // file or folder name, so we need to replace them only before // the path separator, but not at the end of file name. // Since such files or folders are created successfully, a supposed // conversion at the end of file name would never be invoked here. // While converting dots, we preserve "." and ".." path components, // such as when specifying ".." in the destination path. if (IsPathDiv(Name[I+1]) && (Name[I]==' ' || Name[I]=='.' && I>0 && !IsPathDiv(Name[I-1]) && (Name[I-1]!='.' || I>1 && !IsPathDiv(Name[I-2])))) Name[I]='_'; } #else if (I>1 && Name[I]==':') Name[I]='_'; #endif } } void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) { size_t Copied=0; for (;Copied FullName(Code); Code=GetFullPathName(Src.c_str(),(DWORD)FullName.size(),FullName.data(),NULL); if (Code>0 && Code<=FullName.size()) { Dest=FullName.data(); return; } } std::wstring LongName; if (GetWinLongPath(Src,LongName)) // Failed with normal name, try long. { Code=GetFullPathName(LongName.c_str(),0,NULL,NULL); // Get the buffer size. if (Code!=0) { std::vector FullName(Code); Code=GetFullPathName(LongName.c_str(),(DWORD)FullName.size(),FullName.data(),NULL); if (Code>0 && Code<=FullName.size()) { Dest=FullName.data(); return; } } } if (Src!=Dest) Dest=Src; // Copy source to destination in case of failure. } #elif defined(_UNIX) if (IsFullPath(Src)) Dest.clear(); else { std::vector CurDirA(MAXPATHSIZE); if (getcwd(CurDirA.data(),CurDirA.size())==NULL) CurDirA[0]=0; CharToWide(CurDirA.data(),Dest); AddEndSlash(Dest); } Dest+=Src; #else Dest=Src; #endif } bool IsFullPath(const std::wstring &Path) { #ifdef _WIN_ALL return Path.size()>=2 && Path[0]=='\\' && Path[1]=='\\' || Path.size()>=3 && IsDriveLetter(Path) && IsPathDiv(Path[2]); #else return Path.size()>=1 && IsPathDiv(Path[0]); #endif } bool IsFullRootPath(const std::wstring &Path) { return IsFullPath(Path) || IsPathDiv(Path[0]); } // Both source and destination can point to the same string. void GetPathRoot(const std::wstring &Path,std::wstring &Root) { if (IsDriveLetter(Path)) Root=Path.substr(0,2) + L"\\"; else if (Path[0]=='\\' && Path[1]=='\\') { size_t Slash=Path.find('\\',2); if (Slash!=std::wstring::npos) { size_t Length; if ((Slash=Path.find('\\',Slash+1))!=std::wstring::npos) Length=Slash+1; else Length=Path.size(); Root=Path.substr(0,Length); } } else Root.clear(); } int ParseVersionFileName(std::wstring &Name,bool Truncate) { int Version=0; auto VerPos=Name.rfind(';'); if (VerPos!=std::wstring::npos && VerPos+10;Pos--) if (IsDigit(Name[Pos])) { Name[Pos]=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else if (N=='0') // If we already set the rightmost '1' before. { VolNumStart=Pos+1; // Store the position of leftmost digit in volume number. break; } } else { // Old volume numbering scheme. Just set the extension to ".rar". SetExt(Name,L"rar"); VolNumStart=GetExtPos(Name); } if (!FileExist(Name)) { // If the first volume, which name we just generated, does 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. std::wstring Mask=Name; SetExt(Mask,L"*"); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { Name=FD.Name; break; } } } FirstName=Name; return VolNumStart; } #endif #ifndef SFX_MODULE static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,uint ArcNumber,bool &ArcNumPresent) { size_t Pos=0; bool Prefix=false; if (GenerateMask[0]=='+') { Prefix=true; // Add the time string before the archive name. Pos++; // Skip '+' in the beginning of time mask. } // Set the default mask for -ag or -ag+, use the specified otherwise. std::wstring Mask=GenerateMask.size()>Pos ? GenerateMask.substr(Pos):L"yyyymmddhhmmss"; bool QuoteMode=false; uint MAsMinutes=0; // By default we treat 'M' as months. for (uint I=0;I0 && 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'; MAsMinutes--; } 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++; const size_t FieldSize=11; char Field[10][FieldSize]; snprintf(Field[0],FieldSize,"%04u",rlt.Year); snprintf(Field[1],FieldSize,"%02u",rlt.Month); snprintf(Field[2],FieldSize,"%02u",rlt.Day); snprintf(Field[3],FieldSize,"%02u",rlt.Hour); snprintf(Field[4],FieldSize,"%02u",rlt.Minute); snprintf(Field[5],FieldSize,"%02u",rlt.Second); snprintf(Field[6],FieldSize,"%02u",(uint)CurWeek); snprintf(Field[7],FieldSize,"%u",(uint)WeekDay+1); snprintf(Field[8],FieldSize,"%03u",rlt.yDay+1); snprintf(Field[9],FieldSize,"%05u",ArcNumber); const wchar *MaskChars=L"YMDHISWAEN"; // How many times every modifier character was encountered in the mask. int CField[sizeof(Field)/sizeof(Field[0])]{}; QuoteMode=false; for (uint I=0;I1) { // 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. NewName=ArcName; GenArcName(NewName,GenerateMask,ArcNumber-1,ArcNumPresent); } break; } ArcNumber++; } ArcName=NewName; } #endif #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 std::wstring &Src,std::wstring &Dest) { if (Src.empty()) return false; const std::wstring Prefix=L"\\\\?\\"; bool FullPath=Src.size()>=3 && IsDriveLetter(Src) && IsPathDiv(Src[2]); if (IsFullPath(Src)) // Paths in d:\path\name format. { if (IsDriveLetter(Src)) { Dest=Prefix+Src; // "\\?\D:\very long path". return true; } else if (Src.size()>2 && Src[0]=='\\' && Src[1]=='\\') { Dest=Prefix+L"UNC"+Src.substr(1); // "\\?\UNC\server\share". return true; } // We can be here only if modify IsFullPath() in the future. return false; } else { std::wstring CurDir; if (!GetCurDir(CurDir)) return false; if (IsPathDiv(Src[0])) // Paths in \path\name format. { Dest=Prefix+CurDir[0]+L':'+Src; // Copy drive letter 'd:'. return true; } else // Paths in path\name format. { Dest=Prefix+CurDir; AddEndSlash(Dest); size_t Pos=0; if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname. Pos=2; Dest+=Src.substr(Pos); return true; } } return false; } // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. void ConvertToPrecomposed(std::wstring &Name) { if (WinNT() FileName(Size); if (FoldString(MAP_PRECOMPOSED,Name.c_str(),-1,FileName.data(),(int)FileName.size())!=0) Name=FileName.data(); } void MakeNameCompatible(std::wstring &Name) { // Remove trailing spaces and dots in file name and in dir names in path. for (int I=0;I<(int)Name.size();I++) if (I+1==Name.size() || IsPathDiv(Name[I+1])) while (I>=0 && (Name[I]=='.' || Name[I]==' ')) { if (Name[I]=='.') { // 2024.05.01: Permit ./path1, path1/./path2, ../path1, // path1/../path2 and exotic Win32 d:.\path1, d:..\path1 paths // requested by user. Leading dots are possible here if specified // by user in the destination path. if (I==0 || IsPathDiv(Name[I-1]) || I==2 && IsDriveLetter(Name)) break; if (I>=1 && Name[I-1]=='.' && (I==1 || IsPathDiv(Name[I-2]) || I==3 && IsDriveLetter(Name))) break; } Name[I]='_'; break; } // Rename reserved device names, such as aux.txt to _aux.txt. // We check them in path components too, where they are also prohibited. for (size_t I=0;I0 && IsPathDiv(Name[I-1])) { static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"}; const wchar *s=&Name[I]; bool MatchFound=false; for (uint J=0;J Path(256); while (Path.size()<=MAXPATHSIZE) { if (GetModuleFileName(hModule,Path.data(),(DWORD)Path.size()) Buf(BufSize); DWORD Code=GetCurrentDirectory((DWORD)Buf.size(),Buf.data()); Dir=Buf.data(); return Code!=0; } #endif unrar/qopen.cpp000666 000000 000000 00000015401 15026203745 012212 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; int64 SavePos=SeekPos; 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)) { Arc->Seek(SavePos,SEEK_SET); return; } QOHeaderPos=Arc->CurBlockPos; RawDataStart=Arc->Tell(); RawDataSize=Arc->SubHead.UnpSize; Arc->Seek(SavePos,SEEK_SET); Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader. } if (Arc->SubHead.Encrypted) { CommandData *Cmd=Arc->GetCommandData(); #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.clear(); 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() { int64 SavePos=Arc->Tell(); 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; int ReadSize=0; if (SizeToRead!=0) { ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); if (ReadSize<=0) ReadSize=0; else { #ifndef RAR_NOCRYPT if (Arc->SubHead.Encrypted) Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); #endif RawDataPos+=ReadSize; ReadBufSize+=ReadSize; } } Arc->Seek(SavePos,SEEK_SET); 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.resize(HeaderSize); Raw.GetB(LastReadHeader.data(),HeaderSize); // Calculate the absolute position as offset from quick open service header. LastReadHeaderPos=QOHeaderPos-Offset; return true; } unrar/rar.cpp000666 000000 000000 00000004751 15026203745 011662 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 std::wstring ModuleName; #ifdef _WIN_ALL ModuleName=GetModuleFileStr(); #else CharToWide(argv[0],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 { // Use std::unique_ptr to free Cmd in case of exception. std::unique_ptr Cmd(new CommandData); #ifdef SFX_MODULE 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': // Also copy 't' and 'a' modifiers for -v[t,a], if present. Cmd->Command.clear(); for (char *c=Switch+1;*c!=0;c++) Cmd->Command+=etoupper(*c); 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; if (ShutdownOnClose!=POWERMODE_KEEP) ShutdownCheckAnother(true); #endif uiInit(Cmd->Sound); InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset); ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL); Cmd->OutTitle(); Cmd->ProcessCommand(); } 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() && !ShutdownCheckAnother(false)) Shutdown(ShutdownOnClose); #endif ErrHandler.MainExit=true; CloseLogOptions(); return ErrHandler.GetErrorCode(); } #endif unrar/rarpch.cpp000666 000000 000000 00000000131 15026203745 012341 0ustar00000000 000000 // We use rarpch.cpp to create precompiled headers for MS Visual C++. #include "rar.hpp" unrar/rarvm.cpp000666 000000 000000 00000023007 15026203745 012220 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; PrevByte=DestData[I]=(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 00000007510 15026203745 012517 0ustar00000000 000000 #include "rar.hpp" RawRead::RawRead() { RawRead::SrcFile=nullptr; Reset(); } RawRead::RawRead(File *SrcFile) { RawRead::SrcFile=SrcFile; Reset(); } void RawRead::Reset() { Data.clear(); 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.resize(FullSize+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.resize(Data.size()+Size); ReadSize=SrcFile->Read(&Data[DataSize],Size); DataSize+=ReadSize; } return ReadSize; } void RawRead::Read(byte *SrcData,size_t Size) { if (Size!=0) { Data.resize(Data.size()+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>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count; if (SizeToRead > 0) { if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft) { // 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 UnpPackedLeft-=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 && UnpPackedLeft == 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->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize); if (ReadSize!=-1) { ReadSize=TotalRead; #ifndef RAR_NOCRYPT if (Decryption) Decrypt->DecryptBlock(Addr,ReadSize); #endif } Wait(); return ReadSize; } void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { #ifdef RARDLL CommandData *Cmd=((Archive *)SrcFile)->GetCommandData(); 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) { int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); 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(); } void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) { if (ShowProgress && SrcFile!=NULL) { // Important when processing several archives or multivolume archive. ArcPos+=ProcessedArcSize; Archive *SrcArc=(Archive *)SrcFile; CommandData *Cmd=SrcArc->GetCommandData(); 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; } // Return true if encryption or decryption mode is set correctly. bool ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, SecPassword *Password,const byte *Salt,const byte *InitV, uint Lg2Cnt,byte *HashKey,byte *PswCheck) { #ifdef RAR_NOCRYPT return false; #else if (Encrypt) { Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); return Encryption; } else { Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); return Decryption; } #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; } // Extraction progress is based on the position in archive and we adjust // the total archives size here, so trailing blocks do not prevent progress // reaching 100% at the end of extraction. Alternatively we could print "100%" // after completing the entire archive extraction, but then we would need // to take into account possible messages like the checksum error after // last file percent progress. void ComprDataIO::AdjustTotalArcSize(Archive *Arc) { // If we know a position of QO or RR blocks, use them to adjust the total // packed size to beginning of these blocks. Earlier we already calculated // the total size based on entire archive sizes. We also set LastArcSize // to start of first trailing block, to add it later to ProcessedArcSize. uint64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; // QO is always preceding RR record. // Also we check QO and RR to be less than archive length to prevent // negative "ArcLength-LastArcSize" and possible signed integer overflow // when calculating TotalArcSize. if (Arc->MainHead.QOpenOffset>0 && Arc->MainHead.QOpenOffsetMainHead.QOpenOffset; else if (Arc->MainHead.RROffset>0 && Arc->MainHead.RROffsetMainHead.RROffset; else { // If neither QO nor RR are found, exclude the approximate size of // end of archive block. // We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC, // but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file // name, so we do not have two files with 100% at the end of archive. const uint EndBlock=23; if (ArcLength>EndBlock) LastArcSize=ArcLength-EndBlock; } TotalArcSize-=ArcLength-LastArcSize; } unrar/recvol.cpp000666 000000 000000 00000004614 15026203745 012366 0ustar00000000 000000 #include "rar.hpp" #include "recvol3.cpp" #include "recvol5.cpp" bool RecVolumesRestore(CommandData *Cmd,const std::wstring &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(Cmd,false); return RecVol.Restore(Cmd,Name,Silent); } else { RecVolumes5 RecVol(Cmd,false); return RecVol.Restore(Cmd,Name,Silent); } } void RecVolumesTest(CommandData *Cmd,Archive *Arc,const std::wstring &Name) { std::wstring RevName; if (Arc==NULL) RevName=Name; else { // We received .rar or .exe volume as a parameter, trying to find // the matching .rev file number 1. bool NewNumbering=Arc->NewNumbering; std::wstring RecVolMask; size_t VolNumStart=VolNameToFirstName(Name,RecVolMask,NewNumbering); RecVolMask.replace(VolNumStart, std::wstring::npos, L"*.rev"); FindFile Find; Find.SetMask(RecVolMask); FindData RecData; while (Find.Next(&RecData)) { size_t NumPos=GetVolNumPos(RecData.Name); if (RecData.Name[NumPos]!='1') // Name must have "0...01" numeric part. continue; bool FirstVol=true; while (NumPos>0 && IsDigit(RecData.Name[--NumPos])) if (RecData.Name[NumPos]!='0') { FirstVol=false; break; } if (FirstVol) { RevName=RecData.Name; break; } } if (RevName.empty()) // First .rev file not found. return; } File RevFile; if (!RevFile.Open(RevName)) { ErrHandler.OpenErrorMsg(RevName); // 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(Cmd,true); RecVol.Test(Cmd,RevName); } else { RecVolumes3 RecVol(Cmd,true); RecVol.Test(Cmd,RevName); } } unrar/recvol3.cpp000666 000000 000000 00000031407 15026203745 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(RSDecodeThread) { RSEncode *rs=(RSEncode *)Data; rs->DecodeBuf(); } #endif RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly) { memset(SrcFile,0,sizeof(SrcFile)); if (TestOnly) { #ifdef RAR_SMP RSThreadPool=NULL; #endif } else { Buf.resize(TotalBufferSize); #ifdef RAR_SMP RSThreadPool=new ThreadPool(Cmd->Threads); #endif } } RecVolumes3::~RecVolumes3() { for (size_t I=0;I0;ExtPos--) if (!IsDigit(Name[ExtPos])) if (Name[ExtPos]=='_' && IsDigit(Name[ExtPos-1])) DigitGroup++; else break; return DigitGroup<2; } bool RecVolumes3::Restore(CommandData *Cmd,const std::wstring &Name,bool Silent) { std::wstring ArcName=Name; bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10. bool RevName=CmpExt(ArcName,L"rev"); if (RevName) { NewStyle=IsNewStyleRev(ArcName); size_t ExtPos=GetExtPos(ArcName); while (ExtPos>1 && (IsDigit(ArcName[ExtPos-1]) || ArcName[ExtPos-1]=='_')) ExtPos--; ArcName.replace(ExtPos,std::wstring::npos,L"*.*"); FindFile Find; Find.SetMask(ArcName); FindData fd; while (Find.Next(&fd)) { Archive Arc(Cmd); if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) { ArcName=fd.Name; 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(); size_t VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering); std::wstring RecVolMask=ArcName; RecVolMask.replace(VolNumStart,std::wstring::npos,L"*.rev"); size_t BaseNamePartLength=VolNumStart; 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; std::wstring PrevName; while (Find.Next(&RecData)) { std::wstring CurName=RecData.Name; int P[3]; if (!RevName && !NewStyle) { NewStyle=true; size_t DotPos=GetExtPos(CurName); if (DotPos!=std::wstring::npos) { uint LineCount=0; DotPos--; while (DotPos>0 && CurName[DotPos]!='.') { if (CurName[DotPos]=='_') LineCount++; DotPos--; } 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 { size_t DotPos=GetExtPos(CurName); if (DotPos==std::wstring::npos) continue; bool WrongParam=false; for (size_t I=0;I=BaseNamePartLength); P[I]=atoiw(&CurName[DotPos+1]); if (P[I]==0 || P[I]>255) WrongParam=true; } if (WrongParam) continue; } if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>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]; PrevName=CurName; File *NewFile=new File; NewFile->TOpen(CurName); // This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255 // checks above. Still we keep it here for better clarity and security. int SrcPos=FileNumber+P[0]-1; if (SrcPos<0 || SrcPos>=ASIZE(SrcFile)) continue; SrcFile[SrcPos]=NewFile; FoundRecVolumes++; if (RecFileSize==0) RecFileSize=NewFile->FileLength(); } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return false; bool WriteFlags[256]{}; std::wstring LastVolName; 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(); std::wstring NewName=ArcName+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) LastVolName=ArcName; uiMsg(UIMSG_MISSINGVOL,ArcName); uiMsg(UIEVENT_NEWARCHIVE,ArcName); } SrcFile[CurArcNum]=(File*)NewFile; NextVolumeName(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; #else uint ThreadNumber=1; #endif RSEncode *rse=new RSEncode[ThreadNumber]; 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); } delete[] rse; 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.empty()) { // 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,false); } } unrar/recvol5.cpp000666 000000 000000 00000032573 15026203745 012460 0ustar00000000 000000 static const uint MaxVolumes=65535; // We select this limit arbitrarily, to prevent user creating too many // rev files by mistake. #define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly) { RealBuf=NULL; RealReadBuffer=NULL; DataCount=0; RecCount=0; TotalCount=0; RecBufferSize=0; #ifdef RAR_SMP MaxUserThreads=Cmd->Threads; #else MaxUserThreads=1; #endif ThreadData=new RecRSThreadData[MaxUserThreads]; for (uint I=0;IRecRSPtr->ProcessAreaRS(td); } #endif void RecVolumes5::ProcessRS(CommandData *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;IRS==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(CommandData *Cmd,const std::wstring &Name,bool Silent) { std::wstring ArcName=Name; size_t NumPos=GetVolNumPos(ArcName); while (NumPos>0 && IsDigit(ArcName[NumPos-1])) NumPos--; if (NumPos<=GetNamePos(ArcName)) return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. ArcName.replace(NumPos,std::wstring::npos,L"*.*"); std::wstring FirstVolName; std::wstring LongestRevName; 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 (!fd.IsDir && 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++; if (fd.Name.size()>LongestRevName.size()) LongestRevName=fd.Name; } } 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. size_t NumPos=GetVolNumPos(fd.Name); uint VolNum=0; for (uint K=1;(int)NumPos>=0 && IsDigit(fd.Name[NumPos]);K*=10,NumPos--) VolNum+=(fd.Name[NumPos]-'0')*K; if (VolNum==0 || VolNum>MaxVolumes) continue; size_t CurSize=RecItems.size(); if (VolNum>CurSize) { RecItems.resize(VolNum); // for (size_t I=CurSize;If=Vol; Item->New=false; Item->Name=fd.Name; } } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return false; // If we did not find even a single .rar volume, create .rar volume name // based on the longest .rev file name. Use longest .rev, so we have // enough space for volume number. if (FirstVolName.empty()) { SetExt(LongestRevName,L"rar"); VolNameToFirstName(LongestRevName,FirstVolName,true); } uiMsg(UIMSG_RECVOLCALCCHECKSUM); MissingVolumes=0; for (uint I=0;If!=NULL) { uiMsg(UIMSG_STRING,Item->Name); uint RevCRC; CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,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(); std::wstring NewName; NewName=Item->Name+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))==true) { Item->Name=FirstVolName; uiMsg(UIMSG_CREATING,Item->Name); uiMsg(UIEVENT_NEWARCHIVE,Item->Name); File *NewVol=new File; bool UserReject; if (!FileCreate(Cmd,NewVol,Item->Name,&UserReject)) { if (!UserReject) ErrHandler.CreateErrorMsg(Item->Name); ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); } NewVol->Prealloc(Item->FileSize); Item->f=NewVol; } NextVolumeName(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; #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.resize(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,false); } } unrar/resource.cpp000666 000000 000000 00000000146 15026203745 012717 0ustar00000000 000000 #include "rar.hpp" #ifndef RARDLL const wchar* St(MSGID StringId) { return StringId; } #endif unrar/rijndael.cpp000666 000000 000000 00000044235 15026203745 012667 0ustar00000000 000000 /************************************************************************** * This code is based on Szymon Stefanek public domain AES implementation * **************************************************************************/ #include "rar.hpp" #ifdef USE_SSE #include #endif static byte S[256]= { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 }; static byte S5[256]; // Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256. static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36}; 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 (S5[0]==0) GenerateTables(); m_uRounds = 0; CBCMode = true; // Always true for RAR. #ifdef USE_SSE AES_NI=false; #endif #ifdef USE_NEON_AES AES_Neon=false; #endif } void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { // Check SIMD here instead of constructor, so if object is a part of some // structure memset'ed before use, these variables are not lost. #if defined(USE_SSE) #ifdef _MSC_VER int CPUInfo[4]; __cpuid(CPUInfo, 0); if (CPUInfo[0]>=1) // Check the maximum supported cpuid function. { __cpuid(CPUInfo, 1); AES_NI=(CPUInfo[2] & 0x2000000)!=0; } else AES_NI=false; #elif defined(__GNUC__) AES_NI=__builtin_cpu_supports("aes"); #endif #elif defined(USE_NEON_AES) #ifdef _APPLE // getauxval isn't available in OS X uint Value=0; size_t Size=sizeof(Value); int RetCode=sysctlbyname("hw.optional.arm.FEAT_AES",&Value,&Size,NULL,0); // We treat sysctlbyname failure with -1 return code as AES presence, // because "hw.optional.arm.FEAT_AES" was missing in OS X 11, but AES // still was supported by Neon. AES_Neon=RetCode!=0 || Value!=0; #else AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; #endif #endif // Other developers asked us to initialize it to suppress "may be used // uninitialized" warning in code below in some compilers. uint uKeyLenInBytes=0; 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; #if defined(USE_SSE) if (AES_NI) { blockEncryptSSE(input,numBlocks,outBuffer); return; } #elif defined(USE_NEON_AES) if (AES_Neon) { blockEncryptNeon(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) { byte block[16]; if (CBCMode) vst1q_u8(block, veorq_u8(vld1q_u8(prevBlock), vld1q_u8(input))); else vst1q_u8(block, vld1q_u8(input)); uint8x16_t data = vld1q_u8(block); for (uint i = 0; i < m_uRounds-1; i++) { data = vaeseq_u8(data, vld1q_u8((byte *)m_expandedKey[i])); data = vaesmcq_u8(data); } data = vaeseq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds-1]))); data = veorq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds]))); vst1q_u8(outBuffer, data); prevBlock=outBuffer; outBuffer += 16; input += 16; numBlocks--; } vst1q_u8(m_initVector, vld1q_u8(prevBlock)); return; } #endif void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer) { if (inputLen <= 0) return; size_t numBlocks=inputLen/16; #if defined(USE_SSE) if (AES_NI) { blockDecryptSSE(input,numBlocks,outBuffer); return; } #elif defined(USE_NEON_AES) if (AES_Neon) { blockDecryptNeon(input,numBlocks,outBuffer); return; } #endif byte block[16], iv[4][4]; memcpy(iv,m_initVector,16); for (size_t i = numBlocks; 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 #ifdef USE_NEON_AES void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer) { byte iv[16]; memcpy(iv,m_initVector,16); while (numBlocks > 0) { uint8x16_t data = vld1q_u8(input); for (int i=m_uRounds-1; i>0; i--) { data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1])); data = vaesimcq_u8(data); } data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1])); data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0])); if (CBCMode) data = veorq_u8(data, vld1q_u8(iv)); vst1q_u8(iv, vld1q_u8(input)); vst1q_u8(outBuffer, data); input += 16; outBuffer += 16; numBlocks--; } memcpy(m_initVector,iv,16); } #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])); } } static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication. { const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial. byte result = 0; while (b>0) { if ((b & 1) != 0) result ^= a; a = (a & 0x80) ? (a<<1)^poly : a<<1; b >>= 1; } return result; } // 2021-09-24: changed to slower and simpler code without interim tables. // It is still fast enough for our purpose. void Rijndael::GenerateTables() { for (int I=0;I<256;I++) S5[S[I]]=I; for (int I=0;I<256;I++) { byte s=S[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]=s; T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2); T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3); byte b=S5[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]=gmul(b,0xb); U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9); U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd); U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe); } } #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]; std::wstring Str; 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; } // 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check. 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]=byte(gfMul(I<<4,M)); ((byte *)&T1H)[I]=byte(gfMul(I<<4,M)>>8); ((byte *)&T2L)[I]=byte(gfMul(I<<8,M)); ((byte *)&T2H)[I]=byte(gfMul(I<<8,M)>>8); ((byte *)&T3L)[I]=byte(gfMul(I<<12,M)); ((byte *)&T3H)[I]=byte(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.empty() && !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 (uint I=0;I0 && ExpandedFolderList.GetString(CurMask)) return true; FolderWildcards=false; FilterList.Reset(); if (!FileMasks->GetString(CurMask)) return false; // Check if folder wildcards present. bool WildcardFound=false; uint FolderWildcardCount=0; uint SlashPos=0; uint StartPos=0; #ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. if (CurMask.rfind(L"\\\\?\\",0)==0) StartPos=4; #endif for (uint I=StartPos;I2 && CurMask[0]==CPATHDIVIDER && CurMask[1]==CPATHDIVIDER) { auto Slash=CurMask.find(CPATHDIVIDER,2); if (Slash!=std::wstring::npos) { Slash=CurMask.find(CPATHDIVIDER,Slash+1); // If path separator is mssing or it is the last string character. ScanEntireDisk=Slash==std::wstring::npos || Slash!=std::wstring::npos && Slash+1==CurMask.size(); // Win32 FindFirstFile fails for \\server\share names without // the trailing backslash. So we add it here. if (Slash==std::wstring::npos) CurMask+=CPATHDIVIDER; } } else ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; // Calculate the name position again, because we could modify UNC path above. auto NamePos=GetNamePos(CurMask); std::wstring Name=CurMask.substr(NamePos); if (Name.empty()) CurMask+=MASKALL; if (Name==L"." || Name==L"..") { AddEndSlash(CurMask); CurMask+=MASKALL; } Depth=0; OrigCurMask=CurMask; return true; } SCAN_CODE ScanTree::FindProc(FindData *FD) { if (CurMask.empty()) 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; std::wstring SearchMask=CurMask; if (SearchAll) SetName(SearchMask,MASKALL); 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.clear(); 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); // 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; } auto Slash=CurMask.rfind(CPATHDIVIDER); if (Slash!=std::wstring::npos) { std::wstring Mask; Mask=CurMask.substr(Slash); // Name mask with leading slash like \*.* 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; } std::wstring Mask=FastFindFile ? MASKALL:PointToName(CurMask); CurMask=FD->Name; if (CurMask.size()+Mask.size()+1>=MAXPATHSIZE || Depth>=MAXSCANDEPTH-1) { uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); return SCAN_ERROR; } AddEndSlash(CurMask); CurMask+=Mask; Depth++; FindStack.resize(Depth+1); // 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. auto Slash=GetNamePos(CurMask); if (Slash>1) { std::wstring Parent=CurMask.substr(0,Slash-1); DWORD Attr=GetFileAttr(Parent); 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 (CurMask.find(L"System Volume Information\\")!=std::wstring::npos) 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_back((uint)SpecPathLength); std::wstring FullName; // This conversion works for wildcard masks too. ConvertNameToFull(CurMask,FullName); // 2025.04.29: remove the trailing mask, so we issue errors like // "Cannot read contents of "c:\dir"" instead of "c:\path\dir\file.ext", // when searching for file.ext in inaccessible c:\path\dir. RemoveNameFromPath(FullName); uiMsg(UIERROR_DIRSCAN,FullName); ErrHandler.SysErrMsg(); } } unrar/secpassword.cpp000666 000000 000000 00000013622 15026203745 013430 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. static CryptLoader GlobalCryptLoader; #endif SecPassword::SecPassword() { Set(L""); } SecPassword::~SecPassword() { Clean(); } void SecPassword::Clean() { PasswordSet=false; if (!Password.empty()) cleandata(Password.data(),Password.size()*sizeof(Password[0])); } // 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==nullptr || 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,false); } void SecPassword::Get(wchar *Psw,size_t MaxSize) { if (PasswordSet) { Process(&Password[0],Password.size(),Psw,MaxSize,false); Psw[MaxSize-1]=0; } else *Psw=0; } void SecPassword::Get(std::wstring &Psw) { wchar PswBuf[MAXPASSWORD]; Get(PswBuf,ASIZE(PswBuf)); Psw=PswBuf; } void SecPassword::Set(const wchar *Psw) { // Eliminate any traces of previously stored password for security reason // in case it was longer than new one. Clean(); if (*Psw!=0) { PasswordSet=true; Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true); } } size_t SecPassword::Length() { wchar Plain[MAXPASSWORD]; Get(Plain,ASIZE(Plain)); size_t Length=wcslen(Plain); cleandata(Plain,sizeof(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,sizeof(Plain1)); cleandata(Plain2,sizeof(Plain2)); return Result; } // Set CrossProcess to true if we need to pass a password to another process. // We use CrossProcess when transferring parameters to UAC elevated WinRAR // and Windows GUI SFX modules. 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 00000010512 15026203745 012076 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++) { uint32 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. uint32 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); } void sha256_get(const void *Data, size_t Size, byte *Digest) { sha256_context ctx; sha256_init(&ctx); sha256_process(&ctx, Data, Size); sha256_done(&ctx, Digest); } unrar/smallfn.cpp000666 000000 000000 00000000377 15026203745 012532 0ustar00000000 000000 #include "rar.hpp" int ToPercent(int64 N1,int64 N2) { if (N2=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) Str[I]=0; return Str; } void RemoveEOL(std::wstring &Str) { while (!Str.empty()) { wchar c=Str.back(); if (c=='\r' || c=='\n' || c==' ' || c=='\t') Str.pop_back(); else break; } } 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; } void RemoveLF(std::wstring &Str) { for (int I=(int)Str.size()-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) Str.erase(I); } #if defined(SFX_MODULE) // char version of etoupperw. Used in console SFX module only. // Fast toupper for English only input and output. Additionally to speed, // it also avoids Turkish small i to big I with dot conversion problem. // We do not define 'c' as 'int' to avoid necessity to cast all // signed chars passed to this function to unsigned char. unsigned char etoupper(unsigned char c) { return c>='a' && c<='z' ? c-'a'+'A' : c; } #endif // Fast toupper for English only input and output. Additionally to speed, // it also avoids Turkish small i to big I with dot conversion problem. // We do not define 'c' as 'int' to avoid necessity to cast all // signed wchars passed to this function to unsigned char. wchar etoupperw(wchar c) { return c>='a' && c<='z' ? c-'a'+'A' : c; } // 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,std::wstring &Hex) { Hex.clear(); 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; Hex+=HighHex; Hex+=LowHex; } } #ifndef SFX_MODULE uint GetDigits(uint Number) { uint Digits=1; while (Number>=10) { Number/=10; Digits++; } return Digits; } #endif bool LowAscii(const std::string &Str) { for (char Ch : Str) { // We convert char to byte in case char is signed. if (/*(uint)Ch<32 || */(byte)Ch>127) return false; } return true; } bool LowAscii(const std::wstring &Str) { for (wchar Ch : Str) { // We convert wchar_t to uint just in case if some compiler // uses signed wchar_t. if (/*(uint)Ch<32 || */(uint)Ch>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 wcsicompc(const std::wstring &s1,const std::wstring &s2) { return wcsicompc(s1.c_str(),s2.c_str()); } 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 } int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n) { return wcsnicompc(s1.c_str(),s2.c_str(),n); } // Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. void strncpyz(char *dest, const char *src, size_t maxlen) { if (maxlen>0) { while (--maxlen>0 && *src!=0) *dest++=*src++; *dest=0; } } // Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen) { if (maxlen>0) { while (--maxlen>0 && *src!=0) *dest++=*src++; *dest=0; } } // Safe append: resulting dest length cannot exceed maxlen and dest // is always zero terminated. 'maxlen' parameter defines the entire // dest buffer size and is not compatible with wcsncat. void strncatz(char* dest, const char* src, size_t maxlen) { size_t length = strlen(dest); if (maxlen > length) strncpyz(dest + length, src, maxlen - length); } // Safe append: resulting dest length cannot exceed maxlen and dest // is always zero terminated. 'maxlen' parameter defines the entire // dest buffer size and is not compatible with wcsncat. void wcsncatz(wchar* dest, const wchar* src, size_t maxlen) { size_t length = wcslen(dest); if (maxlen > length) wcsncpyz(dest + length, src, maxlen - length); } 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;I0) ThSep=*Info; #elif defined(_UNIX) ThSep=*localeconv()->thousands_sep; #endif if (ThSep==0) // If failed to detect the actual separator value. ThSep=' '; wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int. itoa(n,RawText,ASIZE(RawText)); uint S=0,D=0,L=wcslen(RawText)%3; while (RawText[S]!=0 && D+1=0 || Msg.size()>MaxAllocSize) break; Msg.resize(Msg.size()*4); } std::wstring::size_type ZeroPos=Msg.find(L'\0'); if (ZeroPos!=std::wstring::npos) Msg.resize(ZeroPos); // Remove excessive zeroes at the end. return Msg; } #endif #ifdef _WIN_ALL bool ExpandEnvironmentStr(std::wstring &Str) { DWORD ExpCode=ExpandEnvironmentStrings(Str.c_str(),nullptr,0); if (ExpCode==0) return false; std::vector Buf(ExpCode); ExpCode=ExpandEnvironmentStrings(Str.c_str(),Buf.data(),(DWORD)Buf.size()); if (ExpCode==0 || ExpCode>Buf.size()) return false; Str=Buf.data(); return true; } #endif void TruncateAtZero(std::wstring &Str) { std::wstring::size_type Pos=Str.find(L'\0'); if (Pos!=std::wstring::npos) Str.erase(Pos); } void ReplaceEsc(std::wstring &Str) { std::wstring::size_type Pos=0; while (true) { Pos=Str.find(L'\033',Pos); if (Pos==std::wstring::npos) break; Str[Pos]=L'\''; Str.insert(Pos+1,L"\\033'"); Pos+=6; } } unrar/strlist.cpp000666 000000 000000 00000005341 15026203745 012576 0ustar00000000 000000 #include "rar.hpp" StringList::StringList() { Reset(); } void StringList::Reset() { Rewind(); StringData.clear(); StringsCount=0; SavePosNumber=0; } /* void StringList::AddStringA(const char *Str) { std::wstring StrW; CharToWide(Str,StrW); AddString(StrW); } */ void StringList::AddString(const wchar *Str) { if (Str==NULL) Str=L""; size_t PrevSize=StringData.size(); StringData.resize(PrevSize+wcslen(Str)+1); wcscpy(&StringData[PrevSize],Str); StringsCount++; } void StringList::AddString(const std::wstring &Str) { AddString(Str.c_str()); } /* bool StringList::GetStringA(char *Str,size_t MaxLength) { std::wstring StrW; if (!GetString(StrW)) return false; WideToChar(StrW.c_str(),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; } bool StringList::GetString(std::wstring &Str) { wchar *StrPtr; if (!GetString(&StrPtr)) return false; Str=StrPtr; 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; } bool StringList::GetString(std::wstring &Str,int StringNum) { SavePosition(); Rewind(); bool RetCode=true; while (StringNum-- >=0) if (!GetString(Str)) { 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 std::wstring &Str,bool CaseSensitive) { SavePosition(); Rewind(); bool Found=false; wchar *CurStr; while (GetString(&CurStr)) { if (CurStr!=NULL) if (CaseSensitive && Str!=CurStr || !CaseSensitive && 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 15026203745 012707 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 00000013122 15026203745 012412 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 } #ifdef _WIN_ALL 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; } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void Shutdown(POWER_MODE Mode) { SetPrivilege(SE_SHUTDOWN_NAME); 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); } bool ShutdownCheckAnother(bool Open) { const wchar *EventName=L"rar -ioff"; static HANDLE hEvent=NULL; bool Result=false; // Return false if no other RAR -ioff are running. if (Open) // Create or open the event. hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); else { if (hEvent!=NULL) CloseHandle(hEvent); // Close our event. // Check if other copies still own the event. While race conditions // are possible, they are improbable and their harm is minimal. hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); Result=GetLastError()==ERROR_ALREADY_EXISTS; if (hEvent!=NULL) CloseHandle(hEvent); } return Result; } #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) { std::vector SysDir(MAX_PATH); if (GetSystemDirectory(SysDir.data(),(UINT)SysDir.size())==0) return nullptr; std::wstring FullName; MakeName(SysDir.data(),Name,FullName); return LoadLibrary(FullName.c_str()); } 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() { #ifdef _MSC_VER int CPUInfo[4]; __cpuid(CPUInfo, 0); // Maximum supported cpuid function. uint MaxSupported=CPUInfo[0]; if (MaxSupported>=7) { __cpuid(CPUInfo, 7); if ((CPUInfo[1] & 0x20)!=0) return SSE_AVX2; } if (MaxSupported>=1) { __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; } #elif defined(__GNUC__) if (__builtin_cpu_supports("avx2")) return SSE_AVX2; if (__builtin_cpu_supports("sse4.1")) return SSE_SSE41; if (__builtin_cpu_supports("ssse3")) return SSE_SSSE3; if (__builtin_cpu_supports("sse2")) return SSE_SSE2; if (__builtin_cpu_supports("sse")) return SSE_SSE; #endif return SSE_NONE; } #endif unrar/threadmisc.cpp000666 000000 000000 00000012053 15026203745 013213 0ustar00000000 000000 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 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 #ifdef WIN32_CPU_GROUPS // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask // "Starting with Windows 11 and Windows Server 2022, on a system with // more than 64 processors, process and thread affinities span all // processors in the system, across all processor groups, by default." // Supposing there are 80 CPUs in 2 processor groups 40 CPUs each. // Looks like, beginning from Windows 11 an app can use them all by default, // not resorting to processor groups API. But if we use GetProcessAffinityMask // to count CPUs, we would be limited to 40 CPUs only. So we call // GetActiveProcessorCount() if it is available anf if there are multiple // processor groups. For a single group we prefer the affinity aware // GetProcessAffinityMask(). Out thread pool code handles the case // with restricted processor group affinity. So we avoid the complicated // code to calculate all processor groups affinity here, such as using // GetLogicalProcessorInformationEx, and resort to GetActiveProcessorCount(). HMODULE hKernel=GetModuleHandle(L"kernel32.dll"); if (hKernel!=nullptr) { typedef DWORD (WINAPI *GETACTIVEPROCESSORCOUNT)(WORD GroupNumber); GETACTIVEPROCESSORCOUNT pGetActiveProcessorCount=(GETACTIVEPROCESSORCOUNT)GetProcAddress(hKernel,"GetActiveProcessorCount"); typedef WORD (WINAPI *GETACTIVEPROCESSORGROUPCOUNT)(); GETACTIVEPROCESSORGROUPCOUNT pGetActiveProcessorGroupCount=(GETACTIVEPROCESSORGROUPCOUNT)GetProcAddress(hKernel,"GetActiveProcessorGroupCount"); if (pGetActiveProcessorCount!=nullptr && pGetActiveProcessorGroupCount!=nullptr && pGetActiveProcessorGroupCount()>1) { // Once the thread pool called SetThreadGroupAffinity(), // GetProcessAffinityMask() below will return 0. So we shall always // use GetActiveProcessorCount() here if there are multiple processor // groups, which makes SetThreadGroupAffinity() call possible. DWORD Count=pGetActiveProcessorCount(ALL_PROCESSOR_GROUPS); return Count; } } #endif 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 00000023377 15026203745 013244 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;I1) // If we have multiple processor groups. { if (I>=CumulativeGroupSize) // Filled the processor group, go to next. { if (++CurGroupNumber>=GroupCount) { // If we exceeded the group number, such as when user specified // -mt64 for lower core count, start assigning from beginning. CurGroupNumber=0; CumulativeGroupSize=0; } // Current group size. CurGroupSize=pGetActiveProcessorCount(CurGroupNumber); // Size of all preceding groups including the current. CumulativeGroupSize+=CurGroupSize; } GROUP_AFFINITY GroupAffinity; pGetThreadGroupAffinity(hThread,&GroupAffinity); // Since normally before Windows 11 all threads belong to same source // group, we could set this value only once. But we set it every time // in case we'll decide for some reason to use it to rearrange threads // from different source groups in Windows 11+. uint SrcGroupSize=pGetActiveProcessorCount(GroupAffinity.Group); // Shifting by 64 would be the undefined behavior, so we treat 64 separately. KAFFINITY SrcGroupMask=(KAFFINITY)(SrcGroupSize==64 ? (uint64)0xffffffffffffffff:(uint64(1)<PoolThreadLoop(); 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 (ActiveThreads>=ASIZE(TaskQueue)) WaitDone(); TaskQueue[QueueTop].Proc = Proc; TaskQueue[QueueTop].Param = Data; QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); ActiveThreads++; } // 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() { 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 00000017045 15026203746 012361 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=(WORD)lt->Year; st.wMonth=(WORD)lt->Month; st.wDay=(WORD)lt->Day; st.wHour=(WORD)lt->Hour; st.wMinute=(WORD)lt->Minute; st.wSecond=(WORD)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() { const uint64 ushift=11644473600000000000ULL; // ns between 01-01-1601 and 01-01-1970. return itime*(1000000000/TICKS_PER_SECOND)-ushift; } // Set the high precision Unix time in nanoseconds since 01-01-1970. void RarTime::SetUnixNS(uint64 ns) { const uint64 ushift=11644473600000000000ULL; // ns between 01-01-1601 and 01-01-1970. 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. wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize); } } #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)) return UIASKREP_R_REPLACE; #endif std::wstring NewName=Name; UIASKREP_RESULT Choice=Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL ? UIASKREP_R_REPLACE : uiAskReplace(NewName,FileSize,FileTime,Flags); if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) { PrepareToDelete(Name); // Overwrite the link itself instead of its target. // For normal files we prefer to inherit file attributes, permissions // and hard links. FindData FD; if (FindFile::FastFind(Name,&FD,true) && FD.IsLink) DelFile(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 (GetNamePos(NewName)==0) SetName(Name,NewName); else Name=NewName; if (FileExist(Name)) return uiAskReplaceEx(Cmd,Name,FileSize,FileTime,Flags); return UIASKREP_R_REPLACE; } #if !defined(SFX_MODULE) && !defined(SILENT) if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name)) { Cmd->Overwrite=OVERWRITE_AUTORENAME; return UIASKREP_R_REPLACE; } #endif return Choice; } bool GetAutoRenamedName(std::wstring &Name) { std::wstring Ext=GetExt(Name); for (uint FileVer=1;FileVer<1000000;FileVer++) { std::wstring NewName=Name; RemoveExt(NewName); wchar Ver[10]; itoa(FileVer,Ver,ASIZE(Ver)); NewName = NewName + L"(" + Ver + L")" + Ext; if (!FileExist(NewName)) { Name=NewName; return true; } } return false; } unrar/uiconsole.cpp000666 000000 000000 00000034457 15026203746 013105 0ustar00000000 000000 static bool AnyMessageDisplayed=false; // For console -idn switch. // Purely user interface function. Gets and returns user input. UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags) { wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; FindData ExistingFD={}; // Init 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.c_str()); } else { itoa(FileSize,SizeText2,ASIZE(SizeText2)); FileTime->GetText(DateStr2,ASIZE(DateStr2),false); if ((Flags & UIASKREP_F_EXCHSRCDEST)==0) eprintf(St(MAskReplace),Name.c_str(),SizeText1,DateStr1,SizeText2,DateStr2); else eprintf(St(MAskReplace),Name.c_str(),SizeText2,DateStr2,SizeText1,DateStr1); } 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)); getwstr(Name); return UIASKREP_R_RENAME; } return UIASKREP_R_CANCEL; } void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName) { mprintf(St(Extract ? MExtracting : MExtrTest), ArcName.c_str()); } bool uiStartFileExtract(const std::wstring &FileName,bool Extract,bool Test,bool Skip) { return true; } void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) { // We set the total size to 0 to update only the current progress and keep // the total progress intact in WinRAR. Unlike WinRAR, console RAR has only // the total progress and updates it with current values in such case. int CurPercent=TotalSize!=0 ? ToPercent(CurSize,TotalSize) : ToPercent(CurFileSize,TotalFileSize); 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() { // When creating volumes, AnyMessageDisplayed must be reset for UIEVENT_NEWARCHIVE, // so it ignores this and all earlier messages like UIEVENT_PROTECTEND // and UIEVENT_PROTECTEND, because they precede "Creating archive" message // and do not interfere with -idn and file names. If we do not ignore them, // uiEolAfterMsg() in uiStartFileAddit() can cause unneeded carriage return // in archiving percent after creating a new volume with -v -idn (and -rr // for UIEVENT_PROTECT*) switches. AnyMessageDisplayed is set for messages // after UIEVENT_NEWARCHIVE, so archiving percent with -idn is moved to // next line and does not delete their last characters. // Similarly we ignore UIEVENT_RRTESTINGEND for volumes, because it is issued // before "Testing archive" and would add an excessive \n otherwise. AnyMessageDisplayed=(Code!=UIEVENT_NEWARCHIVE && Code!=UIEVENT_RRTESTINGEND); 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: Log(Str[0],St(MWrongFilePassword),Str[1]); break; 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: mprintf(L"\n"); 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: mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN. 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: { wchar Msg[256]; swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]); Log(Str[0],L"%s: %s",Msg,Str[2]); } 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_EXTRDICTOUTMEM: Log(Str[0],St(MExtrDictOutMem),Num[0]); #ifdef _WIN_32 Log(Str[0],St(MSuggest64bit)); #endif break; case UIERROR_UNEXPEOF: Log(Str[0],St(MLogUnexpEOF)); break; case UIERROR_TRUNCSERVICE: { const wchar *Type=nullptr; if (wcscmp(Str[1],SUBHEAD_TYPE_QOPEN)==0) Type=St(MHeaderQO); else if (wcscmp(Str[1],SUBHEAD_TYPE_RR)==0) Type=St(MHeaderRR); if (Type!=nullptr) Log(Str[0],St(MTruncService),Type); } 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]); mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile. break; #ifndef SFX_MODULE case UIERROR_OPFAILED: Log(NULL,St(MOpFailed)); break; 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]); mprintf(L" "); // For progress percent. 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; case UIERROR_READERRTRUNCATED: Log(NULL,St(MErrReadTrunc),Str[0]); break; case UIERROR_READERRCOUNT: Log(NULL,St(MErrReadCount),Num[0]); break; case UIERROR_DIRNAMEEXISTS: Log(NULL,St(MDirNameExists)); break; case UIERROR_TRUNCPSW: eprintf(St(MTruncPsw),Num[0]); eprintf(L"\n"); break; case UIERROR_ADJUSTVALUE: Log(NULL,St(MAdjustValue),Str[0],Str[1]); break; case UIERROR_SKIPUNSAFELINK: Log(NULL,St(MSkipUnsafeLink),Str[0],Str[1]); 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 UIMSG_SKIPENCARC: Log(NULL,St(MSkipEncArc),Str[0]); break; case UIEVENT_RRTESTINGSTART: mprintf(L"%s ",St(MTestingRR)); break; } } bool uiGetPassword(UIPASSWORD_TYPE Type,const std::wstring &FileName, SecPassword *Password,CheckPassword *CheckPwd) { // 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(); } bool uiIsGlobalPasswordSet() { return false; } void uiAlarm(UIALARM_TYPE Type) { if (uiSoundNotify==SOUND_NOTIFY_ON) { 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(std::wstring &VolName) { eprintf(St(MAskNextVol),VolName.c_str()); return Ask(St(MContinueQuit))!=2; } void uiAskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) { eprintf(St(MErrReadInfo)); int Code=Ask(St(MIgnoreAllRetryQuit)); Ignore=(Code==1); All=(Code==2); Quit=(Code==4); Retry=!Ignore && !All && !Quit; // Default also for invalid input, not just for 'Retry'. } bool uiAskRepeatWrite(const std::wstring &FileName,bool DiskFull) { mprintf(L"\n"); Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName.c_str()); return Ask(St(MRetryAbort))==1; } bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize) { mprintf(L"\n%s",FileName.c_str()); const uint64 GB=1024*1024*1024; // We display sizes in GB here. // Include the reminder for switches like -md6400m. DictSize=DictSize/GB+(DictSize%GB!=0 ? 1:0); // Exclude reminder for in case it is less than archive dictionary size, // but still more than nearest GB value. Otherwise we could have message // like "7 GB dictionary exceeds 7 GB limit", where first 7 might be actually // 6272 MB and second is 6400 MB. MaxDictSize/=GB; mprintf(St(MDictNotAllowed),(uint)DictSize,(uint)MaxDictSize,(uint)DictSize); mprintf(St(MDictExtrAnyway),(uint)DictSize,(uint)DictSize); mprintf(L"\n"); return false; // Stop extracting. } #ifndef SFX_MODULE const wchar *uiGetMonthName(uint Month) { static MSGID MonthID[12]={ MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec }; return St(MonthID[Month]); } #endif void uiEolAfterMsg() { if (AnyMessageDisplayed) { // Avoid deleting several last characters of any previous error message // with percentage indicator in -idn mode. AnyMessageDisplayed=false; mprintf(L"\n"); } } unrar/uisilent.cpp000666 000000 000000 00000002543 15026203746 012730 0ustar00000000 000000 // Purely user interface function. Gets and returns user input. UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags) { return UIASKREP_R_REPLACE; } void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName) { } bool uiStartFileExtract(const std::wstring &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 std::wstring &FileName, SecPassword *Password,CheckPassword *CheckPwd) { return false; } bool uiIsGlobalPasswordSet() { return false; } void uiAlarm(UIALARM_TYPE Type) { } bool uiIsAborted() { return false; } void uiGiveTick() { } bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize) { #ifdef RARDLL if (Cmd->Callback!=nullptr && Cmd->Callback(UCM_LARGEDICT,Cmd->UserData,(LPARAM)(DictSize/1024),(LPARAM)(MaxDictSize/1024))==1) return true; // Continue extracting if unrar.dll callback permits it. #endif return false; // Stop extracting. } #ifndef SFX_MODULE const wchar *uiGetMonthName(uint Month) { return L""; } #endif void uiEolAfterMsg() { } unrar/ulinks.cpp000666 000000 000000 00000012337 15026203746 012403 0ustar00000000 000000 static bool UnixSymlink(CommandData *Cmd,const std::string &Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { CreatePath(LinkName,true,Cmd->DisableNames); // Overwrite prompt was already issued and confirmed earlier, so we can // remove existing symlink or regular file here. PrepareToDelete was also // called earlier inside of uiAskReplaceEx. DelFile(LinkName); std::string LinkNameA; WideToChar(LinkName,LinkNameA); if (symlink(Target.c_str(),LinkNameA.c_str())==-1) // Error. { if (errno==EEXIST) uiMsg(UIERROR_ULINKEXIST,LinkName); else { uiMsg(UIERROR_SLINKCREATE,L"",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.c_str(),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.c_str(),tv); #endif #endif return true; } static bool IsFullPath(const char *PathA) // Unix ASCII version. { return *PathA==CPATHDIVIDER; } // For security purpose we prefer to be sure that CharToWide completed // successfully and even if it truncated a string for some reason, // it didn't affect the number of path related characters we analyze // in IsRelativeSymlinkSafe later. // This check is likely to be excessive, but let's keep it anyway. static bool SafeCharToWide(const std::string &Src,std::wstring &Dest) { if (!CharToWide(Src,Dest) || Dest.empty()) return false; uint SrcChars=0,DestChars=0; for (uint I=0;Src[I]!=0;I++) if (Src[I]=='/' || Src[I]=='.') SrcChars++; for (uint I=0;Dest[I]!=0;I++) if (Dest[I]=='/' || Dest[I]=='.') DestChars++; return SrcChars==DestChars; } static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, const wchar *LinkName,bool &UpLink) { if (IsLink(Arc.FileHead.FileAttr)) { size_t DataSize=(size_t)Arc.FileHead.PackSize; if (DataSize>MAXPATHSIZE) return false; std::vector TargetBuf(DataSize+1); if ((size_t)DataIO.UnpRead((byte*)TargetBuf.data(),DataSize)!=DataSize) return false; std::string Target(TargetBuf.data(),TargetBuf.size()); DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); DataIO.UnpHash.Update(Target.data(),strlen(Target.data())); 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; std::wstring TargetW; if (!SafeCharToWide(Target.data(),TargetW)) return false; TruncateAtZero(TargetW); // 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 && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName.c_str(),LinkName,TargetW.c_str()))) { uiMsg(UIERROR_SKIPUNSAFELINK,Arc.FileHead.FileName,TargetW); ErrHandler.SetErrorCode(RARX_WARNING); return false; } UpLink=Target.find("..")!=std::string::npos; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; } static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) { std::string Target; WideToChar(hd->RedirName,Target); if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) { // Windows absolute path symlinks in Unix. RAR 5.0 used \??\ prefix // for Windows absolute symlinks, since RAR 5.1 /??/ is used. // We escape ? as \? to avoid "trigraph" warning if (Target.rfind("\\??\\",0)!=std::string::npos || Target.rfind("/\?\?/",0)!=std::string::npos) { #if 0 // 2024.12.26: Not used anymore. We unpack absolute Windows symlinks even in Unix. uiMsg(UIERROR_SLINKCREATE,nullptr,L"\"" + hd->FileName + L"\" -> \"" + hd->RedirName + L"\""); ErrHandler.SetErrorCode(RARX_WARNING); return false; #endif // 2024.12.26: User asked to unpack absolute Windows symlinks even in Unix. Target=Target.substr(4); // Remove \??\ prefix. } DosSlashToUnix(Target,Target); } std::wstring TargetW; if (!SafeCharToWide(Target,TargetW)) return false; // 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(TargetW) || !IsRelativeSymlinkSafe(Cmd,hd->FileName.c_str(),Name,TargetW.c_str()))) { uiMsg(UIERROR_SKIPUNSAFELINK,hd->FileName,TargetW); ErrHandler.SetErrorCode(RARX_WARNING); return false; } return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } unrar/unicode.cpp000666 000000 000000 00000054201 15026203746 012520 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; } bool WideToChar(const std::wstring &Src,std::string &Dest) { // We need more than 1 char per wchar_t for DBCS and up to 4 for UTF-8. std::vector DestA(4*Src.size()+1); // "+1" for terminating zero. bool Result=WideToChar(Src.c_str(),DestA.data(),DestA.size()); Dest=DestA.data(); return Result; } bool CharToWide(const std::string &Src,std::wstring &Dest) { // 2 wchar_t per char in case char is converted to UTF-16 surrogate pair. std::vector DestW(2*Src.size()+1); // "+1" for terminating zero. bool Result=CharToWide(Src.c_str(),DestW.data(),DestW.size()); Dest=DestW.data(); return Result; } #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 source data size in wide characters, not in bytes. // DestSize is the maximum allowed destination size. byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize) { for (size_t I=0;I>8); if (*Src==0) break; } return Dest; } // Store UTF-16 raw byte stream. void WideToRaw(const std::wstring &Src,std::vector &Dest) { for (wchar C : Src) { Dest.push_back((byte)C); Dest.push_back((byte)(C>>8)); } // In STL version of this function we do not add the trailing zero. // Otherwise we would need to remove it when restoring std::wstring // from raw data. // Dest.push_back(0); // 2 bytes of trailing UTF-16 zero. // Dest.push_back(0); } wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) { for (size_t I=0;I &Src) { std::wstring Dest; for (size_t I=0;I+1=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; } void WideToUtf(const std::wstring &Src,std::string &Dest) { for (size_t I=0;I>6)); Dest.push_back(0x80|(c&0x3f)); } else { if (c>=0xd800 && c<=0xdbff && I=0xdc00 && Src[I]<=0xdfff) // Surrogate pair. { c=((c-0xd800)<<10)+(Src[I]-0xdc00)+0x10000; I++; } if (c<0x10000) { Dest.push_back(0xe0|(c>>12)); Dest.push_back(0x80|((c>>6)&0x3f)); Dest.push_back(0x80|(c&0x3f)); } else if (c < 0x200000) { Dest.push_back(0xf0|(c>>18)); Dest.push_back(0x80|((c>>12)&0x3f)); Dest.push_back(0x80|((c>>6)&0x3f)); Dest.push_back(0x80|(c&0x3f)); } } } } 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; } bool UtfToWide(const char *Src,std::wstring &Dest) { bool Success=true; Dest.clear(); 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 (d>0xffff) { if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. { Success=false; continue; } if (sizeof(wchar_t)==2) // Use the surrogate pair. { Dest.push_back( ((d-0x10000)>>10)+0xd800 ); Dest.push_back( (d&0x3ff)+0xdc00 ); } else Dest.push_back( d ); } else Dest.push_back( d ); } return Success; } /* bool UtfToWide(const std::vector &Src,std::wstring &Dest) { bool Success=true; Dest.clear(); for (size_t I=0;I>5)==6) { if (Src.size()-I<1 || (Src[I]&0xc0)!=0x80) { Success=false; break; } d=((c&0x1f)<<6)|(Src[I]&0x3f); I++; } else if ((c>>4)==14) { if (Src.size()-I<2 || (Src[I]&0xc0)!=0x80 || (Src[I+1]&0xc0)!=0x80) { Success=false; break; } d=((c&0xf)<<12)|((Src[I]&0x3f)<<6)|(Src[I+1]&0x3f); I+=2; } else if ((c>>3)==30) { if (Src.size()-I<3 || (Src[I]&0xc0)!=0x80 || (Src[I+1]&0xc0)!=0x80 || (Src[I+2]&0xc0)!=0x80) { Success=false; break; } d=((c&7)<<18)|((Src[I]&0x3f)<<12)|((Src[I+1]&0x3f)<<6)|(Src[I+2]&0x3f); I+=3; } else { Success=false; break; } if (d>0xffff) { if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. { Success=false; continue; } if (sizeof(Dest[0])==2) // Use the surrogate pair. { Dest.push_back( ((d-0x10000)>>10)+0xd800 ); Dest.push_back( (d&0x3ff)+0xdc00 ); } else Dest.push_back( d ); } else Dest.push_back( d ); } 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) { // If strings are English or numeric, perform the fast comparison. // It improves speed in cases like comparing against a lot of MOTW masks. bool FastMode=true; while (true) { // English uppercase, English lowercase and digit flags. bool u1=*s1>='A' && *s1<='Z', l1=*s1>='a' && *s1<='z', d1=*s1>='0' && *s1<='9'; bool u2=*s2>='A' && *s2<='Z', l2=*s2>='a' && *s2<='z', d2=*s2>='0' && *s2<='9'; // Fast comparison is impossible if both characters are not alphanumeric or 0. if (!u1 && !l1 && !d1 && *s1!=0 && !u2 && !l2 && !d2 && *s2!=0) { FastMode=false; break; } // Convert lowercase to uppercase, keep numeric and not alphanumeric as is. wchar c1 = l1 ? *s1-'a'+'A' : *s1; wchar c2 = l2 ? *s2-'a'+'A' : *s2; // If characters mistmatch, to return a proper value we must compare // already converted, case insensitive characters instead of original ones. // So we place a.txt before B.txt and can perform the correct case // insensitive binary search in different string lists. if (c1 != c2) return c1 < c2 ? -1 : 1; if (*s1==0) break; s1++; s2++; } if (FastMode) return 0; #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 characters mistmatch, to return a proper value we must compare // already converted, case insensitive characters instead of original ones. // So we place a.txt before B.txt and can perform the correct case // insensitive binary search in different string lists. 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 sl1=wcslen(s1); // Pre-compute to not call wcslen() in Min() twice. size_t l1=Min(sl1+1,n); size_t sl2=wcslen(s2); // Pre-compute to not call wcslen() in Min() twice. size_t l2=Min(sl2+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 } // Case insensitive wcsstr(). 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 nullptr; } // Case insensitive std::wstring substring search. std::wstring::size_type wcscasestr(const std::wstring &str, const std::wstring &search) { const wchar *Found=wcscasestr(str.c_str(),search.c_str()); return Found==nullptr ? std::wstring::npos : Found-str.c_str(); } #ifndef SFX_MODULE wchar* wcslower(wchar *s) { #ifdef _WIN_ALL // _wcslwr requires setlocale and we do not want to depend on setlocale // in Windows. Also CharLower involves less overhead. CharLower(s); #else for (wchar *c=s;*c!=0;c++) *c=towlower(*c); #endif return s; } void wcslower(std::wstring &s) { wcslower(&s[0]); } wchar* wcsupper(wchar *s) { #ifdef _WIN_ALL // _wcsupr requires setlocale and we do not want to depend on setlocale // in Windows. Also CharUpper involves less overhead. CharUpper(s); #else for (wchar *c=s;*c!=0;c++) *c=towupper(*c); #endif return s; } void wcsupper(std::wstring &s) { wcsupper(&s[0]); } #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. Use 0xffff mask to prevent crash // if value larger than 0xffff is passed to this function. return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff)); #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. Use 0xffff mask to prevent crash // if value larger than 0xffff is passed to this function. return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff)); #else return towlower(ch); #endif } int atoiw(const std::wstring &s) { return (int)atoilw(s); } int64 atoilw(const std::wstring &s) { bool sign=false; size_t Pos=0; if (s[Pos]=='-') // We do use signed integers here, for example, in GUI SFX. { Pos++; 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[Pos]>='0' && s[Pos]<='9') { n=n*10+(s[Pos]-'0'); Pos++; } // 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;I(Window); // delete Window; #ifdef RAR_SMP delete UnpThreadPool; delete[] ReadBufMT; delete[] UnpThreadData; #endif } #ifdef RAR_SMP void Unpack::SetThreads(uint Threads) { // More than 8 threads are unlikely to provide noticeable gain // for unpacking, but would use the additional memory. MaxUserThreads=Min(Threads,8); UnpThreadPool=new ThreadPool(MaxUserThreads); } #endif // We get 64-bit WinSize, so we still can check and quit for dictionaries // exceeding a threshold in 32-bit builds. Then we convert WinSize to size_t // MaxWinSize. void Unpack::Init(uint64 WinSize,bool Solid) { // Minimum window size must be at least twice more than maximum possible // size of filter block, which is 0x10000 in RAR now. If window size is // smaller, we can have a block with never cleared flt->NextWindow 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 (WinSizeMin(0x10000000000ULL,UNPACK_MAX_DICT)) // Window size must not exceed 1 TB. throw std::bad_alloc(); // 32-bit build can't unpack dictionaries exceeding 32-bit even in theory. // Also we've not verified if WrapUp and WrapDown work properly in 32-bit // version and >2GB dictionary and if 32-bit version can handle >2GB // distances. Since such version is unlikely to allocate >2GB anyway, // we prohibit >2GB dictionaries for 32-bit build here. if (WinSize>0x80000000 && sizeof(size_t)<=4) throw std::bad_alloc(); // Solid block shall use the same window size for all files. // But if Window isn't initialized when Solid is set, it means that // first file in solid block doesn't have the solid flag. We initialize // the window anyway for such malformed archive. // Non-solid files shall use their specific window sizes, // so current window size and unpack routine behavior doesn't depend on // previously unpacked files and their extraction order. if (!Solid || Window==nullptr) { MaxWinSize=(size_t)WinSize; MaxWinMask=MaxWinSize-1; } // Use the already allocated window when processing non-solid files // with reducing dictionary sizes. if (WinSize<=AllocWinSize) 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. if (Solid && (Window!=NULL || Fragmented && WinSize>FragWindow.GetWinSize())) throw std::bad_alloc(); Alloc.delete_l(Window); // delete Window; Window=nullptr; try { if (!Fragmented) Window=Alloc.new_l((size_t)WinSize,false); // Window=new byte[(size_t)WinSize]; } catch (std::bad_alloc) // Use the fragmented window in this case. { } if (Window==nullptr) if (WinSize<0x1000000 || sizeof(size_t)>4) throw std::bad_alloc(); // Exclude RAR4, small dictionaries and 64-bit. else { if (WinSize>FragWindow.GetWinSize()) FragWindow.Init((size_t)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. // 2023.10.31: We've added FirstWinDone based unused area access check // in Unpack::CopyString(), so this memset might be unnecessary now. // memset(Window,0,(size_t)WinSize); AllocWinSize=WinSize; } } 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; case 29: // RAR 3.x compression. if (!Fragmented) Unpack29(Solid); break; #endif case VER_PACK5: // 50. RAR 5.0 and 7.0 compression algorithms. case VER_PACK7: // 70. ExtraDist=(Method==VER_PACK7); #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) { OldDist[0]=OldDist[1]=OldDist[2]=OldDist[3]=(size_t)-1; OldDistPtr=0; LastDist=(uint)-1; // Initialize it to -1 like LastDist. LastLength=0; // memset(Window,0,MaxWinSize); memset(&BlockTables,0,sizeof(BlockTables)); UnpPtr=WrPtr=0; PrevPtr=0; FirstWinDone=false; WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE); } // 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); UnpInitData30(Solid); #endif 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 ? MAX_QUICK_DECODE_BITS-3 : 0; 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 00000025541 15026203746 012526 0ustar00000000 000000 #define STARTL1 2 static uint DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, 0xee00,0xf000,0xf200,0xf200,0xffff}; static uint PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; #define STARTL2 3 static uint DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, 0xf000,0xf200,0xf240,0xffff}; static uint PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; #define STARTHF0 4 static uint DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, 0xf200,0xf200,0xffff}; static uint PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; #define STARTHF1 5 static uint DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, 0xf7e0,0xffff}; static uint PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; #define STARTHF2 5 static uint DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, 0xffff,0xffff}; static uint PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; #define STARTHF3 6 static uint DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, 0xffff}; static uint PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; #define STARTHF4 8 static uint DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; static uint 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; FirstWinDone|=(PrevPtr>UnpPtr); PrevPtr=UnpPtr; 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 uint ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; static uint ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; static uint ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; static uint ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; uint Length,SaveLength; uint LastDistance; uint Distance; int DistancePlace; NumHuf=0; uint BitField=Inp.fgetbits(); if (LCount==2) { Inp.faddbits(1); if (BitField >= 0x8000) { CopyString15(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(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=(uint)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]=(ushort)LastDistance; ChSetA[DistancePlace]=(ushort)Distance; } Length+=2; OldDist[OldDistPtr++] = ++Distance; OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; CopyString15(Distance,Length); } void Unpack::LongLZ() { uint Length; uint Distance; uint DistancePlace,NewDistancePlace; uint OldAvr2,OldAvr3; NumHuf=0; Nlzb+=16; if (Nlzb > 0xff) { Nlzb=0x90; Nhfb >>= 1; } OldAvr2=AvrLn2; uint 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]=(ushort)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() { uint CurByte,NewBytePlace; uint Length; uint Distance; int BytePlace; uint 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]=(ushort)CurByte; } void Unpack::GetFlagsBuf() { uint Flags,NewFlagsPlace; uint 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]=(ushort)Flags; } void Unpack::UnpInitData15(bool 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 (ushort 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; // 2024.04.18: Distance can be 0 in corrupt RAR 1.5 archives. if (!FirstWinDone && Distance>UnpPtr || Distance>MaxWinSize || Distance==0) while (Length-- > 0) { Window[UnpPtr]=0; UnpPtr=(UnpPtr+1) & MaxWinMask; } else while (Length-- > 0) { 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 00000021416 15026203746 012517 0ustar00000000 000000 #include "rar.hpp" void Unpack::CopyString20(uint Length,uint Distance) { LastDist=Distance; 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; FirstWinDone|=(PrevPtr>UnpPtr); PrevPtr=UnpPtr; 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=(uint)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); // 2024.12.24: Before 7.10 we set "UnpAllBuf=true" here. It was needed for // Pack::PrepareSolidAppend(). Since both UnpAllBuf and FirstWinDone // variables indicate the same thing and we set FirstWinDone in other place // anyway, we replaced UnpAllBuf with FirstWinDone and removed this code. } 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 00000046574 15026203746 012534 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[DC30]; static byte DBits[DC30]; 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;IUnpPtr); PrevPtr=UnpPtr; if (Inp.InAddr>ReadBorder) { if (!UnpReadBuf30()) break; } if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && 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=(uint)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; std::vector VMCode(Length); for (uint I=0;I=ReadTop-1 && !UnpReadBuf30() && I>8; Inp.addbits(8); } return AddVMCode(FirstByte,VMCode.data(),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; std::vector 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; } StackFilter->ParentFilter=(uint)Filters30.size(); Filter=new UnpackFilter30; Filters30.push_back(Filter); // 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_back(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.resize(PrgStack.size()+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; std::vector VMCode(VMCodeSize); for (uint I=0;I>8; VMCodeInp.faddbits(8); } VM.Prepare(VMCode.data(),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]=nullptr; 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]=nullptr; } 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.clear(); LastFilter=0; for (size_t I=0;IUnpPtr); PrevPtr=UnpPtr; if (Inp.InAddr>=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; } // WriteBorder==UnpPtr means that we have MaxWinSize data ahead. if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); if (WrittenFileSize>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); size_t Distance=1; uint DBits,DistSlot=DecodeNumber(Inp,&BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { // It is also possible to always use getbits64() here. if (DBits>36) Distance+=( ( size_t(Inp.getbits64() ) >> (68-DBits) ) << 4 ); else Distance+=( ( size_t(Inp.getbits32() ) >> (36-DBits) ) << 4 ); Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); Distance+=LowDist; // Distance can be 0 for multiples of 4 GB as result of size_t // overflow in 32-bit build. Its lower 32-bit can also erroneously // fit into dictionary after truncating upper 32-bits. Replace such // invalid distances with -1, so CopyString sets 0 data for them. // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. if (sizeof(Distance)==4 && DBits>=30) Distance=(size_t)-1; } else { Distance+=Inp.getbits()>>(16-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,FirstWinDone,MaxWinSize); 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,FirstWinDone,MaxWinSize); else CopyString(LastLength,OldDist[0]); continue; } if (MainSlot<262) { uint DistNum=MainSlot-258; size_t 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,FirstWinDone,MaxWinSize); 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 && WrapDown(WrPtr-UnpPtr)<=Filter.BlockStart; // In malformed archive Filter.BlockStart can be many times larger // than window size, so here we must use the reminder instead of // subtracting the single window size as WrapUp can do. So the result // is always within the window. Since we add and not subtract here, // reminder always provides the valid result in valid archives. Filter.BlockStart=(Filter.BlockStart+UnpPtr)%MaxWinSize; Filters.push_back(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=WrapDown(UnpPtr-WrittenBorder); 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 and no further wrap arounds is possible. if (WrapDown(flt->BlockStart-WrPtr)<=FullWriteSize) flt->NextWindow=false; continue; } size_t BlockStart=flt->BlockStart; uint BlockLength=flt->BlockLength; if (WrapDown(BlockStart-WrittenBorder)0) // We set it to 0 also for invalid filters. { size_t BlockEnd=WrapUp(BlockStart+BlockLength); FilterSrcMemory.resize(BlockLength); byte *Mem=FilterSrcMemory.data(); if (BlockStartUnpWrite(OutMem,BlockLength); UnpSomeRead=true; WrittenFileSize+=BlockLength; WrittenBorder=BlockEnd; WriteSizeLeft=WrapDown(UnpPtr-WrittenBorder); } } 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.resize(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=WrapUp(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE)); // 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 && WrapDown(WrPtr-UnpPtr)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.resize(DataSize); byte *DstData=FilterDstMemory.data(); // 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=WrapUp(StartPtr+BlockSize); } } 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=byte(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)); // 2024.01.04: In theory the valid block can have Header.BlockSize == 0 // and Header.TablePresent == false in case the only block purpose is to // store Header.LastBlockInFile flag if it didn't fit into previous block. // So we do not reject Header.BlockSize == 0. Though currently RAR doesn't // seem to issue such zero length blocks. if (CheckSum!=SavedCheckSum) return false; Header.BlockStart=Inp.InAddr; // We called Inp.faddbits(8) above, thus Header.BlockStart can't be 0 here. // So there is no overflow even if Header.BlockSize is 0. 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); uint DCodes=ExtraDist ? DCX : DCB; MakeDecodeTables(&Table[NC],&Tables.DD,DCodes); MakeDecodeTables(&Table[NC+DCodes],&Tables.LDD,LDC); MakeDecodeTables(&Table[NC+DCodes+LDC],&Tables.RD,RC); return true; } void Unpack::InitFilters() { Filters.clear(); } unrar/unpack50frag.cpp000666 000000 000000 00000005252 15026203746 013362 0ustar00000000 000000 FragmentedWindow::FragmentedWindow() { memset(Mem,0,sizeof(Mem)); memset(MemSize,0,sizeof(MemSize)); LastAllocated=0; } FragmentedWindow::~FragmentedWindow() { Reset(); } void FragmentedWindow::Reset() { LastAllocated=0; 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 (TotalSizeUnpPtr) { SrcPtr+=MaxWinSize; if (Distance>MaxWinSize || !FirstWinDone) { while (Length-- > 0) { (*this)[UnpPtr]=0; if (++UnpPtr>=MaxWinSize) UnpPtr-=MaxWinSize; } return; } } while (Length-- > 0) { (*this)[UnpPtr]=(*this)[SrcPtr]; if (++SrcPtr>=MaxWinSize) SrcPtr-=MaxWinSize; if (++UnpPtr>=MaxWinSize) UnpPtr-=MaxWinSize; } } 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=WrapUp(UnpPtr); // ProcessDecoded and maybe others can leave UnpPtr >= MaxWinSize 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->LengthLiteral)-1) { 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); size_t Distance=1; uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { // It is also possible to always use getbits64() here. if (DBits>36) Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); else Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; // Distance can be 0 for multiples of 4 GB as result of size_t // overflow in 32-bit build. Its lower 32-bit can also erroneously // fit into dictionary after truncating upper 32-bits. Replace such // invalid distances with -1, so CopyString sets 0 data for them. // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. if (sizeof(Distance)==4 && DBits>=30) Distance=(size_t)-1; } else { Distance+=D.Inp.getbits()>>(16-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 (ItemUnpPtr); PrevPtr=UnpPtr; if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); if (WrittenFileSize>DestUnpSize) return false; } if (Item->Type==UNPDT_LITERAL) { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) if (Item->Length==7 && UnpPtrLiteral); UnpPtr+=8; } else #endif for (uint I=0;I<=Item->Length;I++) Window[WrapUp(UnpPtr++)]=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) { size_t Distance=OldDist[Item->Distance]; for (size_t 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=(uint)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=WrapUp(UnpPtr); FirstWinDone|=(PrevPtr>UnpPtr); PrevPtr=UnpPtr; 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 (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); if (WrittenFileSize>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); size_t Distance=1; uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; Distance+=DistSlot; } else { DBits=DistSlot/2 - 1; Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { // It is also possible to always use getbits64() here. if (DBits>36) Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); else Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; // Distance can be 0 for multiples of 4 GB as result of size_t // overflow in 32-bit build. Its lower 32-bit can also erroneously // fit into dictionary after truncating upper 32-bits. Replace such // invalid distances with -1, so CopyString sets 0 data for them. // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. if (sizeof(Distance)==4 && DBits>=30) Distance=(size_t)-1; } 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; size_t 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 00000012337 15026203746 013556 0ustar00000000 000000 _forceinline void Unpack::InsertOldDist(size_t Distance) { OldDist[3]=OldDist[2]; OldDist[2]=OldDist[1]; OldDist[1]=OldDist[0]; OldDist[0]=Distance; } #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) #define UNPACK_COPY8 // We can copy 8 bytes at any position as uint64. #endif _forceinline void Unpack::CopyString(uint Length,size_t Distance) { size_t SrcPtr=UnpPtr-Distance; // Perform the correction here instead of "else", so matches crossing // the window beginning can also be processed by first "if" part. if (Distance>UnpPtr) // Unlike SrcPtr>=MaxWinSize, it catches invalid distances like 0xfffffff0 in 32-bit build. { // Same as WrapDown(SrcPtr), needed because of UnpPtr-Distance above. // We need the same condition below, so we expanded WrapDown() here. SrcPtr+=MaxWinSize; // About Distance>MaxWinSize check. // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize // in a malformed archive. Our WrapDown replacement above might not // correct it, so to prevent out of bound Window read we check it here. // Unlike SrcPtr>=MaxWinSize check, it allows MaxWinSize>0x80000000 // in 32-bit build, which could cause overflow in SrcPtr. // About !FirstWinDone check. // If first window hasn't filled yet and it points outside of window, // set data to 0 instead of copying preceding file data, so result doesn't // depend on previously extracted files in non-solid archive. if (Distance>MaxWinSize || !FirstWinDone) { // Fill area of specified length with 0 instead of returning. // So if only the distance is broken and rest of packed data is valid, // it preserves offsets and allows to continue extraction. // If we set SrcPtr to random offset instead, let's say, 0, // we still will be copying preceding file data if UnpPtr is also 0. while (Length-- > 0) { Window[UnpPtr]=0; UnpPtr=WrapUp(UnpPtr+1); } return; } } 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 UNPACK_COPY8 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. RawPut8(RawGet8(Src),Dest); 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[WrapUp(SrcPtr++)]; // We need to have masked UnpPtr after quit from loop, so it must not // be replaced with 'Window[WrapUp(UnpPtr++)]' UnpPtr=WrapUp(UnpPtr+1); } } _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 00000005150 15026203746 012573 0ustar00000000 000000 void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { // There must be 0 byte between owner and group strings. // Otherwise strlen call below wouldn't be safe. if (memchr(Arc.SubHead.SubData.data(),0,Arc.SubHead.SubData.size())==NULL) return; char *OwnerName=(char *)Arc.SubHead.SubData.data(); int OwnerSize=strlen(OwnerName)+1; int GroupSize=Arc.SubHead.SubData.size()-OwnerSize; char *GroupName=(char *)&Arc.SubHead.SubData[OwnerSize]; std::string GroupStr(GroupName,GroupName+GroupSize); 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(GroupStr.c_str()))==NULL) { uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); ErrHandler.SetErrorCode(RARX_WARNING); return; } uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; std::string NameA; WideToChar(FileName,NameA); #if defined(SAVE_LINKS) && !defined(_APPLE) if (lchown(NameA.c_str(),OwnerID,GroupID)!=0) #else if (chown(NameA.c_str(),OwnerID,GroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CREATE); } SetFileAttr(FileName,Attr); } void SetUnixOwner(Archive &Arc,const std::wstring &FileName) { // 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; } std::string NameA; WideToChar(FileName,NameA); #if defined(SAVE_LINKS) && !defined(_APPLE) if (lchown(NameA.c_str(),hd.UnixOwnerID,hd.UnixGroupID)!=0) #else if (chown(NameA.c_str(),hd.UnixOwnerID,hd.UnixGroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CREATE); } } unrar/volume.cpp000666 000000 000000 00000020330 15026203746 012375 0ustar00000000 000000 #include "rar.hpp" #ifdef RARDLL bool DllVolChange(CommandData *Cmd,std::wstring &NextName); static bool DllVolNotify(CommandData *Cmd,const std::wstring &NextName); #endif bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { CommandData *Cmd=Arc.GetCommandData(); 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); } bool PrevVolEncrypted=Arc.Encrypted; int64 PosBeforeClose=Arc.Tell(); if (DataIO!=NULL) DataIO->ProcessedArcSize+=DataIO->LastArcSize; Arc.Close(); std::wstring NextName=Arc.FileName; NextVolumeName(NextName,!Arc.NewNumbering); #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif bool OldSchemeTested=false; bool FailedOpen=false; // No more next volume open attempts if true. #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. // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause // if we could open the next volume with FMF_OPENEXCLUSIVE. But another // developer asked us to return the previous behavior and always prompt // for confirmation. They want to control when unrar continues, because // the next file might not be fully decoded yet. They write chunks of data // and then close the file again until the next chunk comes in. if (Cmd->VolumePause && !uiAskNextVolume(NextName)) FailedOpen=true; #endif #ifdef _UNIX // open() function in Unix can open a directory. But if directory has a name // of next volume, it would result in read error and Retry/Quit prompt. // So we skip directories in advance here. This check isn't needed // in Windows, where opening directories fails here. if (FileExist(NextName) && IsDir(GetFileAttr(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. std::wstring AltNextName=Arc.FileName; NextVolumeName(AltNextName,true); OldSchemeTested=true; if (Arc.Open(AltNextName,OpenMode)) { NextName=AltNextName; break; } } #ifdef RARDLL if (!DllVolChange(Cmd,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)) #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.c_str()); Arc.CheckArc(true); #ifdef RARDLL if (!DllVolNotify(Cmd,NextName)) return false; #endif if (Arc.Encrypted!=PrevVolEncrypted) { // There is no legitimate reason for encrypted header state to be // changed in the middle of volume sequence. So we abort here to prevent // replacing an encrypted header volume to unencrypted and adding // unexpected files by third party to encrypted extraction. uiMsg(UIERROR_BADARCHIVE,Arc.FileName); ErrHandler.Exit(RARX_BADARC); } 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 && !Cmd->DisableNames) { mprintf(St(MExtrPoints),Arc.FileHead.FileName.c_str()); if (!Cmd->DisablePercentage) mprintf(L" "); } if (DataIO!=NULL) { if (HeaderType==HEAD_ENDARC) DataIO->UnpVolume=false; else { DataIO->UnpVolume=hd->SplitAfter; DataIO->SetPackedSizeToRead(hd->PackSize); } DataIO->AdjustTotalArcSize(&Arc); // 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 bool DllVolChange(CommandData *Cmd,std::wstring &NextName) { bool DllVolChanged=false,DllVolAborted=false; if (Cmd->Callback!=NULL) { std::wstring OrgNextName=NextName; std::vector NameBuf(MAXPATHSIZE); std::copy(NextName.data(), NextName.data() + NextName.size() + 1, NameBuf.begin()); if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NameBuf.data(),RAR_VOL_ASK)==-1) DllVolAborted=true; else { NextName=NameBuf.data(); if (OrgNextName!=NextName) DllVolChanged=true; else { std::string NextNameA; WideToChar(NextName,NextNameA); std::string OrgNextNameA=NextNameA; std::vector NameBufA(MAXPATHSIZE); std::copy(NextNameA.data(), NextNameA.data() + NextNameA.size() + 1, NameBufA.begin()); if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NameBufA.data(),RAR_VOL_ASK)==-1) DllVolAborted=true; else { NextNameA=NameBufA.data(); if (OrgNextNameA!=NextNameA) { // 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); DllVolChanged=true; } } } } } if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) { std::string NextNameA; WideToChar(NextName,NextNameA); std::vector NameBufA(MAXPATHSIZE); std::copy(NextNameA.data(), NextNameA.data() + NextNameA.size() + 1, NameBufA.begin()); int RetCode=Cmd->ChangeVolProc(NameBufA.data(),RAR_VOL_ASK); if (RetCode==0) DllVolAborted=true; else { NextNameA=NameBufA.data(); CharToWide(NextNameA,NextName); } } // 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 static bool DllVolNotify(CommandData *Cmd,const std::wstring &NextName) { std::string NextNameA; WideToChar(NextName,NextNameA); if (Cmd->Callback!=NULL) { if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName.data(),RAR_VOL_NOTIFY)==-1) return false; if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA.data(),RAR_VOL_NOTIFY)==-1) return false; } if (Cmd->ChangeVolProc!=NULL) { int RetCode=Cmd->ChangeVolProc((char *)NextNameA.data(),RAR_VOL_NOTIFY); if (RetCode==0) return false; } return true; } #endif unrar/win32acl.cpp000666 000000 000000 00000005334 15026203746 012517 0ustar00000000 000000 static void SetACLPrivileges(); static bool ReadSacl=false; #ifndef SFX_MODULE void ExtractACL20(Archive &Arc,const std::wstring &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); std::vector 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.c_str(),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 std::wstring &FileName) { std::vector SubData; if (!Arc.ReadSubData(&SubData,NULL,false)) 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.c_str(),si,sd); if (!SetCode) { std::wstring LongName; if (GetWinLongPath(FileName,LongName)) SetCode=SetFileSecurity(LongName.c_str(),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; } unrar/win32lnk.cpp000666 000000 000000 00000015160 15026203746 012542 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 std::wstring &SubstName=hd->RedirName; size_t SubstLength=SubstName.size(); // REPARSE_DATA_BUFFER receives both SubstName and PrintName strings, // thus "*2" below. PrintName is either shorter or same length as SubstName. const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+((DWORD)SubstLength+1)*2*sizeof(wchar); std::vector Buf(BufSize); REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)Buf.data(); // Remove \??\ NTFS junction prefix of present. bool WinPrefix=SubstName.rfind(L"\\??\\",0)!=std::wstring::npos; std::wstring PrintName=WinPrefix ? SubstName.substr(4):SubstName; if (WinPrefix && PrintName.rfind(L"UNC\\",0)!=std::wstring::npos) PrintName=L"\\"+PrintName.substr(3); // Convert UNC\server\share to \\server\share. size_t PrintLength=PrintName.size(); 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))) { uiMsg(UIERROR_SKIPUNSAFELINK,hd->FileName,hd->RedirName); ErrHandler.SetErrorCode(RARX_WARNING); return false; } CreatePath(Name,true,Cmd->DisableNames); // Overwrite prompt was already issued and confirmed earlier, so we can // remove existing symlink or regular file here. PrepareToDelete was also // called earlier inside of uiAskReplaceEx. if (FileExist(Name)) if (IsDir(GetFileAttr(Name))) DelDir(Name); else DelFile(Name); // 'DirTarget' check is important for Unix symlinks to directories. // Unix symlinks do not have their own 'directory' attribute. if (hd->Dir || hd->DirTarget) { if (!CreateDir(Name)) { uiMsg(UIERROR_DIRCREATE,L"",Name); ErrHandler.SetErrorCode(RARX_CREATE); return false; } } else { HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile == INVALID_HANDLE_VALUE) { ErrHandler.CreateErrorMsg(Name); 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.data()); rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName.data()); } 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.data()); rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName.data()); 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) { ErrHandler.CreateErrorMsg(Name); ErrHandler.SetErrorCode(RARX_CREATE); 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,L"",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 00000015720 15026203746 012563 0ustar00000000 000000 #ifdef _WIN_ALL // StreamName must include the leading ':'. static bool IsNtfsProhibitedStream(const std::wstring &StreamName) { // 2024.03.14: We replaced the predefined names check with simpler // "no more than a single colon" check. Second colon could be used to // define the type of alternate stream, but RAR archives work only with // data streams and do not store :$DATA type in archive. It is assumed. // So there is no legitimate use for stream type inside of archive, // but it can be abused to hide the actual file data in file::$DATA // or hide the actual MOTW data in Zone.Identifier:$DATA. uint ColonCount=0; for (wchar Ch:StreamName) if (Ch==':' && ++ColonCount>1) return true; return false; /* const wchar *Reserved[]{ L"::$ATTRIBUTE_LIST",L"::$BITMAP",L"::$DATA",L"::$EA",L"::$EA_INFORMATION", L"::$FILE_NAME",L"::$INDEX_ALLOCATION",L":$I30:$INDEX_ALLOCATION", L"::$INDEX_ROOT",L"::$LOGGED_UTILITY_STREAM",L":$EFS:$LOGGED_UTILITY_STREAM", L":$TXF_DATA:$LOGGED_UTILITY_STREAM",L"::$OBJECT_ID",L"::$REPARSE_POINT" }; for (const wchar *Name : Reserved) if (wcsicomp(StreamName,Name)==0) return true; return false; */ } #endif #if !defined(SFX_MODULE) && defined(_WIN_ALL) void ExtractStreams20(Archive &Arc,const std::wstring &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; } std::wstring StreamName; CharToWide(Arc.StreamHead.StreamName,StreamName); if (StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } // Convert single character names like f:stream to .\f:stream to // resolve the ambiguity with drive letters. std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName; FullName+=StreamName; #ifdef PROPAGATE_MOTW // 2022.10.31: Can't easily read RAR 2.0 stream data here, so if we already // propagated the archive Zone.Identifier stream, also known as Mark of // the Web, to extracted file, we do not overwrite it here. if (Arc.Motw.IsNameConflicting(StreamName)) return; // 2024.02.03: Prevent using Zone.Identifier:$DATA to overwrite Zone.Identifier // according to ZDI-CAN-23156 Trend Micro report. // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream((). // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0) // return; #endif if (IsNtfsProhibitedStream(StreamName)) return; FindData FD; bool HostFound=FindFile::FastFind(FileName,&FD); if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName)) { 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(); } // Restoring original file timestamps. File HostFile; if (HostFound && 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. if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,FD.FileAttr); } #endif #ifdef _WIN_ALL void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode) { std::wstring StreamName=GetStreamNameNTFS(Arc); if (StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (TestMode) { File CurFile; Arc.ReadSubData(nullptr,&CurFile,true); return; } // Convert single character names like f:stream to .\f:stream to // resolve the ambiguity with drive letters. std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName; FullName+=StreamName; #ifdef PROPAGATE_MOTW // 2022.10.31: If we already propagated the archive Zone.Identifier stream, // also known as Mark of the Web, to extracted file, we overwrite it here // only if file zone is stricter. Received a user request for such behavior. std::string ParsedMotw; if (Arc.Motw.IsNameConflicting(StreamName)) { // Do not worry about excessive memory allocation, ReadSubData prevents it. std::vector FileMotw; if (!Arc.ReadSubData(&FileMotw,nullptr,false)) return; ParsedMotw.assign(FileMotw.begin(),FileMotw.end()); // We already set the archive stream. If file stream value isn't more // restricted, we do not want to write it over the existing archive stream. if (!Arc.Motw.IsFileStreamMoreSecure(ParsedMotw)) return; } // 2024.02.03: Prevent using :Zone.Identifier:$DATA to overwrite :Zone.Identifier // according to ZDI-CAN-23156 Trend Micro report. // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream((). // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0) // return; #endif if (IsNtfsProhibitedStream(StreamName)) return; FindData FD; bool HostFound=FindFile::FastFind(FileName,&FD); if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName)) { #ifdef PROPAGATE_MOTW if (!ParsedMotw.empty()) { // The archive propagated security zone is either missing // or less strict than file one. Write the file security zone here. CurFile.Write(ParsedMotw.data(),ParsedMotw.size()); CurFile.Close(); } else #endif if (Arc.ReadSubData(nullptr,&CurFile,false)) CurFile.Close(); } // Restoring original file timestamps. File HostFile; if (HostFound && 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. if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,FD.FileAttr); } #endif std::wstring GetStreamNameNTFS(Archive &Arc) { std::wstring Dest; if (Arc.Format==RARFMT15) Dest=RawToWide(Arc.SubHead.SubData); else { std::string Src(Arc.SubHead.SubData.begin(),Arc.SubHead.SubData.end()); UtfToWide(Src.data(),Dest); } return Dest; } unrar/archive.hpp000666 000000 000000 00000010441 15026203744 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(std::wstring &Name); void ConvertFileHeader(FileHeader *hd); size_t ReadHeader14(); size_t ReadHeader15(); size_t ReadHeader50(); void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb); void RequestArcPassword(RarCheckPassword *SelPwd); void UnexpEndArcMsg(); void BrokenHeaderMsg(); void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info); bool DoGetComment(std::wstring &CmtData); bool ReadCommentData(std::wstring &CmtData); #if !defined(RAR_NOCRYPT) CryptData HeadersCrypt; #endif ComprDataIO SubDataIO; bool DummyCmd; CommandData *Cmd; int RecoveryPercent; RarTime LatestTime; int LastReadBlock; HEADER_TYPE CurHeaderType; bool SilentOpen; #ifdef USE_QOPEN QuickOpen QOpen; bool ProhibitQOpen; #endif public: Archive(CommandData *InitCmd=nullptr); ~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(); int GetRecoveryPercent() {return RecoveryPercent;} size_t ReadHeader(); void CheckArc(bool EnableBroken); void CheckOpen(const std::wstring &Name); bool WCheckOpen(const std::wstring &Name); bool GetComment(std::wstring &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(const byte *SrcData,uint64 DataSize,File *SrcFile, const wchar *Name,uint Flags); bool ReadSubData(std::vector *UnpData,File *DestFile,bool TestMode); HEADER_TYPE GetHeaderType() {return CurHeaderType;} CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} #ifdef USE_QOPEN bool Open(const std::wstring &Name,uint Mode=FMF_READ) override; int Read(void *Data,size_t Size) override; void Seek(int64 Offset,int Method) override; int64 Tell() override; void QOpenUnload() {QOpen.Unload();} void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} #endif static uint64 GetWinSize(uint64 Size,uint &Flags); // Needed to see wstring based Open from File. Otherwise compiler finds // Open in Archive and doesn't check the base class overloads. using File::Open; BaseBlock ShortBlock; MarkHeader MarkHead; MainHeader MainHead; CryptHeader CryptHead; FileHeader FileHead; EndArcHeader EndArcHead; SubBlockHeader SubBlockHead; FileHeader SubHead; CommentHeader CommHead; ProtectHeader ProtectHead; 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; // Total size of files adding to archive. Might also include the size of // files repacked in solid archive. uint64 AddingFilesSize; uint64 AddingHeadersSize; bool NewArchive; std::wstring FirstVolumeName; #ifdef PROPAGATE_MOTW MarkOfTheWeb Motw; #endif }; #endif unrar/blake2s.hpp000666 000000 000000 00000005263 15026203744 012424 0ustar00000000 000000 // Based on public domain code written in 2012 by Samuel Neves #ifndef _RAR_BLAKE2_ #define _RAR_BLAKE2_ #define BLAKE2_DIGEST_SIZE 32 #define BLAKE2_THREADS_NUMBER 8 // Use constexpr instead of enums for -std=c++20 compatibility. constexpr size_t BLAKE2S_BLOCKBYTES = 64; constexpr size_t 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 { // Use constexpr instead of enums, because otherwise clang -std=c++20 // issues a warning about "arithmetic between different enumeration types" // in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration. static constexpr size_t BLAKE_ALIGNMENT = 64; // buffer and uint32 h[8], t[2], f[2]; // 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip. // It might differ in later versions. static constexpr size_t 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 00000006135 15026203744 012475 0ustar00000000 000000 #ifndef _RAR_CMDDATA_ #define _RAR_CMDDATA_ #if defined(_WIN_ALL) && !defined(SFX_MODULE) // In Windows we implement our own command line parser to avoid replacing // \" by " in standard parser. Such replacing corrupts destination paths // like "dest path\" in extraction commands. #define CUSTOM_CMDLINE_PARSER #endif #define DefaultStoreList L"7z;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst" enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1}; class CommandData:public RAROptions { private: void ProcessSwitch(const wchar *Switch); void BadSwitch(const wchar *Switch); uint GetExclAttr(const wchar *Str,bool &Dir); #if !defined(SFX_MODULE) void SetTimeFilters(const wchar *Mod,bool Before,bool Age); void SetStoreTimeMode(const wchar *S); #endif int64 GetVolSize(const wchar *S,uint DefMultiplier); 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(const wchar *ArgW); void ParseDone(); void ParseEnvVar(); void ReadConfig(); void PreprocessArg(const wchar *Arg); void ProcessSwitchesString(const std::wstring &Str); void OutTitle(); void OutHelp(RAR_EXIT ExitCode); bool IsSwitch(int Ch); bool ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList); static bool CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode); bool ExclDirByAttr(uint FileAttr); bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta); bool SizeCheck(int64 Size); bool AnyFiltersActive(); int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, bool Flags,std::wstring *MatchedArg); void ProcessCommand(); void AddArcName(const std::wstring &Name); bool GetArcName(wchar *Name,int MaxSize); bool GetArcName(std::wstring &Name); #ifndef SFX_MODULE void ReportWrongSwitches(RARFORMAT Format); #endif void GetBriefMaskList(const std::wstring &Masks,StringList &Args); std::wstring Command; std::wstring ArcName; std::wstring ExtrPath; std::wstring TempPath; std::wstring SFXModule; std::wstring CommentFile; std::wstring ArcPath; // For -ap. std::wstring ExclArcPath; // For -ep4 switch. std::wstring LogName; std::wstring EmailTo; // Read data from stdin and store in archive under a name specified here // when archiving. Read an archive from stdin if any non-empty string // is specified here when extracting. std::wstring UseStdin; StringList FileArgs; StringList ExclArgs; StringList InclArgs; StringList ArcNames; StringList StoreArgs; #ifdef PROPAGATE_MOTW StringList MotwList; // Extensions to assign the mark of the web. #endif SecPassword Password; std::vector NextVolSizes; #ifdef RARDLL std::wstring DllDestName; #endif }; #endif unrar/coder.hpp000666 000000 000000 00000001160 15026203744 012165 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 byte GetChar(); uint low, code, range; struct SUBRANGE { uint LowCount, HighCount, scale; } SubRange; Unpack *UnpackRead; }; unrar/compress.hpp000666 000000 000000 00000004372 15026203744 012734 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 MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3; static const uint LOW_DIST_REP_COUNT = 16; static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ static const uint DCB = 64; // Base distance codes up to 4 GB. static const uint DCX = 80; // Extended distance codes up to 1 TB. static const uint LDC = 16; static const uint RC = 44; static const uint HUFF_TABLE_SIZEB = NC + DCB + RC + LDC; static const uint HUFF_TABLE_SIZEX = NC + DCX + 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 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_TEXT, // These values can be changed. FILTER_LONGRANGE,FILTER_EXHAUSTIVE,FILTER_NONE }; #endif unrar/consio.hpp000666 000000 000000 00000001451 15026203744 012366 0ustar00000000 000000 #ifndef _RAR_CONSIO_ #define _RAR_CONSIO_ void InitConsole(); void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); void ProhibitConsoleInput(); void OutComment(const std::wstring &Comment); bool IsConsoleOutputPresent(); #ifndef SILENT bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &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 void getwstr(std::wstring &str) {} #else void mprintf(const wchar *fmt,...); void eprintf(const wchar *fmt,...); void Alarm(); int Ask(const wchar *AskStr); void getwstr(std::wstring &str); #endif #endif unrar/crc.hpp000666 000000 000000 00000000523 15026203744 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 00000010731 15026203744 012236 0ustar00000000 000000 #ifndef _RAR_CRYPT_ #define _RAR_CRYPT_ enum CRYPT_METHOD { CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50, CRYPT_UNKNOWN }; #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]; KDF5CacheItem() {Clean();} ~KDF5CacheItem() {Clean();} void Clean() { cleandata(Salt,sizeof(Salt)); cleandata(Key,sizeof(Key)); cleandata(&Lg2Count,sizeof(Lg2Count)); cleandata(PswCheckValue,sizeof(PswCheckValue)); cleandata(HashKeyValue,sizeof(HashKeyValue)); } }; struct KDF3CacheItem { SecPassword Pwd; byte Salt[SIZE_SALT30]; byte Key[16]; byte Init[16]; bool SaltPresent; KDF3CacheItem() {Clean();} ~KDF3CacheItem() {Clean();} void Clean() { cleandata(Salt,sizeof(Salt)); cleandata(Key,sizeof(Key)); cleandata(Init,sizeof(Init)); cleandata(&SaltPresent,sizeof(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); bool 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(); bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt, byte *HashKey,byte *PswCheck); void SetCmt13Encryption(); void EncryptBlock(byte *Buf,size_t Size); void DecryptBlock(byte *Buf,size_t Size); static void SetSalt(byte *Salt,size_t SaltSize); }; class CheckPassword { public: enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW}; virtual CONFIDENCE GetConfidence()=0; virtual bool Check(SecPassword *Password)=0; }; class RarCheckPassword:public CheckPassword { private: CryptData *Crypt; uint Lg2Count; byte Salt[SIZE_SALT50]; byte InitV[SIZE_INITV]; byte PswCheck[SIZE_PSWCHECK]; public: RarCheckPassword() { Crypt=NULL; } ~RarCheckPassword() { delete Crypt; } void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck) { if (Crypt==NULL) Crypt=new CryptData; memcpy(this->Salt,Salt,sizeof(this->Salt)); memcpy(this->InitV,InitV,sizeof(this->InitV)); this->Lg2Count=Lg2Count; memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck)); } bool IsSet() {return Crypt!=NULL;} // RAR5 provides the higly reliable 64 bit password verification value. CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;} bool Check(SecPassword *Password) { byte PswCheck[SIZE_PSWCHECK]; Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck); return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0; } }; 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 00000011730 15026203744 011650 0ustar00000000 000000 #ifndef _UNRAR_DLL_ #define _UNRAR_DLL_ #pragma pack(push, 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 ERAR_LARGE_DICT 25 #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 9 #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; wchar_t *ArcNameEx; unsigned int ArcNameExSize; wchar_t *FileNameEx; unsigned int FileNameExSize; unsigned int Reserved[982]; }; 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 #define ROADOF_KEEPBROKEN 0x0001 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 OpFlags; wchar_t *CmtBufW; wchar_t *MarkOfTheWeb; unsigned int Reserved[23]; }; enum UNRARCALLBACK_MESSAGES { UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, UCM_NEEDPASSWORDW,UCM_LARGEDICT }; 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(pop) #endif unrar/encname.hpp000666 000000 000000 00000000713 15026203744 012502 0ustar00000000 000000 #ifndef _RAR_ENCNAME_ #define _RAR_ENCNAME_ class EncodeFileName { private: void AddFlags(byte Value,std::vector &EncName); byte Flags; uint FlagBits; size_t FlagsPos; size_t DestSize; public: EncodeFileName(); void Encode(const std::string &Name,const std::wstring &NameW,std::vector &EncName); void Decode(const char *Name,size_t NameSize,const byte *EncName,size_t EncSize,std::wstring &NameW); }; #endif unrar/errhnd.hpp000666 000000 000000 00000005114 15026203744 012356 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_READ = 12, RARX_BADARC = 13, RARX_USERBREAK = 255 }; class ErrorHandler { private: RAR_EXIT ExitCode; uint ErrCount; bool EnableBreak; bool Silent; bool DisableShutdown; // Shutdown is not suitable after last error. bool ReadErrIgnoreAll; public: ErrorHandler(); void Clean(); void MemoryError(); void OpenError(const std::wstring &FileName); void CloseError(const std::wstring &FileName); void ReadError(const std::wstring &FileName); void AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit); void WriteError(const std::wstring &ArcName,const std::wstring &FileName); void WriteErrorFAT(const std::wstring &FileName); bool AskRepeatWrite(const std::wstring &FileName,bool DiskFull); void SeekError(const std::wstring &FileName); void GeneralErrMsg(const wchar *fmt,...); void MemoryErrorMsg(); void OpenErrorMsg(const std::wstring &FileName); void OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); void CreateErrorMsg(const std::wstring &FileName); void CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); void ReadErrorMsg(const std::wstring &FileName); void ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); void WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); void ArcBrokenMsg(const std::wstring &ArcName); void ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName); void UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &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(std::wstring &Msg); 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 00000001373 15026203744 012553 0ustar00000000 000000 #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName); bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink); #ifdef _UNIX void SetUnixOwner(Archive &Arc,const std::wstring &FileName); #endif bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting); std::wstring GetStreamNameNTFS(Archive &Arc); void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name); void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name); void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name); #endif unrar/extract.hpp000666 000000 000000 00000005721 15026203744 012552 0ustar00000000 000000 #ifndef _RAR_EXTRACT_ #define _RAR_EXTRACT_ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; class CmdExtract { private: struct ExtractRef { std::wstring RefName; std::wstring TmpName; uint64 RefCount; }; std::vector RefList; struct AnalyzeData { std::wstring StartName; uint64 StartPos; std::wstring EndName; uint64 EndPos; } Analyze; bool ArcAnalyzed; void FreeAnalyzeData(); EXTRACT_ARC_CODE ExtractArchive(); bool ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize); void ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName); #ifdef RARDLL bool ExtrDllGetPassword(); #else bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); #endif void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName); bool ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly); bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName); #ifndef SFX_MODULE void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering); void GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName); #endif bool CheckWinLimit(Archive &Arc,std::wstring &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; bool UseExactVolName; // 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; std::wstring ArcName; bool GlobalPassword; bool PrevProcessed; // If previous file was successfully extracted or tested. std::wstring DestFileName; bool SuppressNoFilesMessage; // In Windows it is set to true if at least one symlink with ".." // in target was extracted. bool ConvertSymlinkPaths; // Last path checked for symlinks. We use it to improve the performance, // so we do not check recently checked folders again. std::wstring LastCheckedSymlink; #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 00000000514 15026203744 012664 0ustar00000000 000000 #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name, bool *UserReject,int64 FileSize=INT64NDF, RarTime *FileTime=NULL,bool WriteOnly=false); #if defined(_WIN_ALL) bool UpdateExistingShortName(const std::wstring &Name); #endif #endif unrar/file.hpp000666 000000 000000 00000012200 15026203744 012005 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 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 }; enum FILE_READ_ERROR_MODE { FREM_ASK, // Propose to use the already read part, retry or abort. FREM_TRUNCATE, // Use the already read part without additional prompt. FREM_IGNORE // Try to skip unreadable block and read further. }; class File { private: FileHandle hFile; bool LastWrite; FILE_HANDLETYPE HandleType; // If we read the user input in console prompts from stdin, we shall // process the available line immediately, not waiting for rest of data. // Otherwise apps piping user responses to multiple Ask() prompts can // hang if no more data is available yet and pipe isn't closed. // If we read RAR archive or other file data from stdin, we shall collect // the entire requested block as long as pipe isn't closed, so we get // complete archive headers, not split between different reads. bool LineInput; bool SkipClose; FILE_READ_ERROR_MODE ReadErrorMode; bool NewFile; bool AllowDelete; bool AllowExceptions; #ifdef _WIN_ALL bool NoSequentialRead; uint CreateMode; #endif bool PreserveAtime; bool TruncatedAfterReadError; int64 CurFilePos; // Used for forward seeks in stdin files. protected: bool OpenShared; // Set by 'Archive' class. public: std::wstring FileName; 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 std::wstring &Name,uint Mode=FMF_READ); void TOpen(const std::wstring &Name); bool WOpen(const std::wstring &Name); bool Create(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); void TCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); bool WCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); virtual bool Close(); // 'virtual' for MultiFile class. bool Delete(); bool Rename(const std::wstring &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 std::wstring &Name,RarTime *ftm,RarTime *fta); #ifdef _UNIX static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta); #endif void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. virtual int64 FileLength(); // 'virtual' for MultiFile class. void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} void SetLineInputMode(bool Mode) {LineInput=Mode;} FILE_HANDLETYPE GetHandleType() {return HandleType;} bool IsSeekable() {return HandleType!=FILE_HANDLESTD;} bool IsDevice(); static bool RemoveCreated(); FileHandle GetHandle() {return hFile;} void SetHandle(FileHandle Handle) {Close();hFile=Handle;} void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;} int64 Copy(File &Dest,int64 Length=INT64NDF); void SetAllowDelete(bool Allow) {AllowDelete=Allow;} void SetExceptions(bool Allow) {AllowExceptions=Allow;} void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;} bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;} #ifdef _UNIX int GetFD() { #ifdef FILE_USE_OPEN return hFile; #else return fileno(hFile); #endif } #endif static size_t CopyBufferSize() { // Values in 0x100000 - 0x400000 range are ok, but multithreaded CRC32 // seems to benefit from 0x400000, especially on ARM CPUs. return 0x400000; } }; #endif unrar/filefn.hpp000666 000000 000000 00000003525 15026203744 012343 0ustar00000000 000000 #ifndef _RAR_FILEFN_ #define _RAR_FILEFN_ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr); bool CreateDir(const std::wstring &Name); bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent); void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta); bool IsRemovable(const std::wstring &Name); #ifndef SFX_MODULE int64 GetFreeDisk(const std::wstring &Name); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) bool IsFAT(const std::wstring &Root); #endif bool FileExist(const std::wstring &Name); bool WildFileExist(const std::wstring &Name); bool IsDir(uint Attr); bool IsUnreadable(uint Attr); bool IsLink(uint Attr); void SetSFXMode(const std::wstring &FileName); void EraseDiskContents(const std::wstring &FileName); bool IsDeleteAllowed(uint FileAttr); void PrepareToDelete(const std::wstring &Name); uint GetFileAttr(const std::wstring &Name); bool SetFileAttr(const std::wstring &Name,uint Attr); bool MkTemp(std::wstring &Name,const wchar *Ext); 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 std::wstring &SrcName,const std::wstring &DestName); bool DelFile(const std::wstring &Name); bool DelDir(const std::wstring &Name); #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool SetFileCompression(const std::wstring &Name,bool State); bool SetFileCompression(HANDLE hFile,bool State); void ResetFileCache(const std::wstring &Name); #endif // Keep it here and not in extinfo.cpp, because it is invoked from Zip.SFX too. bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked); #endif unrar/filestr.hpp000666 000000 000000 00000000531 15026203744 012542 0ustar00000000 000000 #ifndef _RAR_FILESTR_ #define _RAR_FILESTR_ bool ReadTextFile( const std::wstring &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 00000001656 15026203744 012023 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 { std::wstring Name; 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 std::wstring &Mask,FindData *fd); #endif std::wstring FindMask; bool FirstCall; #ifdef _WIN_ALL HANDLE hFind; #else DIR *dirp; #endif public: FindFile(); ~FindFile(); void SetMask(const std::wstring &Mask); bool Next(FindData *fd,bool GetSymLink=false); static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false); }; #endif unrar/getbits.hpp000666 000000 000000 00000003774 15026203744 012547 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() { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) uint32 BitField=RawGetBE4(InBuf+InAddr); BitField >>= (16-InBit); #else uint BitField=(uint)InBuf[InAddr] << 16; BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+2]; BitField >>= (8-InBit); #endif 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=RawGetBE4(InBuf+InAddr); BitField <<= InBit; BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); return BitField & 0xffffffff; } // Return 64 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. uint64 getbits64() { uint64 BitField=RawGetBE8(InBuf+InAddr); BitField <<= InBit; BitField|=(uint)InBuf[InAddr+8] >> (8-InBit); return BitField; } 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 15026203744 012330 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 00000003444 15026203744 012023 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); // Use the const member, so types on both sides of "==" match. // Otherwise clang -std=c++20 issues "ambiguity is between a regular call // to this operator and a call with the argument order reversed" warning. bool operator == (const HashValue &cmp) const; // Not actually used now. Const member for same reason as operator == above. // Can be removed after switching to C++20, which automatically provides "!=" // if operator == is defined. bool operator != (const HashValue &cmp) const {return !(*this==cmp);} HASH_TYPE Type; union { uint CRC32; byte Digest[SHA256_DIGEST_SIZE]; }; }; #ifdef RAR_SMP class ThreadPool; class DataHash; #endif class DataHash { public: struct CRC32ThreadData { void *Data; size_t DataSize; uint DataCRC; }; private: void UpdateCRC32MT(const void *Data,size_t DataSize); uint BitReverse32(uint N); uint gfMulCRC(uint A, uint B); uint gfExpCRC(uint N); // Speed gain seems to vanish above 8 CRC32 threads. static const uint CRC32_POOL_THREADS=8; // Thread pool must allow at least BLAKE2_THREADS_NUMBER threads. static const uint HASH_POOL_THREADS=Max(BLAKE2_THREADS_NUMBER,CRC32_POOL_THREADS); HASH_TYPE HashType; uint CurCRC32; blake2sp_state *blake2ctx; #ifdef RAR_SMP ThreadPool *ThPool; uint MaxThreads; #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 00000022201 15026203744 012503 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 // Smallest RAR 4.x block size. #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 #define SIZEOF_PROTECTHEAD 26 #define SIZEOF_STREAMHEAD 26 #define VER_PACK 29U #define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive. #define VER_PACK7 70U // It is stored as 1, but we subtract 70 when saving an archive. #define VER_UNPACK 29U #define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive. #define VER_UNPACK7 70U // It is stored as 1, but we add 50 when reading an archive. #define VER_UNKNOWN 9999U // Just some large value. #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 service haeders, mostly outdated and not supported. 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; } // We use it to assign this block data to inherited blocks. // Such function seems to be cleaner than '(BaseBlock&)' cast or adding // 'using BaseBlock::operator=;' to every inherited header. void SetBaseBlock(BaseBlock &Src) { *this=Src; } }; 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. size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field. std::wstring OrigName; // Original archive name. RarTime OrigTime; // Original archive time. void Reset(); }; struct FileHeader:BlockHeader { byte HostOS; uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5. byte Method; union { uint FileAttr; uint SubFlags; }; std::wstring FileName; std::vector SubData; RarTime mtime; RarTime ctime; RarTime atime; int64 PackSize; int64 UnpSize; int64 MaxSize; // Reserve packed and unpacked 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. uint64 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 children subheader blocks in archives. bool SubBlock; HOST_SYSTEM_TYPE HSType; FILE_SYSTEM_REDIRECT RedirType; std::wstring RedirName; 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 FileName==Name; } // 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 EAHeader:SubBlockHeader { uint UnpSize; byte UnpVer; byte Method; uint EACRC; }; struct StreamHeader:SubBlockHeader { uint UnpSize; byte UnpVer; byte Method; uint StreamCRC; ushort StreamNameSize; std::string StreamName; }; #endif unrar/headers5.hpp000666 000000 000000 00000011704 15026203744 012576 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 0x00000001 // Version of compression algorithm. #define FCI_ALGO_BIT1 0x00000002 // 0 .. 63. #define FCI_ALGO_BIT2 0x00000004 #define FCI_ALGO_BIT3 0x00000008 #define FCI_ALGO_BIT4 0x00000010 #define FCI_ALGO_BIT5 0x00000020 #define FCI_SOLID 0x00000040 // Solid flag. #define FCI_METHOD_BIT0 0x00000080 // Compression method. #define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used). #define FCI_METHOD_BIT2 0x00000200 #define FCI_DICT_BIT0 0x00000400 // Dictionary size. #define FCI_DICT_BIT1 0x00000800 // 128 KB .. 1 TB. #define FCI_DICT_BIT2 0x00001000 #define FCI_DICT_BIT3 0x00002000 #define FCI_DICT_BIT4 0x00004000 #define FCI_DICT_FRACT0 0x00008000 // Dictionary fraction in 1/32 of size. #define FCI_DICT_FRACT1 0x00010000 #define FCI_DICT_FRACT2 0x00020000 #define FCI_DICT_FRACT3 0x00040000 #define FCI_DICT_FRACT4 0x00080000 #define FCI_RAR5_COMPAT 0x00100000 // RAR7 compression flags and RAR5 compression algorithm. // Main header extra field values. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. #define MHEXTRA_METADATA 0x02 // Archive metadata. // Flags for MHEXTRA_LOCATOR. #define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. #define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. // Flags for MHEXTRA_METADATA. #define MHEXTRA_METADATA_NAME 0x01 // Archive name is present. #define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present. #define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format. #define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision. // 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 00000000525 15026203745 012053 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(); // Replace it with actual check when available. bool IsWindows11OrGreater(); #endif unrar/largepage.hpp000666 000000 000000 00000002425 15026203745 013026 0ustar00000000 000000 #ifndef _RAR_LARGEPAGE_ #define _RAR_LARGEPAGE_ class LargePageAlloc { private: static constexpr const wchar *LOCKMEM_SWITCH=L"isetup_privilege_lockmem"; void* new_large(size_t Size); bool delete_large(void *Addr); #ifdef _WIN_ALL std::vector LargeAlloc; SIZE_T PageSize; #endif bool UseLargePages; public: LargePageAlloc(); void AllowLargePages(bool Allow); static bool IsPrivilegeAssigned(); static bool AssignPrivilege(); static bool AssignPrivilegeBySid(const std::wstring &Sid); static bool AssignConfirmation(); static bool ProcessSwitch(CommandData *Cmd,const wchar *Switch) { if (Switch[0]==LOCKMEM_SWITCH[0]) { size_t Length=wcslen(LOCKMEM_SWITCH); if (wcsncmp(Switch,LOCKMEM_SWITCH,Length)==0) { LargePageAlloc::AssignPrivilegeBySid(Switch+Length); return true; } } return false; } template T* new_l(size_t Size,bool Clear=false) { T *Allocated=(T*)new_large(Size*sizeof(T)); if (Allocated==nullptr) Allocated=Clear ? new T[Size]{} : new T[Size]; return Allocated; } template void delete_l(T *Addr) { if (!delete_large(Addr)) delete[] Addr; } }; #endif unrar/list.hpp000666 000000 000000 00000000123 15026203745 012043 0ustar00000000 000000 #ifndef _RAR_LIST_ #define _RAR_LIST_ void ListArchive(CommandData *Cmd); #endif unrar/loclang.hpp000666 000000 000000 00000066040 15026203745 012521 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 MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit" #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 MFwrSlTitle2 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[1,2] Alternate destination path" #define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" #define MCHelpSwAI L"\n ai Ignore file attributes" #define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]" #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 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 MCHelpSwEP4 L"\n ep4 Exclude the path prefix from names" #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,n,p,q] Display or 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[-] Control notification sounds" #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 MCHelpSwMC L"\n mc Set advanced compression parameters" #define MCHelpSwMD L"\n md[x][kmg] Dictionary size in KB, MB or GB" #define MCHelpSwME L"\n me[par] Set encryption parameters" #define MCHelpSwMLP L"\n mlp Use large memory pages" #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, skip]" #define MCHelpSwOM L"\n om[-|1][=lst] Propagate Mark of the Web" #define MCHelpSwONI L"\n oni Allow potentially incompatible names" #define MCHelpSwOP L"\n op Set the output path for extracted files" #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 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[u] Process files with size less than specified" #define MCHelpSwSM L"\n sm[u] 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[mcao] Process files newer than time" #define MCHelpSwTO L"\n to[mcao] Process files older than time" #define MCHelpSwTA L"\n ta[mcao] Process files modified after YYYYMMDDHHMMSS date" #define MCHelpSwTB L"\n tb[mcao] Process files modified before YYYYMMDDHHMMSS date" #define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)" #define MCHelpSwU L"\n u Update files" #define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes" #define MCHelpSwVUnr L"\n v List all volumes" #define MCHelpSwVn L"\n v[u] Create volumes with size in [bBkKmMgGtT] units" #define MCHelpSwVD L"\n vd Erase disk contents before creating volume" #define MCHelpSwVER L"\n ver[n] File version control" #define MCHelpSwVP L"\n vp Pause before each volume" #define MCHelpSwW L"\n w Assign work directory" #define MCHelpSwX L"\n x Exclude specified file" #define MCHelpSwXa L"\n x@ Read file names to exclude from stdin" #define MCHelpSwXal L"\n x@ Exclude files listed in specified list file" #define MCHelpSwY L"\n y Assume Yes on all queries" #define MCHelpSwZ L"\n z[file] Read archive comment from file" #define MBadArc L"\nERROR: Bad archive %s\n" #define MAskPsw L"Enter password (will not be echoed)" #define MAskPswFor L"\nEnter password (will not be echoed) for %s: " #define MReAskPsw L"\nReenter password: " #define MNotMatchPsw L"\nERROR: Passwords do not match\n" #define MErrWrite L"Write error in the file %s" #define MErrRead L"Read error in the file %s" #define MErrSeek L"Seek error in the file %s" #define MErrFClose L"Cannot close the file %s" #define MErrOutMem L"Not enough memory" #define MErrBrokenArc L"Corrupt archive - use 'Repair' command" #define MProgAborted L"Program aborted" #define MErrRename L"\nCannot rename %s to %s" #define MAbsNextVol L"\nCannot find volume %s" #define MBreak L"\nUser break\n" #define MAskCreatVol L"\nCreate next volume?" #define MAskNextDisk L"\nDisk full. Insert next" #define MCreatVol L"\n\nCreating %sarchive %s\n" #define MAskNextVol L"\nInsert disk with %s" #define MTestVol L"\n\nTesting archive %s\n" #define MExtrVol L"\n\nExtracting from %s\n" #define MConverting L"\nConverting %s" #define MCvtToSFX L"\nConvert archives to SFX" #define MCvtFromSFX L"\nRemoving SFX module" #define MNotSFX L"\n%s is not SFX archive" #define MNotRAR L"\n%s is not RAR archive" #define MNotFirstVol L"\n%s is not the first volume" #define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format" #define MCannotCreate L"\nCannot create %s" #define MCannotOpen L"\nCannot open %s" #define MUnknownMeth L"\nUnknown method in %s" #define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version." #define MOk L" OK" #define MDone L"\nDone" #define MLockingArc L"\nLocking archive" #define MNotMdfOld L"\n\nERROR: Cannot modify old format archive" #define MNotMdfLock L"\n\nERROR: Locked archive" #define MNotMdfVol L"\n\nERROR: Cannot modify volume" #define MPackAskReg L"\nEvaluation copy. Please register.\n" #define MCreateArchive L"\nCreating %sarchive %s\n" #define MUpdateArchive L"\nUpdating %sarchive %s\n" #define MAddSolid L"solid " #define MAddFile L"\nAdding %-58s " #define MUpdFile L"\nUpdating %-58s " #define MAddPoints L"\n... %-58s " #define MMoveDelFiles L"\n\nDeleting files %s..." #define MMoveDelDirs L"and directories" #define MMoveDelFile L"\nDeleting %-30s" #define MMoveDeleted L" deleted" #define MMoveNotDeleted L" NOT DELETED" #define MClearAttrib L"\n\nClearing attributes..." #define MMoveDelDir L"\nDeleting directory %-30s" #define MWarErrFOpen L"\nWARNING: Cannot open %d %s" #define MErrOpenFiles L"files" #define MErrOpenFile L"file" #define MAddNoFiles L"\nWARNING: No files" #define MMdfEncrSol L"\n%s: encrypted" #define MAddAnalyze L"\nAnalyzing archived files: " #define MRepacking L"\nRepacking archived files: " #define MCRCFailed L"\n%-20s - checksum error" #define MExtrTest L"\nTesting archive %s\n" #define MExtracting L"\nExtracting from %s\n" #define MUseCurPsw L"\n%s - use current password?" #define MCreatDir L"\nCreating %-56s" #define MExtrSkipFile L"\nSkipping %-56s" #define MExtrTestFile L"\nTesting %-56s" #define MExtrFile L"\nExtracting %-56s" #define MExtrPoints L"\n... %-56s" #define MExtrErrMkDir L"\nCannot create directory %s" #define MExtrPrinting L"\n------ Printing %s\n\n" #define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password." #define MExtrNoFiles L"\nNo files to extract" #define MExtrAllOk L"\nAll OK" #define MExtrTotalErr L"\nTotal errors: %ld" #define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n" #define MAskOverwrite L"\nOverwrite %s?" #define MAskNewName L"\nEnter new name: " #define MHeaderBroken L"\nCorrupt header is found" #define MMainHeaderBroken L"\nMain archive header is corrupt" #define MLogFileHead L"\n%s - the file header is corrupt" #define MLogProtectHead L"The data recovery header is corrupt" #define MArcComment L"\nArchive comment" #define MReadStdinCmt L"\nReading comment from stdin\n" #define MReadCommFrom L"\nReading comment from %s" #define MDelComment L"\nDeleting a comment from %s" #define MAddComment L"\nAdding a comment to %s" #define MLogCommBrk L"\nThe archive comment is corrupt" #define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:" #define MWriteCommTo L"\nWrite comment to %s" #define MCommNotPres L"\nComment is not present" #define MDelFrom L"\nDeleting from %s" #define MDeleting L"\nDeleting %s" #define MEraseArc L"\nErasing empty archive %s" #define MNoDelFiles L"\nNo files to delete" #define MLogTitle L"-------- %2d %s %d, archive %s" #define MPathTooLong L"\nERROR: Path too long\n" #define MListArchive L"Archive" #define MListDetails L"Details" #define MListSolid L"solid" #define MListSFX L"SFX" #define MListVolume L"volume" #define MListRR L"recovery record" #define MListLock L"lock" #define MListEnc L"encrypted" #define MListEncHead L"encrypted headers" #define MListTitleL L" Attributes Size Date Time Name" #define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name" #define MListName L"Name" #define MListType L"Type" #define MListFile L"File" #define MListDir L"Directory" #define MListUSymlink L"Unix symbolic link" #define MListWSymlink L"Windows symbolic link" #define MListJunction L"NTFS junction point" #define MListHardlink L"Hard link" #define MListCopy L"File reference" #define MListStream L"NTFS alternate data stream" #define MListTarget L"Target" #define MListSize L"Size" #define MListPacked L"Packed size" #define MListRatio L"Ratio" #define MListMtime L"mtime" #define MListCtime L"ctime" #define MListAtime L"atime" #define MListModified L"Modified" #define MListCreated L"Created" #define MListAccessed L"Accessed" #define MListAttr L"Attributes" #define MListFlags L"Flags" #define MListCompInfo L"Compression" #define MListHostOS L"Host OS" #define MListFileVer L"File version" #define MListService L"Service" #define MListNTACLHead L"\n NTFS security data" #define MListStrmHead L"\n NTFS stream: %s" #define MListUnkHead L"\n Unknown subheader type: 0x%04x" #define MYes L"Yes" #define MNo L"No" #define MListNoFiles L" 0 files\n" #define MRprReconstr L"\nReconstructing %s" #define MRprBuild L"\nBuilding %s" #define MRprOldFormat L"\nCannot repair archive with old format" #define MRprFind L"\nFound %s" #define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid?" #define MRprNoFiles L"\nNo files found" #define MLogUnexpEOF L"\nUnexpected end of archive" #define MRepAskReconst L"\nReconstruct archive structure?" #define MRRSearch L"\nSearching for recovery record" #define MAnalyzeFileData L"\nAnalyzing file data" #define MRecRNotFound L"\nData recovery record not found" #define MRecRFound L"\nData recovery record found" #define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged" #define MRecCorrected L" - data recovered" #define MRecFailed L" - cannot recover data" #define MAddRecRec L"\nAdding the data recovery record" #define MEraseForVolume L"\n\nErasing contents of drive %c:\n" #define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n" #define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n" #define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n" #define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n" #define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n" #define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s" #define MSymLinkExists L"\nWARNING: Symbolic link %s already exists" #define MAskRetryCreate L"\nCannot create %s. Retry?" #define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s" #define MFileRO L"\n%s is read-only" #define MACLGetError L"\nWARNING: Cannot get %s security data\n" #define MACLSetError L"\nWARNING: Cannot set %s security data\n" #define MACLBroken L"\nERROR: %s security data are corrupt\n" #define MACLUnknown L"\nWARNING: Unknown format of %s security data\n" #define MStreamBroken L"\nERROR: %s stream data are corrupt\n" #define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n" #define MInvalidName L"\nERROR: Invalid file name %s" #define MProcessArc L"\nProcessing archive %s" #define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name" #define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" #define MUnknownOption L"\nERROR: Unknown option: %s" #define MSwSyntaxError L"\nERROR: '-' is expected in the beginning of: %s" #define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored" #define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored" #define MSubHeadDataCRC L"\nERROR: Corrupt %s data block" #define MScanError L"\nCannot read contents of %s" #define MNotVolume L"\n%s is not volume" #define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets" #define MRecVolMissing L"\n%d volumes missing" #define MRecVolFound L"\n%d recovery volumes found" #define MRecVolAllExist L"\nNothing to reconstruct" #define MRecVolCannotFix L"\nReconstruction impossible" #define MReconstructing L"\nReconstructing..." #define MCreating L"\nCreating %s" #define MRenaming L"\nRenaming %s to %s" #define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB" #define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files" #define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s" #define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives" #define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive" #define MCannotEmail L"\nCannot email the file %s" #define MCopyrightS L"\nRAR SFX archive" #define MSHelpCmd L"\n\n" #define MSHelpCmdE L"\n -x Extract from archive (default)" #define MSHelpCmdT L"\n -t Test archive files" #define MSHelpCmdV L"\n -v Verbosely list contents of archive" #define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d" #define MVolumeNumber L"volume %d" #define MCannotDelete L"\nCannot delete %s" #define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin" #define MCalcCRC L"\nCalculating the checksum" #define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB." #define MCalcCRCAllVol L"\nCalculating checksums of all volumes." #define MNotEnoughDisk L"\nERROR: Not enough disk space for %s." #define MNewerRAR L"\nYou may need a newer version of RAR." #define MUnkEncMethod L"\nUnknown encryption method in %s" #define MWrongPassword L"\nThe specified password is incorrect." #define MWrongFilePassword L"\nIncorrect password for %s" #define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x" #define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated" #define MRRDamaged L"\nRecovery record is corrupt." #define MTestingRR L"\nTesting the recovery record" #define MFailed L"Failed" #define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format." #define MSearchDupFiles L"\nSearching for identical files" #define MNumFound L"%d found." #define MUnknownExtra L"\nUnknown extra field in %s." #define MCorruptExtra L"\nCorrupt %s extra field in %s." #define MCopyError L"\nCannot copy %s to %s." #define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries." #define MCopyingData L"\nCopying data" #define MErrCreateLnkS L"\nCannot create symbolic link %s" #define MErrCreateLnkH L"\nCannot create hard link %s" #define MErrLnkTarget L"\nYou need to unpack the link target first" #define MNeedAdmin L"\nYou may need to run RAR as administrator" #define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB." #define MUseSmalllerDict L"\nPlease use a smaller compression dictionary." #define MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u GB compression dictionary." #define MSuggest64bit L"\n64-bit RAR version is necessary." #define MOpenErrAtime L"\nYou may need to remove -tsp switch or run RAR as administrator to open this file." #define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort." #define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n" #define MErrReadCount L"\n%u files are archived incompletely because of read errors." #define MDirNameExists L"\nDirectory with such name already exists" #define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin" #define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated." #define MAdjustValue L"\nAdjusting %s value to %s." #define MOpFailed L"\nOperation failed" #define MSkipEncArc L"\nSkipping the encrypted archive %s" #define MOrigName L"Original name" #define MOriginalTime L"Original time" #define MFileRenamed L"\n%s is renamed to %s" #define MDictNotAllowed L"\n%u GB dictionary exceeds %u GB limit and needs more than %u GB memory to unpack." #define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway." #define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data." #define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary." #define MSkipUnsafeLink L"\nSkipping the potentially unsafe %s -> %s link. For archives from a trustworthy source use -ola to extract it anyway." #define MTruncService L"\nTruncated at the service block: %s" #define MHeaderQO L"quick open information" #define MHeaderRR L"recovery record" #define MLockInMemoryNeeded L"-mlp switch requires ""Lock pages in memory"" privilege. Do you wish to assign it to the current user account?" #define MPrivilegeAssigned L"User privilege has been successfully assigned and will be activated after Windows restart. Restart now?" unrar/log.hpp000666 000000 000000 00000000433 15026203745 011655 0ustar00000000 000000 #ifndef _RAR_LOG_ #define _RAR_LOG_ void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet); void CloseLogOptions(); #ifdef SILENT inline void Log(const wchar *ArcName,const wchar *fmt,...) {} #else void Log(const wchar *ArcName,const wchar *fmt,...); #endif #endif unrar/match.hpp000666 000000 000000 00000003162 15026203745 012172 0ustar00000000 000000 #ifndef _RAR_MATCH_ #define _RAR_MATCH_ enum { MATCH_NAMES, // Paths are ignored. // Compares names only using wildcards. MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard // must be present in the beginning of file path. // For example, "c:\path1\*" or "c:\path1" will match // "c:\path1\path2\file". // Names are not compared. MATCH_EXACT, // Paths must match exactly. // Names must match exactly. MATCH_ALLWILD, // Paths and names are compared using wildcards. // Unlike MATCH_SUBPATH, paths do not match subdirs // unless a wildcard tells so. MATCH_EXACTPATH, // Paths must match exactly. // Names are compared using wildcards. MATCH_SUBPATH, // Names must be the same, but path in mask is allowed // to be only a part of name path. In other words, // we match all files matching the file mask // in current folder and subfolders. MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains // wildcards and as MATCH_EXACTPATH otherwise. }; #define MATCH_MODEMASK 0x0000ffff #define MATCH_FORCECASESENSITIVE 0x80000000 bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode); inline bool CmpName(const std::wstring &Wildcard,const std::wstring &Name,uint CmpMode) { return CmpName(Wildcard.c_str(),Name.c_str(),CmpMode); } #endif unrar/model.hpp000666 000000 000000 00000005724 15026203745 012204 0ustar00000000 000000 #ifndef _RAR_PPMMODEL_ #define _RAR_PPMMODEL_ #include "coder.hpp" #include "suballoc.hpp" #ifdef ALLOW_MISALIGNED #pragma pack(1) #endif struct RARPPM_DEF { static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; }; struct RARPPM_SEE2_CONTEXT : RARPPM_DEF { // SEE-contexts for PPM-contexts with masked symbols ushort Summ; byte Shift, Count; void init(int InitVal) { Summ=InitVal << (Shift=PERIOD_BITS-4); Count=4; } uint getMean() { short RetVal=GET_SHORT16(Summ) >> Shift; Summ -= RetVal; return RetVal+(RetVal == 0); } void update() { if (Shift < PERIOD_BITS && --Count == 0) { Summ += Summ; Count=3 << Shift++; } } }; class ModelPPM; struct RARPPM_CONTEXT; struct RARPPM_STATE { byte Symbol; byte Freq; RARPPM_CONTEXT* Successor; }; struct RARPPM_CONTEXT : RARPPM_DEF { ushort NumStats; struct FreqData { ushort SummFreq; RARPPM_STATE RARPPM_PACK_ATTR * Stats; }; union { FreqData U; RARPPM_STATE OneState; }; RARPPM_CONTEXT* Suffix; inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder: inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor inline bool decodeSymbol1(ModelPPM *Model); // other orders: inline bool decodeSymbol2(ModelPPM *Model); // BCD context inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor void rescale(ModelPPM *Model); inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState); inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); }; #ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else #pragma pack() #endif #endif class ModelPPM : RARPPM_DEF { private: friend struct RARPPM_CONTEXT; RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext; RARPPM_STATE* FoundState; // found next state transition int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL; byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; byte EscCount, PrevSuccess, HiBitsFlag; ushort BinSumm[128][64]; // binary SEE-contexts RangeCoder Coder; SubAllocator SubAlloc; void RestartModelRare(); void StartModelRare(int MaxOrder); inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1); inline void UpdateModel(); inline void ClearMask(); public: ModelPPM(); void CleanUp(); // reset PPM variables after data error bool DecodeInit(Unpack *UnpackRead,int &EscChar); int DecodeChar(); }; #endif unrar/motw.hpp000666 000000 000000 00000001362 15026203745 012064 0ustar00000000 000000 #ifndef _RAR_MOTW_ #define _RAR_MOTW_ class MarkOfTheWeb { private: const size_t MOTW_STREAM_MAX_SIZE=1024; const wchar* MOTW_STREAM_NAME=L":Zone.Identifier"; // Must start from ':'. int ParseZoneIdStream(std::string &Stream); std::string ZoneIdStream; // Store archive ":Zone.Identifier" NTFS stream data. int ZoneIdValue; // -1 if missing. bool AllFields; // Copy all MOTW fields or ZoneId only. public: MarkOfTheWeb(); void Clear(); void ReadZoneIdStream(const std::wstring &FileName,bool AllFields); void CreateZoneIdStream(const std::wstring &Name,StringList &MotwList); bool IsNameConflicting(const std::wstring &StreamName); bool IsFileStreamMoreSecure(std::string &FileStream); }; #endif unrar/options.hpp000666 000000 000000 00000013167 15026203745 012577 0ustar00000000 000000 #ifndef _RAR_OPTIONS_ #define _RAR_OPTIONS_ #define DEFAULT_RECOVERY 3 #define DEFAULT_RECVOLUMES -10 #define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size. enum PATH_EXCL_MODE { EXCL_UNCHANGED=0, // Process paths as is (default). EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely) EXCL_BASEPATH, // -ep1 (exclude the base part of path) EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter) EXCL_ABSPATH // -ep3 (the full path with the disk letter) }; enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4, SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16}; enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST}; enum EXTTIME_MODE { EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX }; enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE}; enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL}; enum RECURSE_MODE { RECURSE_NONE=0, // no recurse switches RECURSE_DISABLE, // switch -r- RECURSE_ALWAYS, // switch -r RECURSE_WILDCARDS // switch -r0 }; enum OVERWRITE_MODE { OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving. OVERWRITE_ALL, OVERWRITE_NONE, OVERWRITE_AUTORENAME, OVERWRITE_FORCE_ASK }; enum ARC_METADATA { ARCMETA_NONE=0, ARCMETA_SAVE, // -ams ARCMETA_RESTORE // -amr }; enum QOPEN_MODE { QOPEN_NONE=0, QOPEN_AUTO, QOPEN_ALWAYS }; enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 }; #define MAX_FILTER_TYPES 16 enum FilterState { FILTER_DEFAULT=0, // No -mc switch. FILTER_AUTO, // -mc switch is present. FILTER_FORCE, // -mc+ switch is present. FILTER_DISABLE // -mc- switch is present. }; enum SAVECOPY_MODE { SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT, SAVECOPY_DUPLISTEXIT }; enum APPENDARCNAME_MODE { APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR, APPENDARCNAME_OWNDIR }; enum POWER_MODE { POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP, POWERMODE_RESTART }; // Need "forced off" state to turn off sound in GUI command line. enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF}; struct FilterMode { FilterState State; int Param1; int Param2; }; #define MAX_GENERATE_MASK 128 // Here we store simple data types, which we can clear and move all together // quickly. Rest of data types goes to CommandData. class RAROptions { public: RAROptions(); void Init(); uint ExclFileAttr; uint InclFileAttr; // We handle -ed and -e+d with special flags instead of attribute mask, // so it works with both Windows and Unix archives. bool ExclDir; bool InclDir; bool InclAttrSet; uint64 WinSize; uint64 WinSizeLimit; // Switch -mdx. #ifdef USE_QOPEN QOPEN_MODE QOpenMode; #endif bool ConfigDisabled; // Switch -cfg-. RAR_CHARSET CommentCharset; RAR_CHARSET FilelistCharset; RAR_CHARSET ErrlogCharset; RAR_CHARSET RedirectCharset; bool EncryptHeaders; bool SkipEncrypted; bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. MESSAGE_TYPE MsgStream; SOUND_NOTIFY_MODE Sound; OVERWRITE_MODE Overwrite; int Method; HASH_TYPE HashType; uint Recovery; int RecVolNumber; ARC_METADATA ArcMetadata; bool DisablePercentage; bool DisableCopyright; bool DisableDone; bool DisableNames; bool PrintVersion; int Solid; int SolidCount; bool ClearArc; bool AddArcOnly; bool DisableComment; bool FreshFiles; bool UpdateFiles; PATH_EXCL_MODE ExclPath; RECURSE_MODE Recurse; int64 VolSize; uint CurVolNum; bool AllYes; bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. bool DisableSortSolid; int ArcTime; int ConvertNames; bool ProcessOwners; bool SaveSymLinks; bool SaveHardLinks; bool AbsoluteLinks; bool SkipSymLinks; int Priority; int SleepTime; bool UseLargePages; // Quit after processing some system integration related switch, // like enabling the large memory pages privilege. // menu for non-admin user and quit. bool SetupComplete; bool KeepBroken; bool OpenShared; bool DeleteFiles; #ifdef _WIN_ALL bool AllowIncompatNames; // Allow names with trailing dots and spaces. #endif #ifndef SFX_MODULE bool GenerateArcName; wchar GenerateMask[MAX_GENERATE_MASK]; wchar DefGenerateMask[MAX_GENERATE_MASK]; #endif bool SyncFiles; bool ProcessEA; bool SaveStreams; #ifdef PROPAGATE_MOTW bool MotwAllFields; #endif bool SetCompressedAttr; bool IgnoreGeneralAttr; RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore; bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR; RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter; bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR; int64 FileSizeLess; int64 FileSizeMore; bool Lock; bool Test; bool VolumePause; FilterMode FilterModes[MAX_FILTER_TYPES]; uint VersionControl; APPENDARCNAME_MODE AppendArcNameToPath; POWER_MODE Shutdown; EXTTIME_MODE xmtime; // Extended time modes (time precision to store). EXTTIME_MODE xctime; EXTTIME_MODE xatime; bool PreserveAtime; uint Threads; // We use it to init hash even if RAR_SMP is not defined. #ifdef RARDLL int DllOpMode; int DllError; LPARAM UserData; UNRARCALLBACK Callback; CHANGEVOLPROC ChangeVolProc; PROCESSDATAPROC ProcessDataProc; #endif }; #endif unrar/os.hpp000666 000000 000000 00000013456 15026203745 011526 0ustar00000000 000000 #ifndef _RAR_OS_ #define _RAR_OS_ #define FALSE 0 #define TRUE 1 #if defined(RARDLL) && !defined(SILENT) #define SILENT #endif #include #include #include #include #include // For automatic pointers. #include #ifdef _WIN_ALL #define LITTLE_ENDIAN // We got a report that just "#define STRICT" is incompatible with // "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order // in which these statements are reached this may cause a compiler warning // and build break for other projects incorporating this source. // So we changed it to "#define STRICT 1". #ifndef STRICT #define STRICT 1 #endif // 'ifndef' check here is needed for unrar.dll header to avoid macro // re-definition warnings in third party projects. #ifndef UNICODE #define UNICODE #define _UNICODE // Set _T() macro to convert from narrow to wide strings. #endif #define WINVER _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP #if !defined(ZIPSFX) #define RAR_SMP #endif #define WIN32_LEAN_AND_MEAN #include #include #include #pragma comment(lib, "Shlwapi.lib") #include #pragma comment(lib, "PowrProf.lib") #include #include #include #include #include #include #include #include // For WMI requests. #include #include #pragma comment(lib, "wbemuuid.lib") #include #include #include #include #include // Use SSE only for x86/x64, not ARM Windows. #if defined(_M_IX86) || defined(_M_X64) #define USE_SSE #define SSE_ALIGNMENT 16 #endif #include #include #include #include #include #include #include #include #include #include #define SAVE_LINKS #define ENABLE_ACCESS #define DefConfigName L"rar.ini" #define DefLogName L"rar.log" #define SPATHDIVIDER L"\\" #define CPATHDIVIDER L'\\' #define MASKALL L"*" #define READBINARY "rb" #define READTEXT "rt" #define UPDATEBINARY "r+b" #define CREATEBINARY "w+b" #define WRITEBINARY "wb" #define APPENDTEXT "at" #define _stdfunction __cdecl #define _forceinline __forceinline #endif // _WIN_ALL #ifdef _UNIX #include #include #include #include #if defined(__QNXNTO__) #include #endif #ifdef _APPLE #include #endif #ifndef SFX_MODULE #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ #if defined(__i386__) || defined(__x86_64__) #include #define USE_SSE #define SSE_ALIGNMENT 16 #endif #endif #if defined(__aarch64__) && (defined(__ARM_FEATURE_CRYPTO) || defined(__ARM_FEATURE_CRC32)) #include #ifndef _APPLE #include #include #endif #ifdef __ARM_FEATURE_CRYPTO #define USE_NEON_AES #endif #ifdef __ARM_FEATURE_CRC32 #define USE_NEON_CRC32 #endif #endif #ifdef S_IFLNK #define SAVE_LINKS #endif #if defined(__linux) || defined(__FreeBSD__) #include #define USE_LUTIMES #endif #define ENABLE_ACCESS #define DefConfigName L".rarrc" #define DefLogName L".rarlog" #define SPATHDIVIDER L"/" #define CPATHDIVIDER L'/' #define MASKALL L"*" #define READBINARY "r" #define READTEXT "r" #define UPDATEBINARY "r+" #define CREATEBINARY "w+" #define WRITEBINARY "w" #define APPENDTEXT "a" #define _stdfunction #define _forceinline inline #ifdef _APPLE #if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN) #define BIG_ENDIAN #undef LITTLE_ENDIAN #endif #if defined(__i386__) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN #undef BIG_ENDIAN #endif #endif #if defined(__sparc) || defined(sparc) || defined(__hpux) #ifndef BIG_ENDIAN #define BIG_ENDIAN #endif #endif #ifdef __VMS # define LITTLE_ENDIAN #endif // Unlike Apple x64, utimensat shall be available in all Apple M1 systems. #if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__) #define UNIX_TIME_NS // Nanosecond time precision in Unix. #endif #endif // _UNIX typedef const wchar* MSGID; #ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required. #define SSE_ALIGNMENT 1 #endif // Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN. #if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN #endif #if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN) #define BIG_ENDIAN #endif #if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) #if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64) #define LITTLE_ENDIAN #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) #define LITTLE_ENDIAN #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__) #define BIG_ENDIAN #else #error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them." #endif #endif #if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN #undef LITTLE_ENDIAN #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN #undef BIG_ENDIAN #else #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef one of them." #endif #endif #if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) // Allow unaligned integer access, increases speed in some operations. #define ALLOW_MISALIGNED #endif #endif // _RAR_OS_ unrar/pathfn.hpp000666 000000 000000 00000010024 15026203745 012351 0ustar00000000 000000 #ifndef _RAR_PATHFN_ #define _RAR_PATHFN_ wchar* PointToName(const wchar *Path); std::wstring PointToName(const std::wstring &Path); size_t GetNamePos(const std::wstring &Path); wchar* PointToLastChar(const wchar *Path); wchar GetLastChar(const std::wstring &Path); size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath); void SetName(std::wstring &FullName,const std::wstring &Name); void SetExt(std::wstring &Name,std::wstring NewExt); void RemoveExt(std::wstring &Name); void SetSFXExt(std::wstring &SFXName); wchar *GetExt(const wchar *Name); std::wstring GetExt(const std::wstring &Name); std::wstring::size_type GetExtPos(const std::wstring &Name); bool CmpExt(const std::wstring &Name,const std::wstring &Ext); bool IsWildcard(const std::wstring &Str); bool IsPathDiv(int Ch); bool IsDriveDiv(int Ch); bool IsDriveLetter(const std::wstring &Path); int GetPathDisk(const std::wstring &Path); void AddEndSlash(std::wstring &Path); void MakeName(const std::wstring &Path,const std::wstring &Name,std::wstring &Pathname); void GetPathWithSep(const std::wstring &FullName,std::wstring &Path); void RemoveNameFromPath(std::wstring &Path); #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool GetAppDataPath(std::wstring &Path,bool Create); void GetRarDataPath(std::wstring &Path,bool Create); #endif #ifdef _WIN_ALL bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path); #endif #ifndef SFX_MODULE bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create); void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create); #endif size_t GetVolNumPos(const std::wstring &ArcName); void NextVolumeName(std::wstring &ArcName,bool OldNumbering); bool IsNameUsable(const std::wstring &Name); void MakeNameUsable(std::wstring &Name,bool Extended); void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength); void UnixSlashToDos(const std::string &SrcName,std::string &DestName); void UnixSlashToDos(const std::wstring &SrcName,std::wstring &DestName); void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength); void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength); void DosSlashToUnix(const std::string &SrcName,std::string &DestName); void DosSlashToUnix(const std::wstring &SrcName,std::wstring &DestName); inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength) { #ifdef _WIN_ALL UnixSlashToDos(SrcName,DestName,MaxLength); #else DosSlashToUnix(SrcName,DestName,MaxLength); #endif } inline void SlashToNative(const std::string &SrcName,std::string &DestName) { #ifdef _WIN_ALL UnixSlashToDos(SrcName,DestName); #else DosSlashToUnix(SrcName,DestName); #endif } inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength) { #ifdef _WIN_ALL UnixSlashToDos(SrcName,DestName,MaxLength); #else DosSlashToUnix(SrcName,DestName,MaxLength); #endif } inline void SlashToNative(const std::wstring &SrcName,std::wstring &DestName) { #ifdef _WIN_ALL UnixSlashToDos(SrcName,DestName); #else DosSlashToUnix(SrcName,DestName); #endif } void ConvertNameToFull(const std::wstring &Src,std::wstring &Dest); bool IsFullPath(const std::wstring &Path); bool IsFullRootPath(const std::wstring &Path); void GetPathRoot(const std::wstring &Path,std::wstring &Root); int ParseVersionFileName(std::wstring &Name,bool Truncate); size_t VolNameToFirstName(const std::wstring &VolName,std::wstring &FirstName,bool NewNumbering); #ifndef SFX_MODULE void GenerateArchiveName(std::wstring &ArcName,const std::wstring &GenerateMask,bool Archiving); #endif #ifdef _WIN_ALL bool GetWinLongPath(const std::wstring &Src,std::wstring &Dest); void ConvertToPrecomposed(std::wstring &Name); void MakeNameCompatible(std::wstring &Name); #endif #ifdef _WIN_ALL std::wstring GetModuleFileStr(); std::wstring GetProgramFile(const std::wstring &Name); #endif #if defined(_WIN_ALL) bool SetCurDir(const std::wstring &Dir); #endif #ifdef _WIN_ALL bool GetCurDir(std::wstring &Dir); #endif #endif unrar/qopen.hpp000666 000000 000000 00000003001 15026203745 012210 0ustar00000000 000000 #ifndef _RAR_QOPEN_ #define _RAR_QOPEN_ struct QuickOpenItem { byte *Header; size_t HeaderSize; uint64 ArcPos; QuickOpenItem *Next; }; class Archive; class RawRead; class QuickOpen { private: void Close(); uint ReadBuffer(); bool ReadRaw(RawRead &Raw); bool ReadNext(); Archive *Arc; bool WriteMode; QuickOpenItem *ListStart; QuickOpenItem *ListEnd; byte *Buf; // Read quick open data here. static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE. size_t CurBufSize; // Current size of buffered data in write mode. #ifndef RAR_NOCRYPT // For shell extension. CryptData Crypt; #endif bool Loaded; uint64 QOHeaderPos; // Main QO header position. uint64 RawDataStart; // Start of QO data, just after the main header. uint64 RawDataSize; // Size of entire QO data. uint64 RawDataPos; // Current read position in QO data. size_t ReadBufSize; // Size of Buf data currently read from QO. size_t ReadBufPos; // Current read position in Buf data. std::vector LastReadHeader; uint64 LastReadHeaderPos; uint64 SeekPos; bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer. public: QuickOpen(); ~QuickOpen(); void Init(Archive *Arc,bool WriteMode); void Load(uint64 BlockPos); void Unload() { Loaded=false; } bool Read(void *Data,size_t Size,size_t &Result); bool Seek(int64 Offset,int Method); bool Tell(int64 *Pos); }; #endif unrar/rar.hpp000666 000000 000000 00000002756 15026203745 011672 0ustar00000000 000000 #ifndef _RAR_RARCOMMON_ #define _RAR_RARCOMMON_ #include "raros.hpp" #include "rartypes.hpp" #include "os.hpp" #ifdef RARDLL #include "dll.hpp" #endif #include "version.hpp" #include "rardefs.hpp" #include "rarlang.hpp" #include "rawint.hpp" #include "unicode.hpp" #include "errhnd.hpp" #include "secpassword.hpp" #include "strlist.hpp" #include "timefn.hpp" #include "sha1.hpp" #include "sha256.hpp" #include "blake2s.hpp" #include "hash.hpp" #include "options.hpp" #include "rijndael.hpp" #include "crypt.hpp" #include "headers5.hpp" #include "headers.hpp" #include "pathfn.hpp" #include "strfn.hpp" #ifdef _WIN_ALL #include "isnt.hpp" #endif #ifdef PROPAGATE_MOTW #include "motw.hpp" #endif #include "file.hpp" #include "crc.hpp" #include "filefn.hpp" #include "filestr.hpp" #include "find.hpp" #include "scantree.hpp" #include "getbits.hpp" #include "rdwrfn.hpp" #ifdef USE_QOPEN #include "qopen.hpp" #endif #include "archive.hpp" #include "match.hpp" #include "cmddata.hpp" #include "ui.hpp" #include "filcreat.hpp" #include "consio.hpp" #include "system.hpp" #include "log.hpp" #include "rawread.hpp" #include "encname.hpp" #include "resource.hpp" #include "compress.hpp" #include "rarvm.hpp" #include "model.hpp" #include "threadpool.hpp" #include "largepage.hpp" #include "unpack.hpp" #include "extinfo.hpp" #include "extract.hpp" #include "list.hpp" #include "rs.hpp" #include "rs16.hpp" #include "recvol.hpp" #include "volume.hpp" #include "smallfn.hpp" #include "global.hpp" #endif unrar/rardefs.hpp000666 000000 000000 00000003201 15026203745 012516 0ustar00000000 000000 #ifndef _RAR_DEFS_ #define _RAR_DEFS_ #define Min(x,y) (((x)<(y)) ? (x):(y)) #define Max(x,y) (((x)>(y)) ? (x):(y)) // Universal replacement of abs function. #define Abs(x) (((x)<0) ? -(x):(x)) #define ASIZE(x) (sizeof(x)/sizeof(x[0])) // MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of // CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword. // We allow a larger MAXPASSWORD to unpack archives with lengthy passwords // in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR // to 128 for compatibility and because it is enough for AES-256. #define MAXPASSWORD 512 #define MAXPASSWORD_RAR 128 // Set some arbitrary sensible limit to maximum path length to prevent // the excessive memory allocation for dynamically allocated strings. #define MAXPATHSIZE 0x10000 #define MAXSFXSIZE 0x400000 #define MAXCMTSIZE 0x40000 #ifdef _WIN_32 #define DefSFXName L"default32.sfx" #else #define DefSFXName L"default.sfx" #endif #define DefSortListName L"rarfiles.lst" // Maximum dictionary allowed by compression. Can be less than // maximum dictionary supported by decompression. #define PACK_MAX_DICT 0x1000000000ULL // 64 GB. // Maximum dictionary allowed by decompression. #define UNPACK_MAX_DICT 0x1000000000ULL // 64 GB. #ifndef SFX_MODULE #define USE_QOPEN #endif // Produce the value, which is equal or larger than 'v' and aligned to 'a'. #define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) ) #if defined(_WIN_ALL) && !defined(SFX_MODULE) #define PROPAGATE_MOTW // Propagate the archive Mark of the Web. #endif #endif unrar/rarlang.hpp000666 000000 000000 00000000205 15026203745 012517 0ustar00000000 000000 #ifndef _RAR_LANG_ #define _RAR_LANG_ #ifdef USE_RC #include "rarres.hpp" #else #include "loclang.hpp" #endif #endif unrar/raros.hpp000666 000000 000000 00000000733 15026203745 012225 0ustar00000000 000000 #ifndef _RAR_RAROS_ #define _RAR_RAROS_ #if defined(__WIN32__) || defined(_WIN32) #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop. #ifdef _M_X64 #define _WIN_64 #else #define _WIN_32 #endif #endif #if defined(ANDROID) || defined(__ANDROID__) #define _UNIX #define _ANDROID #endif #ifdef __APPLE__ #define _UNIX #define _APPLE #endif #if !defined(_WIN_ALL) && !defined(_UNIX) #define _UNIX #endif #endif unrar/rartypes.hpp000666 000000 000000 00000002425 15026203745 012750 0ustar00000000 000000 #ifndef _RAR_TYPES_ #define _RAR_TYPES_ #include typedef uint8_t byte; // Unsigned 8 bits. typedef uint16_t ushort; // Preferably 16 bits, but can be more. typedef unsigned int uint; // Preferably 32 bits, likely can be more. typedef uint32_t uint32; // 32 bits exactly. typedef int32_t int32; // Signed 32 bits exactly. typedef uint64_t uint64; // 64 bits exactly. typedef int64_t int64; // Signed 64 bits exactly. typedef wchar_t wchar; // Unicode character // Get lowest 16 bits. #define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) // Make 64 bit integer from two 32 bit. #define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low)) // Maximum int64 value. #define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff)) // Special int64 value, large enough to never be found in real life // and small enough to fit to both signed and unsigned 64-bit ints. // We use it in situations, when we need to indicate that parameter // is not defined and probably should be calculated inside of function. // Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it // compatible with 32 bit int64 if 64 bit type is not supported. #define INT64NDF INT32TO64(0x7fffffff,0x7fffffff) #endif unrar/rarvm.hpp000666 000000 000000 00000001731 15026203745 012225 0ustar00000000 000000 #ifndef _RAR_VM_ #define _RAR_VM_ #define VM_MEMSIZE 0x40000 #define VM_MEMMASK (VM_MEMSIZE-1) enum VM_StandardFilters { VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO, VMSF_DELTA }; struct VM_PreparedProgram { VM_PreparedProgram() { FilteredDataSize=0; Type=VMSF_NONE; } VM_StandardFilters Type; uint InitR[7]; byte *FilteredData; uint FilteredDataSize; }; class RarVM { private: bool ExecuteStandardFilter(VM_StandardFilters FilterType); uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount); void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount); byte *Mem; uint R[8]; public: RarVM(); ~RarVM(); void Init(); void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg); void Execute(VM_PreparedProgram *Prg); void SetMemory(size_t Pos,byte *Data,size_t DataSize); static uint ReadData(BitInput &Inp); }; #endif unrar/rawint.hpp000666 000000 000000 00000007513 15026203745 012406 0ustar00000000 000000 #ifndef _RAR_RAWINT_ #define _RAR_RAWINT_ #define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) #define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) #define rotl32(x,n) rotls(x,n,32) #define rotr32(x,n) rotrs(x,n,32) inline uint RawGet2(const void *Data) { byte *D=(byte *)Data; return D[0]+(D[1]<<8); } inline uint32 RawGet4(const void *Data) { #if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) byte *D=(byte *)Data; return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24); #else return *(uint32 *)Data; #endif } inline uint64 RawGet8(const void *Data) { #if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) byte *D=(byte *)Data; return INT32TO64(RawGet4(D+4),RawGet4(D)); #else return *(uint64 *)Data; #endif } inline void RawPut2(uint Field,void *Data) { byte *D=(byte *)Data; D[0]=(byte)(Field); D[1]=(byte)(Field>>8); } inline void RawPut4(uint Field,void *Data) { #if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) byte *D=(byte *)Data; D[0]=(byte)(Field); D[1]=(byte)(Field>>8); D[2]=(byte)(Field>>16); D[3]=(byte)(Field>>24); #else *(uint32 *)Data=(uint32)Field; #endif } inline void RawPut8(uint64 Field,void *Data) { #if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) byte *D=(byte *)Data; D[0]=(byte)(Field); D[1]=(byte)(Field>>8); D[2]=(byte)(Field>>16); D[3]=(byte)(Field>>24); D[4]=(byte)(Field>>32); D[5]=(byte)(Field>>40); D[6]=(byte)(Field>>48); D[7]=(byte)(Field>>56); #else *(uint64 *)Data=Field; #endif } #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) #define USE_MEM_BYTESWAP #endif // Load 4 big endian bytes from memory and return uint32. inline uint32 RawGetBE4(const byte *m) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) return _byteswap_ulong(*(uint32 *)m); #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) return __builtin_bswap32(*(uint32 *)m); #else return uint32(m[0])<<24 | uint32(m[1])<<16 | uint32(m[2])<<8 | m[3]; #endif } // Load 8 big endian bytes from memory and return uint64. inline uint64 RawGetBE8(const byte *m) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) return _byteswap_uint64(*(uint64 *)m); #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) return __builtin_bswap64(*(uint64 *)m); #else return uint64(m[0])<<56 | uint64(m[1])<<48 | uint64(m[2])<<40 | uint64(m[3])<<32 | uint64(m[4])<<24 | uint64(m[5])<<16 | uint64(m[6])<<8 | m[7]; #endif } // Save integer to memory as big endian. inline void RawPutBE4(uint i,byte *mem) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) *(uint32*)mem = _byteswap_ulong((uint32)i); #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) *(uint32*)mem = __builtin_bswap32((uint32)i); #else mem[0]=byte(i>>24); mem[1]=byte(i>>16); mem[2]=byte(i>>8); mem[3]=byte(i); #endif } // Save integer to memory as big endian. inline void RawPutBE8(uint64 i,byte *mem) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) *(uint64*)mem = _byteswap_uint64(i); #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) *(uint64*)mem = __builtin_bswap64(i); #else mem[0]=byte(i>>56); mem[1]=byte(i>>48); mem[2]=byte(i>>40); mem[3]=byte(i>>32); mem[4]=byte(i>>24); mem[5]=byte(i>>16); mem[6]=byte(i>>8); mem[7]=byte(i); #endif } inline uint32 ByteSwap32(uint32 i) { #ifdef _MSC_VER return _byteswap_ulong(i); #elif defined(__clang__) || defined(__GNUC__) return __builtin_bswap32(i); #else return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); #endif } inline bool IsPow2(uint64 n) // Check if 'n' is power of 2. { return (n & (n-1))==0; } inline uint64 GetGreaterOrEqualPow2(uint64 n) { uint64 p=1; while (p Data; File *SrcFile; size_t DataSize; size_t ReadPos; CryptData *Crypt; public: RawRead(); RawRead(File *SrcFile); void Reset(); size_t Read(size_t Size); void Read(byte *SrcData,size_t Size); byte Get1(); ushort Get2(); uint Get4(); uint64 Get8(); uint64 GetV(); uint GetVSize(size_t Pos); size_t GetB(void *Field,size_t Size); void GetW(wchar *Field,size_t Size); uint GetCRC15(bool ProcessedOnly); uint GetCRC50(); byte* GetDataPtr() {return &Data[0];} size_t Size() {return DataSize;} size_t PaddedSize() {return Data.size()-DataSize;} size_t DataLeft() {return DataSize-ReadPos;} size_t GetPos() {return ReadPos;} void SetPos(size_t Pos) {ReadPos=Pos;} void Skip(size_t Size) {ReadPos+=Size;} void Rewind() {SetPos(0);} void SetCrypt(CryptData *Crypt) {RawRead::Crypt=Crypt;} }; uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow); #endif unrar/rdwrfn.hpp000666 000000 000000 00000004554 15026203745 012406 0ustar00000000 000000 #ifndef _RAR_DATAIO_ #define _RAR_DATAIO_ class Archive; class CmdAdd; class Unpack; class ArcFileSearch; class ComprDataIO { private: void ShowUnpRead(int64 ArcPos,int64 ArcSize); void ShowUnpWrite(); bool UnpackFromMemory; size_t UnpackFromMemorySize; byte *UnpackFromMemoryAddr; bool UnpackToMemory; size_t UnpackToMemorySize; byte *UnpackToMemoryAddr; size_t UnpWrSize; byte *UnpWrAddr; int64 UnpPackedSize; int64 UnpPackedLeft; bool ShowProgress; bool TestMode; bool SkipUnpCRC; bool NoFileHeader; File *SrcFile; File *DestFile; CmdAdd *Command; FileHeader *SubHead; int64 *SubHeadPos; #ifndef RAR_NOCRYPT CryptData *Crypt; CryptData *Decrypt; #endif int LastPercent; wchar CurrentCommand; public: ComprDataIO(); ~ComprDataIO(); void Init(); int UnpRead(byte *Addr,size_t Count); void UnpWrite(byte *Addr,size_t Count); void EnableShowProgress(bool Show) {ShowProgress=Show;} void GetUnpackedData(byte **Data,size_t *Size); void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;} void SetTestMode(bool Mode) {TestMode=Mode;} void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;} void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;} void SetFiles(File *SrcFile,File *DestFile); void SetCommand(CmdAdd *Cmd) {Command=Cmd;} void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} bool SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;} void AdjustTotalArcSize(Archive *Arc); bool PackVolume; bool UnpVolume; bool NextVolumeMissing; int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; // Size of already processed archives. // Used to calculate the total operation progress. int64 ProcessedArcSize; // Last extracted archive size up to QO or RR block. int64 LastArcSize; int64 TotalArcSize; DataHash PackedDataHash; // Packed write and unpack read hash. DataHash PackHash; // Pack read hash. DataHash UnpHash; // Unpack write hash. bool Encryption; bool Decryption; }; #endif unrar/recvol.hpp000666 000000 000000 00000004657 15026203745 012402 0ustar00000000 000000 #ifndef _RAR_RECVOL_ #define _RAR_RECVOL_ #define REV5_SIGN "Rar!\x1aRev" #define REV5_SIGN_SIZE 8 class RecVolumes3 { private: File *SrcFile[256]; std::vector Buf; #ifdef RAR_SMP ThreadPool *RSThreadPool; #endif public: RecVolumes3(CommandData *Cmd,bool TestOnly); ~RecVolumes3(); void Make(CommandData *Cmd,std::wstring ArcName); bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent); void Test(CommandData *Cmd,const std::wstring &Name); }; struct RecVolItem { File *f; std::wstring Name; uint CRC; uint64 FileSize; bool New; // Newly created RAR volume. bool Valid; // If existing RAR volume is valid. }; class RecVolumes5; struct RecRSThreadData { RecVolumes5 *RecRSPtr; RSCoder16 *RS; bool Encode; uint DataNum; const byte *Data; size_t StartPos; size_t Size; }; class RecVolumes5 { private: void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode); uint ReadHeader(File *RecFile,bool FirstRev); std::vector RecItems; byte *RealReadBuffer; // Real pointer returned by 'new'. byte *ReadBuffer; // Pointer aligned for SSE instructions. byte *RealBuf; // Real pointer returned by 'new'. byte *Buf; // Store ECC or recovered data here, aligned for SSE. size_t RecBufferSize; // Buffer area allocated for single volume. uint DataCount; // Number of archives. uint RecCount; // Number of recovery volumes. uint TotalCount; // Total number of archives and recovery volumes. bool *ValidFlags; // Volume validity flags for recovering. uint MissingVolumes; // Number of missing or bad RAR volumes. #ifdef RAR_SMP ThreadPool *RecThreadPool; #endif uint MaxUserThreads; // Maximum number of threads defined by user. RecRSThreadData *ThreadData; // Array to store thread parameters. public: // 'public' only because called from thread functions. void ProcessAreaRS(RecRSThreadData *td); public: RecVolumes5(CommandData *Cmd,bool TestOnly); ~RecVolumes5(); bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent); void Test(CommandData *Cmd,const std::wstring &Name); }; bool RecVolumesRestore(CommandData *Cmd,const std::wstring &Name,bool Silent); void RecVolumesTest(CommandData *Cmd,Archive *Arc,const std::wstring &Name); #endif unrar/resource.hpp000666 000000 000000 00000000210 15026203745 012714 0ustar00000000 000000 #ifndef _RAR_RESOURCE_ #define _RAR_RESOURCE_ #ifdef RARDLL #define St(x) (L"") #else const wchar *St(MSGID StringId); #endif #endif unrar/rijndael.hpp000666 000000 000000 00000003507 15026203745 012671 0ustar00000000 000000 #ifndef _RIJNDAEL_H_ #define _RIJNDAEL_H_ /************************************************************************** * This code is based on Szymon Stefanek public domain AES implementation * **************************************************************************/ #define _MAX_KEY_COLUMNS (256/32) #define _MAX_ROUNDS 14 #define MAX_IV_SIZE 16 class Rijndael { private: #ifdef USE_SSE #ifdef __GNUC__ __attribute__((target("aes"))) #endif void blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer); #ifdef __GNUC__ __attribute__((target("aes"))) #endif void blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer); bool AES_NI; #endif #ifdef USE_NEON_AES // In Android we must specify -march=armv8-a+crypto compiler switch // to support Neon AES commands, "crypto" attribute seems to be optional. __attribute__((target("+crypto"))) void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer); __attribute__((target("+crypto"))) void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer); bool AES_Neon; #endif void keySched(byte key[_MAX_KEY_COLUMNS][4]); void keyEncToDec(); void GenerateTables(); // RAR always uses CBC, but we may need to turn it off when calling // this code from other archive formats with CTR and other modes. bool CBCMode; int m_uRounds; byte m_initVector[MAX_IV_SIZE]; byte m_expandedKey[_MAX_ROUNDS+1][4][4]; public: Rijndael(); void Init(bool Encrypt,const byte *key,uint keyLen,const byte *initVector); void blockEncrypt(const byte *input, size_t inputLen, byte *outBuffer); void blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer); void SetCBCMode(bool Mode) {CBCMode=Mode;} }; #endif // _RIJNDAEL_H_ unrar/rs.hpp000666 000000 000000 00000001463 15026203745 011524 0ustar00000000 000000 #ifndef _RAR_RS_ #define _RAR_RS_ #define MAXPAR 255 // Maximum parity data size. #define MAXPOL 512 // Maximum polynomial degree. class RSCoder { private: void gfInit(); int gfMult(int a,int b); void pnInit(); void pnMult(int *p1,int *p2,int *r); int gfExp[MAXPOL]; // Galois field exponents. int gfLog[MAXPAR+1]; // Galois field logarithms. int GXPol[MAXPOL*2]; // Generator polynomial g(x). int ErrorLocs[MAXPAR+1],ErrCount; int Dnm[MAXPAR+1]; int ParSize; // Parity bytes size and so the number of recovery volumes. int ELPol[MAXPOL]; // Error locator polynomial. bool FirstBlockDone; public: void Init(int ParSize); void Encode(byte *Data,int DataSize,byte *DestData); bool Decode(byte *Data,int DataSize,int *EraLoc,int EraSize); }; #endif unrar/rs16.hpp000666 000000 000000 00000003071 15026203745 011670 0ustar00000000 000000 #ifndef _RAR_RS16_ #define _RAR_RS16_ class RSCoder16 { private: static const uint gfSize=65535; // Galois field size. void gfInit(); // Galois field inititalization. inline uint gfAdd(uint a,uint b); // Addition in Galois field. inline uint gfMul(uint a,uint b); // Multiplication in Galois field. inline uint gfInv(uint a); // Inverse element in Galois field. uint *gfExp; // Galois field exponents. uint *gfLog; // Galois field logarithms. void MakeEncoderMatrix(); void MakeDecoderMatrix(); void InvertDecoderMatrix(); #ifdef USE_SSE #if defined(USE_SSE) && defined(__GNUC__) __attribute__((target("ssse3"))) #endif bool SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); #endif bool Decoding; // If we are decoding or encoding data. uint ND; // Number of data units. uint NR; // Number of Reed-Solomon code units. uint NE; // Number of erasures. bool *ValidFlags; // Validity flags for data and ECC units. uint *MX; // Cauchy based coding or decoding matrix. uint *DataLog; // Buffer to store data logarithms for UpdateECC. size_t DataLogSize; public: RSCoder16(); ~RSCoder16(); bool Init(uint DataCount, uint RecCount, bool *ValidityFlags); #if 0 // We use only UpdateECC now. void Process(const uint *Data, uint *Out); #endif void UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); }; #endif unrar/scantree.hpp000666 000000 000000 00000004223 15026203745 012701 0ustar00000000 000000 #ifndef _RAR_SCANTREE_ #define _RAR_SCANTREE_ enum SCAN_DIRS { SCAN_SKIPDIRS, // Skip directories, but recurse for files if recursion mode is enabled. SCAN_GETDIRS, // Get subdirectories in recurse mode. SCAN_GETDIRSTWICE, // Get the directory name both before and after the list of files it contains. SCAN_GETCURDIRS // Get subdirectories in current directory even in RECURSE_NONE mode. }; enum SCAN_CODE { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT }; class CommandData; class ScanTree { private: static constexpr size_t MAXSCANDEPTH = MAXPATHSIZE/2; bool ExpandFolderMask(); bool GetFilteredMask(); bool GetNextMask(); SCAN_CODE FindProc(FindData *FD); void ScanError(bool &Error); // FindFile *FindStack[MAXSCANDEPTH]; std::vector FindStack; int Depth; int SetAllMaskDepth; StringList *FileMasks; RECURSE_MODE Recurse; bool GetLinks; SCAN_DIRS GetDirs; uint Errors; // Set when processing paths like c:\ (root directory without wildcards). bool ScanEntireDisk; std::wstring CurMask; std::wstring OrigCurMask; // Store all folder masks generated from folder wildcard mask in non-recursive mode. StringList ExpandedFolderList; // Store a filter string for folder wildcard in recursive mode. StringList FilterList; // Save the list of unreadable dirs here. StringList *ErrDirList; std::vector *ErrDirSpecPathLength; // Set if processing a folder wildcard mask. bool FolderWildcards; bool SearchAllInRoot; size_t SpecPathLength; std::wstring ErrArcName; CommandData *Cmd; public: ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs); ~ScanTree(); SCAN_CODE GetNext(FindData *FindData); size_t GetSpecPathLength() {return SpecPathLength;} uint GetErrors() {return Errors;}; void SetErrArcName(const std::wstring &Name) {ErrArcName=Name;} void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;} void SetErrDirList(StringList *List,std::vector *Lengths) { ErrDirList=List; ErrDirSpecPathLength=Lengths; } }; #endif unrar/secpassword.hpp000666 000000 000000 00000001601 15026203745 013427 0ustar00000000 000000 #ifndef _RAR_SECURE_PASSWORD_ #define _RAR_SECURE_PASSWORD_ // Store a password securely (if data encryption is provided by OS) // or obfuscated to make search for password in memory dump less trivial. class SecPassword { private: void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); std::vector Password = std::vector(MAXPASSWORD); bool PasswordSet; public: SecPassword(); ~SecPassword(); void Clean(); void Get(wchar *Psw,size_t MaxSize); void Get(std::wstring &Psw); void Set(const wchar *Psw); bool IsSet() {return PasswordSet;} size_t Length(); bool operator == (SecPassword &psw); }; void cleandata(void *data,size_t size); inline void cleandata(std::wstring &s) {cleandata(&s[0],s.size()*sizeof(s[0]));} void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess); #endif unrar/sha1.hpp000666 000000 000000 00000000612 15026203745 011727 0ustar00000000 000000 #ifndef _RAR_SHA1_ #define _RAR_SHA1_ typedef struct { uint32 state[5]; uint64 count; unsigned char buffer[64]; } sha1_context; void sha1_init( sha1_context * c ); void sha1_process(sha1_context * c, const byte *data, size_t len); void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len); void sha1_done( sha1_context * c, uint32 digest[5] ); #endif unrar/sha256.hpp000666 000000 000000 00000000613 15026203745 012104 0ustar00000000 000000 #ifndef _RAR_SHA256_ #define _RAR_SHA256_ #define SHA256_DIGEST_SIZE 32 typedef struct { uint32 H[8]; uint64 Count; byte Buffer[64]; } sha256_context; void sha256_init(sha256_context *ctx); void sha256_process(sha256_context *ctx, const void *Data, size_t Size); void sha256_done(sha256_context *ctx, byte *Digest); void sha256_get(const void *Data, size_t Size, byte *Digest); #endif unrar/smallfn.hpp000666 000000 000000 00000000176 15026203745 012534 0ustar00000000 000000 #ifndef _RAR_SMALLFN_ #define _RAR_SMALLFN_ int ToPercent(int64 N1,int64 N2); int ToPercentUnlim(int64 N1,int64 N2); #endif unrar/strfn.hpp000666 000000 000000 00000003744 15026203745 012240 0ustar00000000 000000 #ifndef _RAR_STRFN_ #define _RAR_STRFN_ const char* NullToEmpty(const char *Str); const wchar* NullToEmpty(const wchar *Str); void OemToExt(const std::string &Src,std::string &Dest); enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8}; void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding); int stricomp(const char *s1,const char *s2); int strnicomp(const char *s1,const char *s2,size_t n); wchar* RemoveEOL(wchar *Str); void RemoveEOL(std::wstring &Str); wchar* RemoveLF(wchar *Str); void RemoveLF(std::wstring &Str); void strncpyz(char *dest, const char *src, size_t maxlen); void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen); void strncatz(char* dest, const char* src, size_t maxlen); void wcsncatz(wchar* dest, const wchar* src, size_t maxlen); #if defined(SFX_MODULE) unsigned char etoupper(unsigned char c); #endif wchar etoupperw(wchar c); bool IsDigit(int ch); bool IsSpace(int ch); bool IsAlpha(int ch); void BinToHex(const byte *Bin,size_t BinSize,std::wstring &Hex); #ifndef SFX_MODULE uint GetDigits(uint Number); #endif bool LowAscii(const std::string &Str); bool LowAscii(const std::wstring &Str); int wcsicompc(const wchar *s1,const wchar *s2); int wcsicompc(const std::wstring &s1,const std::wstring &s2); int wcsnicompc(const wchar *s1,const wchar *s2,size_t n); int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n); void itoa(int64 n,char *Str,size_t MaxSize); void itoa(int64 n,wchar *Str,size_t MaxSize); void fmtitoa(int64 n,wchar *Str,size_t MaxSize); std::wstring GetWide(const char *Src); bool GetCmdParam(const std::wstring &CmdLine,std::wstring::size_type &Pos,std::wstring &Param); #ifndef RARDLL void PrintfPrepareFmt(const wchar *Org,std::wstring &Cvt); std::wstring wstrprintf(const wchar *fmt,...); std::wstring vwstrprintf(const wchar *fmt,va_list arglist); #endif #ifdef _WIN_ALL bool ExpandEnvironmentStr(std::wstring &Str); #endif void TruncateAtZero(std::wstring &Str); void ReplaceEsc(std::wstring &Str); #endif unrar/strlist.hpp000666 000000 000000 00000001634 15026203745 012604 0ustar00000000 000000 #ifndef _RAR_STRLIST_ #define _RAR_STRLIST_ class StringList { private: std::vector StringData; size_t CurPos; size_t StringsCount; size_t SaveCurPos[16],SavePosNumber; public: StringList(); void Reset(); // void AddStringA(const char *Str); void AddString(const wchar *Str); void AddString(const std::wstring &Str); // bool GetStringA(char *Str,size_t MaxLength); bool GetString(wchar *Str,size_t MaxLength); bool GetString(std::wstring &Str); bool GetString(wchar *Str,size_t MaxLength,int StringNum); bool GetString(std::wstring &Str,int StringNum); wchar* GetString(); bool GetString(wchar **Str); void Rewind(); size_t ItemsCount() {return StringsCount;}; size_t GetCharCount() {return StringData.size();} bool Search(const std::wstring &Str,bool CaseSensitive); void SavePosition(); void RestorePosition(); }; #endif unrar/suballoc.hpp000666 000000 000000 00000004446 15026203745 012710 0ustar00000000 000000 /**************************************************************************** * This file is part of PPMd project * * Written and distributed to public domain by Dmitry Shkarin 1997, * * 1999-2000 * * Contents: interface to memory allocation routines * ****************************************************************************/ #if !defined(_SUBALLOC_H_) #define _SUBALLOC_H_ #if defined(__GNUC__) && defined(ALLOW_MISALIGNED) #define RARPPM_PACK_ATTR __attribute__ ((packed)) #else #define RARPPM_PACK_ATTR #endif /* defined(__GNUC__) */ #ifdef ALLOW_MISALIGNED #pragma pack(1) #endif struct RARPPM_MEM_BLK { ushort Stamp, NU; RARPPM_MEM_BLK* next, * prev; void insertAt(RARPPM_MEM_BLK* p) { next=(prev=p)->next; p->next=next->prev=this; } void remove() { prev->next=next; next->prev=prev; } } RARPPM_PACK_ATTR; #ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else #pragma pack() #endif #endif class SubAllocator { private: static const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; static const int N_INDEXES=N1+N2+N3+N4; struct RAR_NODE { RAR_NODE* next; }; inline void InsertNode(void* p,int indx); inline void* RemoveNode(int indx); inline uint U2B(int NU); inline void SplitBlock(void* pv,int OldIndx,int NewIndx); inline void GlueFreeBlocks(); void* AllocUnitsRare(int indx); inline RARPPM_MEM_BLK* MBPtr(RARPPM_MEM_BLK *BasePtr,int Items); long SubAllocatorSize; byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; byte *HeapStart,*LoUnit, *HiUnit; struct RAR_NODE FreeList[N_INDEXES]; public: SubAllocator(); ~SubAllocator() {StopSubAllocator();} void Clean(); bool StartSubAllocator(int SASize); void StopSubAllocator(); void InitSubAllocator(); inline void* AllocContext(); inline void* AllocUnits(int NU); inline void* ExpandUnits(void* ptr,int OldNU); inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU); inline void FreeUnits(void* ptr,int OldNU); long GetAllocatedMemory() {return(SubAllocatorSize);} byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; }; #endif /* !defined(_SUBALLOC_H_) */ unrar/system.hpp000666 000000 000000 00000002142 15026203745 012417 0ustar00000000 000000 #ifndef _RAR_SYSTEM_ #define _RAR_SYSTEM_ #ifdef _WIN_ALL #ifndef BELOW_NORMAL_PRIORITY_CLASS #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 #endif #ifndef PROCESS_MODE_BACKGROUND_BEGIN #define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 #define PROCESS_MODE_BACKGROUND_END 0x00200000 #endif #ifndef SHTDN_REASON_MAJOR_APPLICATION #define SHTDN_REASON_MAJOR_APPLICATION 0x00040000 #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #define SHTDN_REASON_MINOR_MAINTENANCE 0x00000001 #endif #endif void InitSystemOptions(int SleepTime); void SetPriority(int Priority); clock_t MonoClock(); void Wait(); bool EmailFile(const std::wstring &FileName,std::wstring MailToW); #ifdef _WIN_ALL bool SetPrivilege(LPCTSTR PrivName); #endif void Shutdown(POWER_MODE Mode); bool ShutdownCheckAnother(bool Open); #ifdef _WIN_ALL HMODULE WINAPI LoadSysLibrary(const wchar *Name); bool IsUserAdmin(); #endif #ifdef USE_SSE enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2}; SSE_VERSION GetSSEVersion(); extern SSE_VERSION _SSE_Version; #endif #endif unrar/threadpool.hpp000666 000000 000000 00000005271 15026203746 013243 0ustar00000000 000000 #ifndef _RAR_THREADPOOL_ #define _RAR_THREADPOOL_ #ifndef RAR_SMP const uint MaxPoolThreads=1; // For single threaded version. #else // We need to use the processor groups API to increase it beyond 64. // Also be sure to check and adjust if needed per thread and total block size // when compressing if going above 64. const uint MaxPoolThreads=64; #ifdef _UNIX #include #include #endif // Undefine for debugging. #define USE_THREADS #ifdef _UNIX #define NATIVE_THREAD_TYPE void* typedef void* (*NATIVE_THREAD_PTR)(void *Data); typedef pthread_t THREAD_HANDLE; typedef pthread_mutex_t CRITSECT_HANDLE; #else #define NATIVE_THREAD_TYPE DWORD WINAPI typedef DWORD (WINAPI *NATIVE_THREAD_PTR)(void *Data); typedef HANDLE THREAD_HANDLE; typedef CRITICAL_SECTION CRITSECT_HANDLE; #endif typedef void (*PTHREAD_PROC)(void *Data); #define THREAD_PROC(fn) void fn(void *Data) uint GetNumberOfCPU(); uint GetNumberOfThreads(); class ThreadPool { private: struct QueueEntry { PTHREAD_PROC Proc; void *Param; }; void CreateThreads(); static NATIVE_THREAD_TYPE PoolThread(void *Param); void PoolThreadLoop(); bool GetQueuedTask(QueueEntry *Task); // Number of threads in the pool. Must not exceed MaxPoolThreads. uint MaxAllowedThreads; THREAD_HANDLE ThreadHandles[MaxPoolThreads]; // Number of actually created threads. uint ThreadsCreatedCount; uint ActiveThreads; QueueEntry TaskQueue[MaxPoolThreads]; uint QueueTop; uint QueueBottom; bool Closing; // Set true to quit all threads. #ifdef _WIN_ALL // Semaphore counting number of tasks stored in queue. HANDLE QueuedTasksCnt; // Event signalling if no active tasks are performing now. HANDLE NoneActive; #elif defined(_UNIX) // Semaphores seem to be slower than conditional variables in pthreads, // so we use the conditional variable to count tasks stored in queue. uint QueuedTasksCnt; pthread_cond_t QueuedTasksCntCond; pthread_mutex_t QueuedTasksCntMutex; bool AnyActive; // Active tasks present flag. pthread_cond_t AnyActiveCond; pthread_mutex_t AnyActiveMutex; #endif // Pool critical section. We use the single section for all branches // to avoid deadlocks, when thread1 has section1 and wants section2 // and thread2 has section2 and wants section1. CRITSECT_HANDLE CritSection; public: ThreadPool(uint MaxThreads); ~ThreadPool(); void AddTask(PTHREAD_PROC Proc,void *Data); void WaitDone(); #ifdef _WIN_ALL static int ThreadPriority; static void SetPriority(int Priority) {ThreadPriority=Priority;} #endif }; #endif // RAR_SMP #endif // _RAR_THREADPOOL_ unrar/timefn.hpp000666 000000 000000 00000005121 15026203746 012356 0ustar00000000 000000 #ifndef _RAR_TIMEFN_ #define _RAR_TIMEFN_ struct RarLocalTime { uint Year; uint Month; uint Day; uint Hour; uint Minute; uint Second; uint Reminder; // Part of time smaller than 1 second, represented in 1/REMINDER_PRECISION intervals. uint wDay; uint yDay; }; class RarTime { private: static const uint TICKS_PER_SECOND = 1000000000; // Internal precision. // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601. // We use nanoseconds here to handle the high precision Unix time. // It allows dates up to July 2185. // // If we'll ever need to extend the date range, we can define a lower // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows // versions can differ in least significant digits of "lt" time output // for Unix archives. // Alternatively we can introduce 'bool HighPrecision' set to true // in SetUnixNS() and TicksPerSecond() instead of constant above. // It might be more reliable than defining TicksPerSecond variable, // which wouldn't survive memset of any structure hosting RarTime. // We would need to eliminate all such memsets in the entire code first. uint64 itime; public: // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND. // Unlike TICKS_PER_SECOND, it is a public field. static const uint REMINDER_PRECISION = TICKS_PER_SECOND; public: RarTime() {Reset();} bool operator == (const RarTime &rt) const {return itime==rt.itime;} bool operator != (const RarTime &rt) const {return itime!=rt.itime;} bool operator < (const RarTime &rt) const {return itime (const RarTime &rt) const {return itime>rt.itime;} bool operator >= (const RarTime &rt) const {return itime>rt.itime || itime==rt.itime;} void GetLocal(RarLocalTime *lt); void SetLocal(RarLocalTime *lt); #ifdef _WIN_ALL void GetWinFT(FILETIME *ft); void SetWinFT(FILETIME *ft); #endif uint64 GetWin(); void SetWin(uint64 WinTime); time_t GetUnix(); void SetUnix(time_t ut); uint64 GetUnixNS(); void SetUnixNS(uint64 ns); uint GetDos(); void SetDos(uint DosTime); void GetText(wchar *DateStr,size_t MaxSize,bool FullMS); void SetIsoText(const wchar *TimeText); void SetAgeText(const wchar *TimeText); void SetCurrentTime(); void Reset() {itime=0;} bool IsSet() {return itime!=0;} void Adjust(int64 ns); }; const wchar *GetMonthName(uint Month); bool IsLeapYear(uint Year); #endif unrar/ui.hpp000666 000000 000000 00000016207 15026203746 011520 0ustar00000000 000000 #ifndef _RAR_UI_ #define _RAR_UI_ // UIERROR_ - error message; // UIMSG_ - informational message; // UIWAIT_ - message waiting for user confirmation; // UIEVENT_ - if simple message is not enough; enum UIMESSAGE_CODE { UIERROR_SYSERRMSG, UIERROR_GENERALERRMSG, UIERROR_INCERRCOUNT, UIERROR_CHECKSUM, UIERROR_CHECKSUMENC, UIERROR_CHECKSUMPACKED, UIERROR_BADPSW, UIERROR_MEMORY, UIERROR_FILEOPEN, UIERROR_FILECREATE, UIERROR_FILECLOSE, UIERROR_FILESEEK, UIERROR_FILEREAD, UIERROR_FILEWRITE, UIERROR_FILEDELETE, UIERROR_RECYCLEFAILED, UIERROR_FILERENAME, UIERROR_FILEATTR, UIERROR_FILECOPY, UIERROR_FILECOPYHINT, UIERROR_DIRCREATE, UIERROR_SLINKCREATE, UIERROR_HLINKCREATE, UIERROR_NOLINKTARGET, UIERROR_NEEDADMIN, UIERROR_ARCBROKEN, UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN, UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN, UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, UIERROR_NOTSFX, UIERROR_OLDTOSFX,UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM,UIERROR_EXTRDICTOUTMEM, UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING, UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF, UIERROR_TRUNCSERVICE, UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME, UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED, UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED, UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT, UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA, UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX, UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_NEEEDSFX64, UIERROR_EMAIL, UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET, UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET, UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, UIERROR_DIRNAMEEXISTS, UIERROR_TRUNCPSW, UIERROR_ADJUSTVALUE, UIERROR_SKIPUNSAFELINK, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, UIMSG_RRFOUND, UIMSG_RRNOTFOUND, UIMSG_RRDAMAGED, UIMSG_BLOCKSRECOVERED, UIMSG_COPYINGDATA, UIMSG_AREADAMAGED, UIMSG_SECTORDAMAGED, UIMSG_SECTORRECOVERED, UIMSG_SECTORNOTRECOVERED, UIMSG_FOUND, UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, UIMSG_SKIPENCARC, UIMSG_FILERENAME, UIWAIT_FIRST, UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, UIEVENT_FIRST, UIEVENT_SEARCHDUPFILESSTART, UIEVENT_SEARCHDUPFILESEND, UIEVENT_CLEARATTRSTART, UIEVENT_CLEARATTRFILE, UIEVENT_DELADDEDSTART, UIEVENT_DELADDEDFILE, UIEVENT_FILESFOUND, UIEVENT_ERASEDISK, UIEVENT_FILESUMSTART, UIEVENT_FILESUMPROGRESS, UIEVENT_FILESUMEND, UIEVENT_PROTECTSTART, UIEVENT_PROTECTEND, UIEVENT_TESTADDEDSTART, UIEVENT_TESTADDEDEND, UIEVENT_RRTESTINGSTART, UIEVENT_RRTESTINGEND, UIEVENT_NEWARCHIVE, UIEVENT_NEWREVFILE }; // Flags for uiAskReplace function. enum UIASKREP_FLAGS { UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4 }; // Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only // UIASKREP_R_REPLACE, UIASKREP_R_SKIP and UIASKREP_R_CANCEL codes. enum UIASKREP_RESULT { UIASKREP_R_REPLACE,UIASKREP_R_SKIP,UIASKREP_R_REPLACEALL,UIASKREP_R_SKIPALL, UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED }; UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); void uiInit(SOUND_NOTIFY_MODE Sound); void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName); bool uiStartFileExtract(const std::wstring &FileName,bool Extract,bool Test,bool Skip); void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize); void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); enum UIPASSWORD_TYPE { UIPASSWORD_GLOBAL, // For -p, -hp without parameter or Ctrl+P in WinRAR. UIPASSWORD_FILE, // Extracting an encrypted file. UIPASSWORD_ARCHIVE, // Extracting or opening an encrypted header archive. }; bool uiGetPassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password,CheckPassword *CheckPwd); bool uiIsGlobalPasswordSet(); enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; void uiAlarm(UIALARM_TYPE Type); void uiEolAfterMsg(); bool uiAskNextVolume(std::wstring &VolName); #if !defined(SILENT) && !defined(SFX_MODULE) void uiAskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); #endif bool uiAskRepeatWrite(const std::wstring &FileName,bool DiskFull); bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize); #ifndef SFX_MODULE const wchar *uiGetMonthName(uint Month); #endif class uiMsgStore { private: static const size_t MAX_MSG = 8; const wchar *Str[MAX_MSG]; uint Num[MAX_MSG]; uint StrSize,NumSize; UIMESSAGE_CODE Code; public: uiMsgStore(UIMESSAGE_CODE Code) { // Init arrays in case a caller passes fewer parameters than expected. for (uint I=0;ICode=Code; } uiMsgStore& operator << (const wchar *s) { if (StrSize void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN) { // Process first parameter and pass the rest to same uiMsgBase. Store< void uiMsg(UIMESSAGE_CODE Code,TN&&... aN) { uiMsgStore Store(Code); uiMsgBase(Store,aN...); Store.Msg(); } #endif unrar/unicode.hpp000666 000000 000000 00000004235 15026203746 012527 0ustar00000000 000000 #ifndef _RAR_UNICODE_ #define _RAR_UNICODE_ #if defined( _WIN_ALL) #define DBCS_SUPPORTED #endif bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); bool WideToChar(const std::wstring &Src,std::string &Dest); bool CharToWide(const std::string &Src,std::wstring &Dest); byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize); void WideToRaw(const std::wstring &Src,std::vector &Dest); wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); std::wstring RawToWide(const std::vector &Src); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); void WideToUtf(const std::wstring &Src,std::string &Dest); size_t WideToUtfSize(const wchar *Src); bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize); bool UtfToWide(const char *Src,std::wstring &Dest); //bool UtfToWide(const std::vector &Src,std::wstring &Dest); bool IsTextUtf8(const byte *Src); bool IsTextUtf8(const byte *Src,size_t SrcSize); int wcsicomp(const wchar *s1,const wchar *s2); inline int wcsicomp(const std::wstring &s1,const std::wstring &s2) {return wcsicomp(s1.c_str(),s2.c_str());} int wcsnicomp(const wchar *s1,const wchar *s2,size_t n); inline int wcsnicomp(const std::wstring &s1,const std::wstring &s2,size_t n) {return wcsnicomp(s1.c_str(),s2.c_str(),n);} const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search); std::wstring::size_type wcscasestr(const std::wstring &str, const std::wstring &search); #ifndef SFX_MODULE wchar* wcslower(wchar *s); void wcslower(std::wstring &s); wchar* wcsupper(wchar *s); void wcsupper(std::wstring &s); #endif int toupperw(int ch); int tolowerw(int ch); int atoiw(const std::wstring &s); int64 atoilw(const std::wstring &s); #ifdef DBCS_SUPPORTED class SupportDBCS { public: SupportDBCS(); void Init(); char* charnext(const char *s); bool IsLeadByte[256]; bool DBCSMode; }; extern SupportDBCS gdbcs; inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);} inline bool IsDBCSMode() {return gdbcs.DBCSMode;} #else #define charnext(s) ((s)+1) #define IsDBCSMode() (false) #endif #endif unrar/unpack.hpp000666 000000 000000 00000032163 15026203746 012363 0ustar00000000 000000 #ifndef _RAR_UNPACK_ #define _RAR_UNPACK_ // Maximum allowed number of compressed bits processed in quick mode. #define MAX_QUICK_DECODE_BITS 9 // Maximum number of filters per entire data block. Must be at least // twice more than MAX_PACK_FILTERS to store filters from two data blocks. #define MAX_UNPACK_FILTERS 8192 // Maximum number of filters per entire data block for RAR3 unpack. // Must be at least twice more than v3_MAX_PACK_FILTERS to store filters // from two data blocks. #define MAX3_UNPACK_FILTERS 8192 // Limit maximum number of channels in RAR3 delta filter to some reasonable // value to prevent too slow processing of corrupt archives with invalid // channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. // No need to provide it for RAR5, which uses only 5 bits to store channels. #define MAX3_UNPACK_CHANNELS 1024 // Maximum size of single filter block. We restrict it to limit memory // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. #define MAX_FILTER_BLOCK_SIZE 0x400000 // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_READ, // so we keep the number of buffered filters in unpacker reasonable. #define UNPACK_MAX_WRITE 0x400000 // Decode compressed bit fields to alphabet numbers. struct DecodeTable:PackDef { // Real size of DecodeNum table. uint MaxNum; // Left aligned start and upper limit codes defining code space // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of // range for bit length and DecodeLen[BitLength] defines next code // after the end of range or in other words the upper limit code // for specified bit length. uint DecodeLen[16]; // 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. uint DecodePos[16]; // Number of compressed bits processed in quick mode. // Must not exceed MAX_QUICK_DECODE_BITS. uint QuickBits; // Translates compressed bits (up to QuickBits length) // to bit length in quick mode. byte QuickLen[1< FilterSrcMemory; std::vector FilterDstMemory; // Filters code, one entry per filter. std::vector Filters; size_t OldDist[4],OldDistPtr; uint LastLength; // LastDist is necessary only for RAR2 and older with circular OldDist // array. In RAR3 last distance is always stored in OldDist[0]. uint LastDist; size_t UnpPtr; // Current position in window. size_t PrevPtr; // UnpPtr value for previous loop iteration. bool FirstWinDone; // At least one dictionary was processed. size_t WrPtr; // Last written unpacked data position. // Top border of read packed data. int ReadTop; // Border to call UnpReadBuf. We use it instead of (ReadTop-C) // for optimization reasons. Ensures that we have C bytes in buffer // unless we are at the end of file. int ReadBorder; UnpackBlockHeader BlockHeader; UnpackBlockTables BlockTables; size_t WriteBorder; // Perform write when reaching this border. byte *Window; FragmentedWindow FragWindow; bool Fragmented; int64 DestUnpSize; bool Suspended; bool UnpSomeRead; int64 WrittenFileSize; bool FileExtracted; /***************************** Unpack v 1.5 *********************************/ void Unpack15(bool Solid); void ShortLZ(); void LongLZ(); void HuffDecode(); void GetFlagsBuf(); void UnpInitData15(bool Solid); void InitHuff(); void CorrHuff(ushort *CharSet,byte *NumToPlace); void CopyString15(uint Distance,uint Length); uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); ushort ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; byte NToPl[256],NToPlB[256],NToPlC[256]; uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; int Buf60,NumHuf,StMode,LCount,FlagsCnt; uint Nhfb,Nlzb,MaxDist3; /***************************** Unpack v 1.5 *********************************/ /***************************** Unpack v 2.0 *********************************/ void Unpack20(bool Solid); DecodeTable MD[4]; // Decode multimedia data, up to 4 channels. unsigned char UnpOldTable20[MC20*4]; bool UnpAudioBlock; uint UnpChannels,UnpCurChannel; int UnpChannelDelta; void CopyString20(uint Length,uint Distance); bool ReadTables20(); void UnpWriteBuf20(); void UnpInitData20(int Solid); void ReadLastTables(); byte DecodeAudio(int Delta); struct AudioVariables AudV[4]; /***************************** Unpack v 2.0 *********************************/ /***************************** Unpack v 3.0 *********************************/ enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; void UnpInitData30(bool Solid); void Unpack29(bool Solid); void InitFilters30(bool Solid); bool ReadEndOfBlock(); bool ReadVMCode(); bool ReadVMCodePPM(); bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize); int SafePPMDecodeChar(); bool ReadTables30(); bool UnpReadBuf30(); void UnpWriteBuf30(); void ExecuteCode(VM_PreparedProgram *Prg); int PrevLowDist,LowDistRepCount; ModelPPM PPM; int PPMEscChar; byte UnpOldTable[HUFF_TABLE_SIZE30]; int UnpBlockType; // If we already read decoding tables for Unpack v2,v3,v5. // We should not use a single variable for all algorithm versions, // because we can have a corrupt archive with one algorithm file // followed by another algorithm file with "solid" flag and we do not // want to reuse tables from one algorithm in another. bool TablesRead2,TablesRead3,TablesRead5; // Virtual machine to execute filters code. RarVM VM; // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. BitInput VMCodeInp; // Filters code, one entry per filter. std::vector Filters30; // Filters stack, several entrances of same filter are possible. std::vector PrgStack; // Lengths of preceding data blocks, one length of one last block // for every filter. Used to reduce the size required to write // the data block length if lengths are repeating. std::vector OldFilterLengths; int LastFilter; /***************************** Unpack v 3.0 *********************************/ public: Unpack(ComprDataIO *DataIO); ~Unpack(); void Init(uint64 WinSize,bool Solid); void AllowLargePages(bool Allow) {Alloc.AllowLargePages(Allow);} void DoUnpack(uint Method,bool Solid); bool IsFileExtracted() {return FileExtracted;} void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} #ifdef RAR_SMP void SetThreads(uint Threads); void UnpackDecode(UnpackThreadData &D); #endif uint64 AllocWinSize; size_t MaxWinSize; size_t MaxWinMask; bool ExtraDist; // Allow distances up to 1 TB. byte GetChar() { if (Inp.InAddr>BitInput::MAX_SIZE-30) { UnpReadBuf(); if (Inp.InAddr>=BitInput::MAX_SIZE) // If nothing was read. return 0; } return Inp.InBuf[Inp.InAddr++]; } // If window position crosses the window beginning, wrap it to window end. // Replaces &MaxWinMask for non-power 2 window sizes. // We can't use %WinSize everywhere not only for performance reason, // but also because C++ % is reminder instead of modulo. // We need additional checks in the code if WinPos distance from 0 // can exceed MaxWinSize. Alternatively we could add such check here. inline size_t WrapDown(size_t WinPos) { return WinPos >= MaxWinSize ? WinPos + MaxWinSize : WinPos; } // If window position crosses the window end, wrap it to window beginning. // Replaces &MaxWinMask for non-power 2 window sizes. // Unlike WrapDown, we can use %WinSize here if there was no size_t // overflow when calculating WinPos. // We need additional checks in the code if WinPos distance from MaxWinSize // can be MaxWinSize or more. Alternatively we could add such check here // or use %WinSize. inline size_t WrapUp(size_t WinPos) { return WinPos >= MaxWinSize ? WinPos - MaxWinSize : WinPos; } }; #endif unrar/version.hpp000666 000000 000000 00000000242 15026203746 012560 0ustar00000000 000000 #define RARVER_MAJOR 7 #define RARVER_MINOR 12 #define RARVER_BETA 0 #define RARVER_DAY 23 #define RARVER_MONTH 6 #define RARVER_YEAR 2025 unrar/volume.hpp000666 000000 000000 00000000233 15026203746 012402 0ustar00000000 000000 #ifndef _RAR_VOLUME_ #define _RAR_VOLUME_ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, wchar Command); #endif unrar/makefile000666 000000 000000 00000003773 14660600670 012076 0ustar00000000 000000 # # Makefile for UNIX - unrar # Linux using GCC # 2024.08.19: -march=native isn't recognized on some platforms such as RISCV64. # Thus we removed it. Clang ARM users can add -march=armv8-a+crypto to enable # ARM NEON crypto. CXX=c++ CXXFLAGS=-O2 -std=c++11 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else LIBFLAGS=-fPIC DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP STRIP=strip AR=ar LDFLAGS=-pthread DESTDIR=/usr ########################## COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) LINK=$(CXX) WHAT=UNRAR UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o LIB_OBJ=filestr.o scantree.o dll.o qopen.o OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \ archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \ resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \ rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \ list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o largepage.o .cpp.o: $(COMPILE) -D$(WHAT) -c $< all: unrar install: install-unrar uninstall: uninstall-unrar clean: @rm -f *.bak *~ @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) @rm -f unrar libunrar.* # We removed 'clean' from dependencies, because it prevented parallel # 'make -Jn' builds. unrar: $(OBJECTS) $(UNRAR_OBJ) @rm -f unrar $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) $(STRIP) unrar sfx: WHAT=SFX_MODULE sfx: $(OBJECTS) @rm -f default.sfx $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS) $(STRIP) default.sfx lib: WHAT=RARDLL lib: CXXFLAGS+=$(LIBFLAGS) lib: $(OBJECTS) $(LIB_OBJ) @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) install-unrar: install -D unrar $(DESTDIR)/bin/unrar uninstall-unrar: rm -f $(DESTDIR)/bin/unrar install-lib: install libunrar.so $(DESTDIR)/lib install libunrar.a $(DESTDIR)/lib uninstall-lib: rm -f $(DESTDIR)/lib/libunrar.so unrar/dll.def000666 000000 000000 00000000363 10743433674 011627 0ustar00000000 000000 EXPORTS RAROpenArchive RAROpenArchiveEx RARCloseArchive RARReadHeader RARReadHeaderEx RARProcessFile RARProcessFileW RARSetCallback RARSetChangeVolProc RARSetProcessDataProc RARSetPassword RARGetDllVersion unrar/dll_nocrypt.def000666 000000 000000 00000000364 11115012456 013367 0ustar00000000 000000 EXPORTS RAROpenArchive RAROpenArchiveEx RARCloseArchive RARReadHeader RARReadHeaderEx RARProcessFile RARProcessFileW RARSetCallback RARSetChangeVolProc RARSetProcessDataProc ; RARSetPassword RARGetDllVersion unrar/dll.rc000666 000000 000000 00000001274 15026202325 011461 0ustar00000000 000000 #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION 7, 12, 100, 1637 PRODUCTVERSION 7, 12, 100, 1637 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" VALUE "FileVersion", "7.12.0\0" VALUE "ProductVersion", "7.12.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x0409, 0x04E4 } } unrar/UnRAR.vcxproj000666 000000 000000 00000033472 14623610241 012733 0ustar00000000 000000  Debug Win32 Debug x64 Release Win32 Release x64 {95CC809B-03FC-4EDB-BB20-FD07A698C05F} UnRAR Win32Proj 8.1 Application v140_xp MultiByte true Application v140_xp MultiByte Application v140_xp MultiByte false Application v140_xp MultiByte <_ProjectFileVersion>14.0.24720.0 build\unrar32\$(Configuration)\ build\unrar32\$(Configuration)\obj\ true false build\unrar64\$(Configuration)\ build\unrar64\$(Configuration)\obj\ true false build\unrar32\$(Configuration)\ build\unrar32\$(Configuration)\obj\ false false build\unrar64\$(Configuration)\ build\unrar64\$(Configuration)\obj\ false false /MP %(AdditionalOptions) Disabled UNRAR;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebug false Use rar.hpp Level3 ProgramDatabase StdCall 4007;4996;%(DisableSpecificWarnings) NoExtensions true Console MachineX86 X64 /MP %(AdditionalOptions) Disabled UNRAR;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebug false Use rar.hpp Level3 ProgramDatabase StdCall 4007;4996;%(DisableSpecificWarnings) NotSet true Console MachineX64 /MP %(AdditionalOptions) MaxSpeed true Neither true false UNRAR;%(PreprocessorDefinitions) false MultiThreaded Default true true NoExtensions Precise false Use rar.hpp Level3 ProgramDatabase StdCall 4007;4996;%(DisableSpecificWarnings) true Console true true MachineX86 X64 /MP %(AdditionalOptions) MinSpace true Neither true false UNRAR;%(PreprocessorDefinitions) false false MultiThreaded true true false Use rar.hpp Level3 ProgramDatabase StdCall 4007;4996;%(DisableSpecificWarnings) NotSet true Console true true MachineX64 Create Create Create Create unrar/UnRARDll.vcxproj000666 000000 000000 00000052740 14623610407 013372 0ustar00000000 000000  Debug Win32 Debug x64 release_nocrypt Win32 release_nocrypt x64 Release Win32 Release x64 UnRAR {E815C46C-36C4-499F-BBC2-E772C6B17971} UnRAR Win32Proj 8.1 DynamicLibrary v140_xp MultiByte true DynamicLibrary v140_xp MultiByte true DynamicLibrary v140_xp MultiByte DynamicLibrary v140_xp MultiByte false DynamicLibrary v140_xp MultiByte false DynamicLibrary v140_xp MultiByte <_ProjectFileVersion>14.0.24720.0 build\unrardll32\$(Configuration)\ build\unrardll32\$(Configuration)\obj\ true true build\unrardll64\$(Configuration)\ build\unrardll64\$(Configuration)\obj\ true true build\unrardll32\$(Configuration)\ build\unrardll32\$(Configuration)\obj\ false true build\unrardll64\$(Configuration)\ build\unrardll64\$(Configuration)\obj\ false true build\unrardll32\$(Configuration)\ build\unrardll32\$(Configuration)\obj\ false true build\unrardll64\$(Configuration)\ build\unrardll64\$(Configuration)\obj\ false true /MP %(AdditionalOptions) Disabled RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) false Sync EnableFastChecks MultiThreadedDebug Default false Use rar.hpp Level3 ProgramDatabase Cdecl 4007;4996;%(DisableSpecificWarnings) NoExtensions $(OutDir)unrar.dll dll.def true Console MachineX86 X64 /MP %(AdditionalOptions) Disabled RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) false Sync EnableFastChecks MultiThreadedDebug Default false Use rar.hpp Level3 ProgramDatabase Cdecl 4007;4996;%(DisableSpecificWarnings) NotSet $(OutDir)unrar.dll dll.def true Console MachineX64 /MP %(AdditionalOptions) MaxSpeed true Neither true false RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) false Sync MultiThreaded Default true true NoExtensions Precise false Use rar.hpp Level3 ProgramDatabase Cdecl 4007;4996;%(DisableSpecificWarnings) /SAFESEH %(AdditionalOptions) $(OutDir)unrar.dll dll.def true Console true true MachineX86 X64 /MP %(AdditionalOptions) MaxSpeed true Neither true false RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) false false Sync MultiThreaded Default true true false Use rar.hpp Level3 ProgramDatabase Cdecl 4007;4996;%(DisableSpecificWarnings) NotSet $(OutDir)unrar.dll dll.def true Console true true MachineX64 /MP %(AdditionalOptions) MaxSpeed true Neither true false RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) false Sync MultiThreaded Default true true NoExtensions Precise false Use rar.hpp Level3 ProgramDatabase Cdecl 4007;4996;%(DisableSpecificWarnings) /SAFESEH %(AdditionalOptions) $(OutDir)unrar.dll dll_nocrypt.def true Console true true MachineX86 X64 /MP %(AdditionalOptions) MaxSpeed true Neither true false RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) false false Sync MultiThreaded Default true true false Use rar.hpp Level3 ProgramDatabase StdCall 4007;4996;%(DisableSpecificWarnings) NotSet $(OutDir)unrar.dll dll_nocrypt.def true Console true true MachineX64 Create Create Create Create Create Create unrar/readme.txt000666 000000 000000 00000003350 12046263473 012365 0ustar00000000 000000 Portable UnRAR version 1. General This package includes freeware Unrar C++ source and makefile for several Unix compilers. Unrar source is subset of RAR and generated from RAR source automatically, by a small program removing blocks like '#ifndef UNRAR ... #endif'. Such method is not perfect and you may find some RAR related stuff unnecessary in Unrar, especially in header files. If you wish to port Unrar to a new platform, you may need to edit '#define LITTLE_ENDIAN' in os.hpp and data type definitions in rartypes.hpp. if computer architecture does not allow not aligned data access, you need to undefine ALLOW_NOT_ALIGNED_INT and define STRICT_ALIGNMENT_REQUIRED in os.h. UnRAR.vcproj and UnRARDll.vcproj are projects for Microsoft Visual C++. UnRARDll.vcproj lets to build unrar.dll library. 2. Unrar binaries If you compiled Unrar for OS, which is not present in "Downloads" and "RAR extras" on www.rarlab.com, we will appreciate if you send us the compiled executable to place it to our site. 3. Acknowledgements This source includes parts of code written by other authors. Please see acknow.txt file for details. 4. Legal stuff Unrar source may be used in any software to handle RAR archives without limitations free of charge, but cannot be used to re-create the RAR compression algorithm, which is proprietary. Distribution of modified Unrar source in separate form or as a part of other software is permitted, provided that it is clearly stated in the documentation and source comments that the code may not be used to develop a RAR (WinRAR) compatible archiver. More detailed license text is available in license.txt. unrar/license.txt000666 000000 000000 00000003670 11544270740 012554 0ustar00000000 000000 ****** ***** ****** UnRAR - free utility for RAR archives ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ****** ******* ****** License for use and distribution of ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ** ** ** ** ** ** FREE portable version ~~~~~~~~~~~~~~~~~~~~~ The source code of UnRAR utility is freeware. This means: 1. All copyrights to RAR and the utility UnRAR are exclusively owned by the author - Alexander Roshal. 2. UnRAR source code may be used in any software to handle RAR archives without limitations free of charge, but cannot be used to develop RAR (WinRAR) compatible archiver and to re-create RAR compression algorithm, which is proprietary. Distribution of modified UnRAR source code in separate form or as a part of other software is permitted, provided that full text of this paragraph, starting from "UnRAR source code" words, is included in license, or in documentation if license is not available, and in source code comments of resulting package. 3. The UnRAR utility may be freely distributed. It is allowed to distribute UnRAR inside of other software packages. 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS SOFTWARE. 5. Installing and using the UnRAR utility signifies acceptance of these terms and conditions of the license. 6. If you don't agree with terms of the license you must remove UnRAR files from your storage devices and cease to use the utility. Thank you for your interest in RAR and UnRAR. Alexander L. Roshal unrar/acknow.txt000666 000000 000000 00000005334 14127552350 012413 0ustar00000000 000000 ACKNOWLEDGMENTS * We used "Screaming Fast Galois Field Arithmetic Using Intel SIMD Instructions" paper by James S. Plank, Kevin M. Greenan and Ethan L. Miller to improve Reed-Solomon coding performance. Also we are grateful to Artem Drobanov and Bulat Ziganshin for samples and ideas allowed to make Reed-Solomon coding more efficient. * RAR4 text compression algorithm is based on Dmitry Shkarin PPMII and Dmitry Subbotin carryless rangecoder public domain source code. You can find it in ftp.elf.stuba.sk/pub/pc/pack. * RAR encryption includes parts of public domain code from Szymon Stefanek AES and Steve Reid SHA-1 implementations. * With exception of SFX modules, RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code is available here: https://sourceforge.net/projects/slicing-by-8/ Original Intel Slicing-by-8 code is licensed under BSD License available at http://www.opensource.org/licenses/bsd-license.html Copyright (c) 2004-2006 Intel Corporation. All Rights Reserved Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ), designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn and Christian Winnerlein. * Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed to significantly improve RAR compression and speed.