diff -ur unrar-6.1.7/UnRARDll.vcxproj unrar-6.2.7/UnRARDll.vcxproj --- unrar-6.1.7/UnRARDll.vcxproj 2022-10-19 22:05:12 +++ unrar-6.2.7/UnRARDll.vcxproj 2022-05-17 22:04:05 @@ -138,7 +138,7 @@ Sync EnableFastChecks MultiThreadedDebug - 4Bytes + Default false Use rar.hpp @@ -168,7 +168,7 @@ Sync EnableFastChecks MultiThreadedDebug - 4Bytes + Default false Use rar.hpp @@ -198,7 +198,7 @@ false Sync MultiThreaded - 4Bytes + Default true true NoExtensions @@ -239,7 +239,7 @@ false Sync MultiThreaded - 4Bytes + Default true true false @@ -274,7 +274,7 @@ false Sync MultiThreaded - 4Bytes + Default true true NoExtensions @@ -315,7 +315,7 @@ false Sync MultiThreaded - 4Bytes + Default true true false diff -ur unrar-6.1.7/archive.cpp unrar-6.2.7/archive.cpp --- unrar-6.1.7/archive.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/archive.cpp 2023-05-14 13:10:33 @@ -3,15 +3,15 @@ #include "arccmt.cpp" -Archive::Archive(RAROptions *InitCmd) +Archive::Archive(CommandData *InitCmd) { Cmd=NULL; // Just in case we'll have an exception in 'new' below. DummyCmd=(InitCmd==NULL); - Cmd=DummyCmd ? (new RAROptions):InitCmd; + Cmd=DummyCmd ? (new CommandData):InitCmd; OpenShared=Cmd->OpenShared; - Format=RARFMT15; + Format=RARFMT_NONE; Solid=false; Volume=false; MainComment=false; @@ -31,9 +31,9 @@ NextBlockPos=0; - memset(&MainHead,0,sizeof(MainHead)); - memset(&CryptHead,0,sizeof(CryptHead)); - memset(&EndArcHead,0,sizeof(EndArcHead)); + MainHead.Reset(); + CryptHead={}; + EndArcHead.Reset(); VolNumber=0; VolWrite=0; diff -ur unrar-6.1.7/archive.hpp unrar-6.2.7/archive.hpp --- unrar-6.1.7/archive.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/archive.hpp 2023-05-14 13:10:33 @@ -32,8 +32,8 @@ size_t ReadHeader14(); size_t ReadHeader15(); size_t ReadHeader50(); - void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); - void RequestArcPassword(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb); + void RequestArcPassword(RarCheckPassword *SelPwd); void UnexpEndArcMsg(); void BrokenHeaderMsg(); void UnkEncVerMsg(const wchar *Name,const wchar *Info); @@ -45,7 +45,7 @@ #endif ComprDataIO SubDataIO; bool DummyCmd; - RAROptions *Cmd; + CommandData *Cmd; RarTime LatestTime; @@ -58,7 +58,7 @@ bool ProhibitQOpen; #endif public: - Archive(RAROptions *InitCmd=NULL); + Archive(CommandData *InitCmd=NULL); ~Archive(); static RARFORMAT IsSignature(const byte *D,size_t Size); bool IsArchive(bool EnableBroken); @@ -83,7 +83,7 @@ const wchar *Name,uint Flags); bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode); HEADER_TYPE GetHeaderType() {return CurHeaderType;} - RAROptions* GetRAROptions() {return Cmd;} + CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} #if 0 void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); diff -ur unrar-6.1.7/arcread.cpp unrar-6.2.7/arcread.cpp --- unrar-6.1.7/arcread.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/arcread.cpp 2023-05-14 13:10:34 @@ -100,6 +100,9 @@ // 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); @@ -145,7 +148,7 @@ #ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. return 0; #else - RequestArcPassword(); + RequestArcPassword(NULL); byte Salt[SIZE_SALT30]; if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) @@ -251,7 +254,11 @@ 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); @@ -577,14 +584,20 @@ // 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(); + RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); byte PswCheck[SIZE_PSWCHECK]; HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); - // Verify password validity. - if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) + // Verify password validity. If header is damaged, we cannot rely on + // password check value, because it can be damaged too. + if (CryptHead.UsePswCheck && !BrokenHeader && + memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P. { @@ -850,8 +863,6 @@ hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); - hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; - char FileName[NM*4]; size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); Raw.GetB((byte *)FileName,ReadNameSize); @@ -875,26 +886,7 @@ if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; -#if 0 - // For RAR5 format we read the user specified recovery percent here. - // It would be useful to do it for shell extension too, so we display - // the correct recovery record size in archive properties. But then - // we would need to include the entire recovery record processing - // code to shell extension, which is not done now. - if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) - { - // 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[0],hd->SubData.Size()); - RecoveryPercent=(int)RawPercent.GetV(); - RSBlockHeader Header; - GetRRInfo(this,&Header); - RecoverySize=Header.RecSectionSize*Header.RecCount; - } -#endif - if (BadCRC) // Add the file name to broken header message displayed above. uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } @@ -916,7 +908,7 @@ #if !defined(RAR_NOCRYPT) -void Archive::RequestArcPassword() +void Archive::RequestArcPassword(RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { @@ -946,7 +938,7 @@ ErrHandler.Exit(RARX_USERBREAK); } #else - if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd)) { Close(); uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. @@ -959,7 +951,7 @@ #endif -void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) +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; @@ -982,22 +974,57 @@ if (bb->HeaderType==HEAD_MAIN) { MainHeader *hd=(MainHeader *)bb; - if (FieldType==MHEXTRA_LOCATOR) + switch(FieldType) { - 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; - } + 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<0x10000) // Prevent excessive allocation. + { + std::vector NameU((size_t)NameSize); // UTF-8 name. + Raw->GetB(&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) + { + NameU.push_back(0); + std::vector NameW(NameU.size()*4); + UtfToWide(&NameU[0],&NameW[0],NameW.size()); + hd->OrigName.assign(&NameW[0]); + } + } + } + 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; } } @@ -1453,7 +1480,9 @@ { if (SubHead.UnpSize>0x1000000) { - // So huge allocation must never happen in valid archives. + // 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; } diff -ur unrar-6.1.7/array.hpp unrar-6.2.7/array.hpp --- unrar-6.1.7/array.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/array.hpp 2023-05-14 13:10:34 @@ -10,7 +10,6 @@ size_t BufSize; size_t AllocSize; size_t MaxSize; - bool Secure; // Clean memory if true. public: Array(); Array(size_t Size); @@ -24,14 +23,13 @@ void Alloc(size_t Items); void Reset(); void SoftReset(); - void operator = (Array &Src); + Array& operator = (const Array &Src); void Push(T Item); void Append(T *Item,size_t Count); T* Addr(size_t Item) {return Buffer+Item;} void SetMaxSize(size_t Size) {MaxSize=Size;} T* Begin() {return Buffer;} T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} - void SetSecure() {Secure=true;} }; @@ -41,7 +39,6 @@ BufSize=0; AllocSize=0; MaxSize=0; - Secure=false; } @@ -71,11 +68,7 @@ template Array::~Array() { if (Buffer!=NULL) - { - if (Secure) - cleandata(Buffer,AllocSize*sizeof(T)); free(Buffer); - } } @@ -111,25 +104,9 @@ size_t Suggested=AllocSize+AllocSize/4+32; size_t NewSize=Max(BufSize,Suggested); - T *NewBuffer; - if (Secure) - { - NewBuffer=(T *)malloc(NewSize*sizeof(T)); - if (NewBuffer==NULL) - ErrHandler.MemoryError(); - if (Buffer!=NULL) - { - memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); - cleandata(Buffer,AllocSize*sizeof(T)); - free(Buffer); - } - } - else - { - NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); - if (NewBuffer==NULL) - ErrHandler.MemoryError(); - } + T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); Buffer=NewBuffer; AllocSize=NewSize; } @@ -165,12 +142,13 @@ } -template void Array::operator =(Array &Src) +template Array& Array::operator =(const Array &Src) { Reset(); Alloc(Src.BufSize); if (Src.BufSize!=0) memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); + return *this; } diff -ur unrar-6.1.7/blake2s.hpp unrar-6.2.7/blake2s.hpp --- unrar-6.1.7/blake2s.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/blake2s.hpp 2023-05-14 13:10:34 @@ -20,10 +20,15 @@ // 'new' operator. struct blake2s_state { - enum { BLAKE_ALIGNMENT = 64 }; + // 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]; - enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; + // 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]; diff -ur unrar-6.1.7/cmddata.cpp unrar-6.2.7/cmddata.cpp --- unrar-6.1.7/cmddata.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/cmddata.cpp 2023-05-14 13:10:34 @@ -26,9 +26,10 @@ FileArgs.Reset(); ExclArgs.Reset(); InclArgs.Reset(); - StoreArgs.Reset(); ArcNames.Reset(); - NextVolSizes.Reset(); + StoreArgs.Reset(); + Password.Clean(); + NextVolSizes.clear(); } @@ -314,6 +315,21 @@ 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 'N': // Reserved for archive name. break; case 'O': @@ -415,7 +431,7 @@ else if (!Password.IsSet()) { - uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); eprintf(L"\n"); } break; @@ -685,7 +701,7 @@ case 'P': if (Switch[1]==0) { - uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); eprintf(L"\n"); } else diff -ur unrar-6.1.7/cmddata.hpp unrar-6.2.7/cmddata.hpp --- unrar-6.1.7/cmddata.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/cmddata.hpp 2023-05-14 13:10:34 @@ -2,7 +2,7 @@ #define _RAR_CMDDATA_ -#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" +#define DefaultStoreList L"7z;ace;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}; @@ -65,6 +65,10 @@ StringList InclArgs; StringList ArcNames; StringList StoreArgs; + + SecPassword Password; + + std::vector NextVolSizes; }; #endif diff -ur unrar-6.1.7/cmdfilter.cpp unrar-6.2.7/cmdfilter.cpp --- unrar-6.1.7/cmdfilter.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/cmdfilter.cpp 2023-05-14 13:10:34 @@ -289,8 +289,8 @@ return 0; if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) return 0; - if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || - FileHead.Dir && !InclDir)) + if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 && + (!FileHead.Dir || !InclDir)) return 0; if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; diff -ur unrar-6.1.7/cmdmix.cpp unrar-6.2.7/cmdmix.cpp --- unrar-6.1.7/cmdmix.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/cmdmix.cpp 2023-05-14 13:10:34 @@ -92,6 +92,13 @@ if (Found) continue; #endif +#ifdef _UNIX + if (CmpMSGID(Help[I],MRARTitle2)) + { + mprintf(St(MFwrSlTitle2)); + continue; + } +#endif #if !defined(_UNIX) && !defined(_WIN_ALL) if (CmpMSGID(Help[I],MCHelpSwOW)) continue; diff -ur unrar-6.1.7/compress.hpp unrar-6.2.7/compress.hpp --- unrar-6.1.7/compress.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/compress.hpp 2023-05-14 13:10:34 @@ -17,6 +17,7 @@ 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} */ diff -ur unrar-6.1.7/crc.cpp unrar-6.2.7/crc.cpp --- unrar-6.1.7/crc.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/crc.cpp 2023-05-14 13:10:34 @@ -110,3 +110,164 @@ #endif +#if 0 +static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64. + +void InitCRC64(uint64 *CRCTab) +{ + const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42; + for (uint I=0;I<256;I++) + { + uint64 C=I; + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^poly: (C>>1); + CRCTab[I]=C; + } +} + + +static void InitTables64() +{ + InitCRC64(crc64_tables[0]); + + for (uint I=0;I<256;I++) // Build additional lookup tables. + { + uint64 C=crc64_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc64_tables[0][(byte)C]^(C>>8); + crc64_tables[J][I]=C; + } + } +} + + +// We cannot place the intialization to CRC64(), because we use this function +// in multithreaded mode and it conflicts with multithreading. +struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64; + +uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7)!=0;Size--,Data++) + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (byte *DataEnd=Data+Size/8*8; Data> 8 ) ] ^ + crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^ + crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^ + crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^ + crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^ + crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^ + crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ; + } + + for (Size%=8;Size>0;Size--,Data++) // Process left data. + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + +#if 0 +static void TestCRC(); +struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC; + +void TestCRC() +{ + 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() || Method==CRYPT_NONE) + if (Method==CRYPT_NONE || !Password->IsSet()) 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. switch(Method) { diff -ur unrar-6.1.7/crypt.hpp unrar-6.2.7/crypt.hpp --- unrar-6.1.7/crypt.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/crypt.hpp 2023-05-14 13:10:34 @@ -30,6 +30,18 @@ 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 @@ -39,6 +51,17 @@ 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)); + } }; @@ -77,7 +100,6 @@ ushort Key15[4]; public: CryptData(); - ~CryptData(); bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt, byte *HashKey,byte *PswCheck); @@ -86,6 +108,54 @@ 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); diff -ur unrar-6.1.7/crypt3.cpp unrar-6.2.7/crypt3.cpp --- unrar-6.1.7/crypt3.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/crypt3.cpp 2023-05-14 13:10:34 @@ -18,8 +18,9 @@ if (!Cached) { byte RawPsw[2*MAXPASSWORD+SIZE_SALT30]; - WideToRaw(PwdW,RawPsw,ASIZE(RawPsw)); - size_t RawLength=2*wcslen(PwdW); + size_t PswLength=wcslen(PwdW); + size_t RawLength=2*PswLength; + WideToRaw(PwdW,PswLength,RawPsw,RawLength); if (Salt!=NULL) { memcpy(RawPsw+RawLength,Salt,SIZE_SALT30); diff -ur unrar-6.1.7/crypt5.cpp unrar-6.2.7/crypt5.cpp --- unrar-6.1.7/crypt5.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/crypt5.cpp 2023-05-14 13:10:34 @@ -21,7 +21,7 @@ sha256_context ICtx; if (ICtxOpt!=NULL && *SetIOpt) - ICtx=*ICtxOpt; // Use already calculated first block context. + ICtx=*ICtxOpt; // Use already calculated the first block context. else { // This calculation is the same for all iterations with same password. @@ -90,10 +90,10 @@ byte SaltData[MaxSalt+4]; memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); - SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. - SaltData[SaltLength + 1] = 0; - SaltData[SaltLength + 2] = 0; - SaltData[SaltLength + 3] = 1; + 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]; @@ -140,7 +140,7 @@ for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password && + if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt && memcmp(Item->Salt,Salt,SIZE_SALT50)==0) { memcpy(Key,Item->Key,sizeof(Key)); diff -ur unrar-6.1.7/dll.rc unrar-6.2.7/dll.rc --- unrar-6.1.7/dll.rc 2022-10-19 22:05:12 +++ unrar-6.2.7/dll.rc 2023-05-14 13:04:53 @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 12, 100, 489 -PRODUCTVERSION 6, 12, 100, 489 +FILEVERSION 6, 22, 1, 865 +PRODUCTVERSION 6, 22, 1, 865 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "6.12.0\0" - VALUE "ProductVersion", "6.12.0\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" + VALUE "FileVersion", "6.22.1\0" + VALUE "ProductVersion", "6.22.1\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff -ur unrar-6.1.7/errhnd.cpp unrar-6.2.7/errhnd.cpp --- unrar-6.1.7/errhnd.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/errhnd.cpp 2023-05-14 13:10:34 @@ -169,10 +169,13 @@ void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) { - Wait(); // Keep GUI responsive if many files cannot be opened when archiving. uiMsg(UIERROR_FILEOPEN,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_OPEN); + + // Keep GUI responsive if many files cannot be opened when archiving. + // Call after SysErrMsg to avoid modifying the error code and SysErrMsg text. + Wait(); } diff -ur unrar-6.1.7/extinfo.cpp unrar-6.2.7/extinfo.cpp --- unrar-6.1.7/extinfo.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/extinfo.cpp 2023-05-14 13:10:34 @@ -112,6 +112,68 @@ } +// 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 wchar *SrcName,const wchar *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 + wchar Path[NM]; + if (wcslen(SrcName)>=ASIZE(Path)) + return false; // It should not be that long, skip. + wcsncpyz(Path,SrcName,ASIZE(Path)); + + size_t SkipLength=wcslen(SkipPart); + + if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0) + SkipLength=0; // Parameter validation, not really needed now. + + // Do not check parts already checked in previous path to improve performance. + for (uint I=0;Path[I]!=0 && ISkipLength) + SkipLength=I; + + wchar *Name=Path; + if (SkipLength>0) + { + // Avoid converting symlinks in destination path part specified by user. + Name+=SkipLength; + while (IsPathDiv(*Name)) + Name++; + } + + for (wchar *s=Path+wcslen(Path)-1;s>Name;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) +#ifdef _WIN_ALL + if (!DelDir(Path)) +#else + if (!DelFile(Path)) +#endif + return false; // Couldn't delete the symlink to replace it with directory. + } + LastChecked=SrcName; +//#endif + return true; +} + + bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) { // Catch root dir based /path/file paths also as stuff like \\?\. @@ -131,10 +193,14 @@ UpLevels++; TargetName++; } - // If link target includes "..", it must not have another links - // in the path, because they can bypass our safety check. For example, + // If link target includes "..", it must not have another links in its + // source path, because they can bypass our safety check. For example, // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next - // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. + // 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; @@ -160,15 +226,26 @@ } -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *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=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL; +#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); + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink); if (Arc.Format==RARFMT50) return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); -#elif defined _WIN_ALL +#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) diff -ur unrar-6.1.7/extinfo.hpp unrar-6.2.7/extinfo.hpp --- unrar-6.1.7/extinfo.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/extinfo.hpp 2023-05-14 13:10:34 @@ -1,8 +1,9 @@ #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ +bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked); bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink); #ifdef _UNIX void SetUnixOwner(Archive &Arc,const wchar *FileName); #endif diff -ur unrar-6.1.7/extract.cpp unrar-6.2.7/extract.cpp --- unrar-6.1.7/extract.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/extract.cpp 2023-05-14 13:10:34 @@ -5,10 +5,30 @@ CmdExtract::Cmd=Cmd; *ArcName=0; - *DestFileName=0; + ArcAnalyzed=false; + Analyze=new AnalyzeData; + memset(Analyze,0,sizeof(*Analyze)); + TotalFileCount=0; + + // Common for all archives involved. Set here instead of DoExtract() + // to use in unrar.dll too. Allows to avoid LinksToDirs() calls + // and save CPU time in no symlinks including ".." in target were extracted. +#if defined(_WIN_ALL) + // We can't expand symlink path components in another symlink target + // in Windows. We can't create symlinks in Android now. Even though we do not + // really need LinksToDirs() calls in these systems, we still call it + // for extra safety, but only if symlink with ".." in target was extracted. + ConvertSymlinkPaths=false; +#else + // 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%. + ConvertSymlinkPaths=true; +#endif + Unp=new Unpack(&DataIO); #ifdef RAR_SMP Unp->SetThreads(Cmd->Threads); @@ -18,10 +38,29 @@ CmdExtract::~CmdExtract() { + FreeAnalyzeData(); delete Unp; + delete Analyze; } +void CmdExtract::FreeAnalyzeData() +{ + for (size_t I=0;ICommand[0]); - FindData FD; - while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) - if (FindFile::FastFind(ArcName,&FD)) - DataIO.TotalArcSize+=FD.Size; + if (*Cmd->UseStdin==0) + { + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) + DataIO.TotalArcSize+=FD.Size; + } Cmd->ArcNames.Rewind(); while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) @@ -97,7 +139,11 @@ AllMatchesExact=true; AnySolidDataUnpackedWell=false; + ArcAnalyzed=false; + StartTime.SetCurrentTime(); + + LastCheckedSymlink.clear(); } @@ -168,20 +214,33 @@ } #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==0) + { + 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 (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering)) + if (*Analyze->StartName!=0) { + wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName)); + *Analyze->StartName=0; + 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. @@ -216,7 +275,13 @@ else uiStartArchiveExtract(!Cmd->Test,ArcName); - Arc.ViewComment(); +#ifndef SFX_MODULE + if (Analyze->StartPos!=0) + { + Arc.Seek(Analyze->StartPos,SEEK_SET); + Analyze->StartPos=0; + } +#endif while (1) @@ -272,8 +337,15 @@ return false; HEADER_TYPE HeaderType=Arc.GetHeaderType(); - if (HeaderType!=HEAD_FILE) + 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==0 || wcscmp(Analyze->EndName,Arc.FileName)==0)) + return false; + } + else + { #ifndef SFX_MODULE if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) SetExtraInfo20(Cmd,Arc,DestFileName); @@ -315,6 +387,9 @@ if (Arc.FileHead.UnpSize<0) Arc.FileHead.UnpSize=0; + // 2022.03.20: We might remove this check in the future. + // It duplicates Analyze->EndPos and Analyze->EndName in all cases except + // volumes on removable media. if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) return false; @@ -413,13 +488,39 @@ 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. + wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); + AddEndSlash(DestFileName,ASIZE(DestFileName)); + wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); + MkTemp(DestFileName,ASIZE(DestFileName)); + MatchedRef->TmpName=wcsdup(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 || (SkipSolid=Arc.Solid)!=0) + if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0) { // First common call of uiStartFileExtract. It is done before overwrite // prompts, so if SkipSolid state is changed below, we'll need to make @@ -427,7 +528,8 @@ if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) return false; - ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); + if (!RefTarget) + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); // DestFileName can be set empty in case of excessive -ap switch. ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; @@ -464,9 +566,13 @@ return !Arc.Solid; // Can try extracting next file only in non-solid archive. } - while (true) // Repeat the password prompt for wrong and empty passwords. + if (Arc.FileHead.Encrypted) { - 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 @@ -476,77 +582,83 @@ return false; } #else - if (!ExtrGetPassword(Arc,ArcFileName)) + if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL)) { PasswordCancelled=true; return false; } #endif - } - // Set a password before creating the file, so we can skip creating - // in case of wrong password. - SecPassword FilePassword=Cmd->Password; -#if defined(_WIN_ALL) && !defined(SFX_MODULE) - ConvertDosPassword(Arc,FilePassword); -#endif + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; + #if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); + #endif - byte PswCheck[SIZE_PSWCHECK]; - DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, - Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, - Arc.FileHead.InitV,Arc.FileHead.Lg2Count, - Arc.FileHead.HashKey,PswCheck); + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); - // If header is damaged, we cannot rely on password check value, - // because it can be damaged too. - if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && - memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && - !Arc.BrokenHeader) - { - if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) { - // 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(); + 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 + // 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; } -#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; } - break; } + else + DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); #ifdef RARDLL if (*Cmd->DllDestName!=0) wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); #endif + if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && + ConvertSymlinkPaths) + ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); + File CurFile; bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; - if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) + if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) { if (ExtrFile && Command!='P' && !Cmd->Test) { - // Overwrite prompt for symbolic and hard links. + // 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) && !UserReject) FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); @@ -667,25 +779,50 @@ if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) { wchar RedirName[NM]; - ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(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,ASIZE(RedirName)); + ConvertPath(RedirName,RedirName,ASIZE(RedirName)); + wchar NameExisting[NM]; ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); else - LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize); } else if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) { if (FileCreateMode) - LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); + { + 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,DestFileName); + uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName); LinkSuccess=false; } @@ -709,6 +846,7 @@ Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); 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 @@ -866,22 +1004,64 @@ } -bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) { - SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. - File Existing; - if (!Existing.WOpen(NameExisting)) + if (!Existing.Open(NameExisting)) { - uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); - uiMsg(UIERROR_FILECOPYHINT,ArcName); + 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; + Cmd->DllError=ERAR_EREFERENCE; #endif - return false; + return false; + } } - Array Buffer(0x100000); + Array Buffer(0x100000); int64 CopySize=0; while (true) @@ -890,6 +1070,10 @@ int ReadSize=Existing.Read(&Buffer[0],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[0],ReadSize); CopySize+=ReadSize; } @@ -900,6 +1084,16 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) { + 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. + wcsncpyz(DestName,ArcFileName,DestSize); + return; + } + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); if (*Cmd->ExtrPath!=0) @@ -1033,11 +1227,11 @@ #ifndef RARDLL -bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { - if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !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); @@ -1055,7 +1249,7 @@ case -1: ErrHandler.Exit(RARX_USERBREAK); case 2: - if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)) return false; break; case 3: @@ -1131,6 +1325,8 @@ DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); if (!DirExist) { + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); } @@ -1212,6 +1408,8 @@ MakeNameUsable(DestFileName,true); + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { @@ -1258,31 +1456,59 @@ #ifndef SFX_MODULE -// To speed up solid volumes extraction, try to find a non-first start volume, -// which still allows to unpack all files. It is possible for independent -// solid volumes with solid statistics reset in the beginning. -bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) +// 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 wchar *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 false; // No need to check further for * and *.* masks. + return; // No need to check further for * and *.* masks. - wchar StartName[NM]; - *StartName=0; - // Start search from first volume if all volumes preceding current are available. wchar NextName[NM]; - GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName)); + if (Volume) + GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName)); + else + wcsncpyz(NextName,ArcName,ASIZE(NextName)); + + bool MatchFound=false; + bool PrevMatched=false; + bool OpenNext=false; - bool Matched=false; - while (!Matched) + 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) || !Arc.Volume) + 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=0; + Analyze->EndPos=0; + } break; + } - bool OpenNext=false; + OpenNext=false; while (Arc.ReadHeader()>0) { Wait(); @@ -1295,17 +1521,88 @@ } 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 (!Arc.FileHead.Solid) // Can start extraction from here. - wcsncpyz(StartName,NextName,ASIZE(StartName)); + if (!MatchFound && !Arc.FileHead.Solid) // 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) + wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); + // 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)!=0) { - Matched=true; // First matched file found, must stop further scan. - break; + 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;IEndName,NextName,ASIZE(Analyze->EndName)); + Analyze->EndPos=Arc.CurBlockPos; + } + PrevMatched=false; + } } + + FirstFile=false; if (Arc.FileHead.SplitAfter) { OpenNext=true; // Allow open next volume. @@ -1316,16 +1613,25 @@ } Arc.Close(); - if (!OpenNext) - break; + if (Volume && OpenNext) + { + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FirstVolume=false; - NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + // Needed for multivolume archives. Added in case some 'break' + // will quit early from loop above, so we do not set it in the loop. + // Now it can happen for hypothetical archive without file records + // and with HEAD_ENDARC record. + FirstFile=false; + } + else + break; } - bool NewStartFound=wcscmp(VolName,StartName)!=0; - if (NewStartFound) // Found a new volume to start extraction. - wcsncpyz(ArcName,StartName,ASIZE(ArcName)); - - return NewStartFound; + + // If file references are present, we can't reliably skip in semi-solid + // archives, because reference source can be present in skipped data. + if (RefList.Size()!=0) + memset(Analyze,0,sizeof(*Analyze)); } #endif diff -ur unrar-6.1.7/extract.hpp unrar-6.2.7/extract.hpp --- unrar-6.1.7/extract.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/extract.hpp 2023-05-14 13:10:34 @@ -6,13 +6,32 @@ class CmdExtract { private: + struct ExtractRef + { + wchar *RefName; + wchar *TmpName; + uint64 RefCount; + }; + Array RefList; + + struct AnalyzeData + { + wchar StartName[NM]; + uint64 StartPos; + wchar EndName[NM]; + uint64 EndPos; + } *Analyze; + + bool ArcAnalyzed; + + void FreeAnalyzeData(); EXTRACT_ARC_CODE ExtractArchive(); - bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize); void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); #ifdef RARDLL bool ExtrDllGetPassword(); #else - bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); @@ -21,7 +40,7 @@ bool ExtrCreateFile(Archive &Arc,File &CurFile); bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); #ifndef SFX_MODULE - bool DetectStartVolume(const wchar *VolName,bool NewNumbering); + void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering); void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); #endif @@ -52,6 +71,15 @@ bool PrevProcessed; // If previous file was successfully extracted or tested. wchar DestFileName[NM]; bool PasswordCancelled; + + // 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 diff -ur unrar-6.1.7/filcreat.cpp unrar-6.2.7/filcreat.cpp --- unrar-6.1.7/filcreat.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/filcreat.cpp 2023-05-14 13:10:34 @@ -3,7 +3,7 @@ // 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(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) diff -ur unrar-6.1.7/filcreat.hpp unrar-6.2.7/filcreat.hpp --- unrar-6.1.7/filcreat.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/filcreat.hpp 2023-05-14 13:10:34 @@ -1,7 +1,7 @@ #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ -bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize=INT64NDF, RarTime *FileTime=NULL,bool WriteOnly=false); diff -ur unrar-6.1.7/file.cpp unrar-6.2.7/file.cpp --- unrar-6.1.7/file.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/file.cpp 2023-05-14 13:10:34 @@ -522,29 +522,35 @@ { if (hFile==FILE_BAD_HANDLE) return true; - if (!IsSeekable()) + if (!IsSeekable()) // To extract archives from stdin with -si. { - if (Method==SEEK_CUR) + // 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) { - Offset+=CurFilePos; - Method=SEEK_SET; - } - if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward. - { - uint64 SkipSize=Offset-CurFilePos; - while (SkipSize>0) + uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos; + while (SkipSize>0) // Reading to emulate seek forward. { - byte Buf[4096]; int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); if (ReadSize<=0) return false; SkipSize-=ReadSize; + CurFilePos+=ReadSize; } - CurFilePos=Offset; 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 or end of file seek on unseekable file. + return false; // Backward seek on unseekable file. } if (Offset<0 && Method!=SEEK_SET) { @@ -732,17 +738,40 @@ } -void File::GetOpenFileTime(RarTime *ft) +#ifdef _UNIX +void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_ALL - FILETIME FileTime; - GetFileTime(hFile,NULL,NULL,&FileTime); - ft->SetWinFT(&FileTime); +#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 -#if defined(_UNIX) || defined(_EMX) +#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); - ft->SetUnix(st.st_mtime); + StatToRarTime(st,ftm,ftc,fta); #endif } diff -ur unrar-6.1.7/file.hpp unrar-6.2.7/file.hpp --- unrar-6.1.7/file.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/file.hpp 2023-05-14 13:10:34 @@ -14,8 +14,6 @@ #define FILE_BAD_HANDLE NULL #endif -class RAROptions; - enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; @@ -88,6 +86,9 @@ wchar FileName[NM]; FILE_ERRORTYPE ErrorType; + + byte *SeekBuf; // To read instead of seek for stdin files. + static const size_t SeekBufSize=0x10000; public: File(); virtual ~File(); @@ -118,7 +119,10 @@ void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); - void GetOpenFileTime(RarTime *ft); +#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. int64 FileLength(); void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} diff -ur unrar-6.1.7/filefn.cpp unrar-6.2.7/filefn.cpp --- unrar-6.1.7/filefn.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/filefn.cpp 2023-05-14 13:10:34 @@ -320,7 +320,6 @@ } -#if 0 wchar *MkTemp(wchar *Name,size_t MaxSize) { size_t Length=wcslen(Name); @@ -354,7 +353,6 @@ } return Name; } -#endif #if !defined(SFX_MODULE) @@ -399,7 +397,7 @@ 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, + // to keep it as is. It looks better for WinRAR. uiExtractProgress(TotalRead,FileLength,0,0); } else diff -ur unrar-6.1.7/filefn.hpp unrar-6.2.7/filefn.hpp --- unrar-6.1.7/filefn.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/filefn.hpp 2023-05-14 13:10:34 @@ -27,9 +27,7 @@ void PrepareToDelete(const wchar *Name); uint GetFileAttr(const wchar *Name); bool SetFileAttr(const wchar *Name,uint Attr); -#if 0 wchar* MkTemp(wchar *Name,size_t MaxSize); -#endif enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; diff -ur unrar-6.1.7/find.cpp unrar-6.2.7/find.cpp --- unrar-6.1.7/find.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/find.cpp 2023-05-14 13:10:34 @@ -117,7 +117,7 @@ if (hFind==INVALID_HANDLE_VALUE) return false; FindClose(hFind); -#else +#elif defined(_UNIX) char FindMaskA[NM]; WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); @@ -143,21 +143,7 @@ fd->FileAttr=st.st_mode; fd->Size=st.st_size; -#ifdef UNIX_TIME_NS -#if defined(_APPLE) - fd->mtime.SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); - fd->atime.SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); - fd->ctime.SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); -#else - fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); - fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); - fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); -#endif -#else - fd->mtime.SetUnix(st.st_mtime); - fd->atime.SetUnix(st.st_atime); - fd->ctime.SetUnix(st.st_ctime); -#endif + File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime); wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); #endif diff -ur unrar-6.1.7/getbits.cpp unrar-6.2.7/getbits.cpp --- unrar-6.1.7/getbits.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/getbits.cpp 2023-05-14 13:10:34 @@ -5,7 +5,7 @@ ExternalBuffer=false; if (AllocBuffer) { - // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // getbits*() attempt to read data from InAddr, ... InAddr+3 positions. // So let's allocate 3 additional bytes for situation, when we need to // read only 1 byte from the last position of buffer and avoid a crash // from access to next 3 bytes, which contents we do not need. diff -ur unrar-6.1.7/getbits.hpp unrar-6.2.7/getbits.hpp --- unrar-6.1.7/getbits.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/getbits.hpp 2023-05-14 13:10:34 @@ -28,26 +28,38 @@ 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=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); + 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() { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + uint32 BitField=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); +#else uint BitField=(uint)InBuf[InAddr] << 24; BitField|=(uint)InBuf[InAddr+1] << 16; BitField|=(uint)InBuf[InAddr+2] << 8; BitField|=(uint)InBuf[InAddr+3]; +#endif BitField <<= InBit; BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); return BitField & 0xffffffff; diff -ur unrar-6.1.7/hardlinks.cpp unrar-6.2.7/hardlinks.cpp --- unrar-6.1.7/hardlinks.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/hardlinks.cpp 2023-05-14 13:10:34 @@ -1,7 +1,5 @@ bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) { - SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. - if (!FileExist(NameExisting)) { uiMsg(UIERROR_HLINKCREATE,NameNew); diff -ur unrar-6.1.7/hash.cpp unrar-6.2.7/hash.cpp --- unrar-6.1.7/hash.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/hash.cpp 2023-05-14 13:10:34 @@ -26,7 +26,7 @@ } -bool HashValue::operator == (const HashValue &cmp) +bool HashValue::operator == (const HashValue &cmp) const { if (Type==HASH_NONE || cmp.Type==HASH_NONE) return true; diff -ur unrar-6.1.7/hash.hpp unrar-6.2.7/hash.hpp --- unrar-6.1.7/hash.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/hash.hpp 2023-05-14 13:10:34 @@ -6,8 +6,14 @@ struct HashValue { void Init(HASH_TYPE Type); - bool operator == (const HashValue &cmp); - bool operator != (const HashValue &cmp) {return !(*this==cmp);} + + // 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. + bool operator != (const HashValue &cmp) const {return !(*this==cmp);} HASH_TYPE Type; union diff -ur unrar-6.1.7/headers.cpp unrar-6.2.7/headers.cpp --- unrar-6.1.7/headers.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/headers.cpp 2023-05-14 13:10:34 @@ -49,13 +49,5 @@ void MainHeader::Reset() { - HighPosAV=0; - PosAV=0; - CommentInHeader=false; - PackComment=false; - Locator=false; - QOpenOffset=0; - QOpenMaxSize=0; - RROffset=0; - RRMaxSize=0; + *this={}; } diff -ur unrar-6.1.7/headers.hpp unrar-6.2.7/headers.hpp --- unrar-6.1.7/headers.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/headers.hpp 2023-05-14 13:10:34 @@ -6,7 +6,7 @@ #define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. #define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. #define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. -#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size. #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 @@ -162,12 +162,16 @@ ushort HighPosAV; uint PosAV; bool CommentInHeader; - bool PackComment; // For RAR 1.4 archive format only. + 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. + 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(); }; @@ -230,7 +234,7 @@ bool LargeFile; // 'true' for HEAD_SERVICE block, which is a child of preceding file block. - // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. + // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives. bool SubBlock; HOST_SYSTEM_TYPE HSType; diff -ur unrar-6.1.7/headers5.hpp unrar-6.2.7/headers5.hpp --- unrar-6.1.7/headers5.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/headers5.hpp 2023-05-14 13:10:34 @@ -59,10 +59,17 @@ // 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. diff -ur unrar-6.1.7/isnt.cpp unrar-6.2.7/isnt.cpp --- unrar-6.1.7/isnt.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/isnt.cpp 2023-05-14 13:10:34 @@ -40,7 +40,7 @@ IWbemServices *pSvc = NULL; - hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc); + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc); if (FAILED(hres)) { diff -ur unrar-6.1.7/list.cpp unrar-6.2.7/list.cpp --- unrar-6.1.7/list.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/list.cpp 2023-05-14 13:10:34 @@ -36,6 +36,7 @@ { Arc.ViewComment(); mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: ",St(MListDetails)); uint SetCount=0; const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); @@ -61,6 +62,16 @@ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); if (Arc.Encrypted) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", 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"); } diff -ur unrar-6.1.7/loclang.hpp unrar-6.2.7/loclang.hpp --- unrar-6.1.7/loclang.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/loclang.hpp 2023-05-14 13:10:34 @@ -28,6 +28,7 @@ #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" @@ -58,6 +59,7 @@ #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" @@ -394,3 +396,6 @@ #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" diff -ur unrar-6.1.7/makefile unrar-6.2.7/makefile --- unrar-6.1.7/makefile 2022-10-19 22:05:12 +++ unrar-6.2.7/makefile 2023-02-06 13:31:30 @@ -126,7 +126,7 @@ 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 + list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o .cpp.o: $(COMPILE) -D$(WHAT) -c $< @@ -142,20 +142,23 @@ @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) @rm -f unrar libunrar.* -unrar: clean $(OBJECTS) $(UNRAR_OBJ) +# 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: clean $(OBJECTS) +sfx: $(OBJECTS) @rm -f default.sfx $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS) $(STRIP) default.sfx lib: WHAT=RARDLL lib: CXXFLAGS+=$(LIBFLAGS) -lib: clean $(OBJECTS) $(LIB_OBJ) +lib: $(OBJECTS) $(LIB_OBJ) @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) diff -ur unrar-6.1.7/model.cpp unrar-6.2.7/model.cpp --- unrar-6.1.7/model.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/model.cpp 2023-05-14 13:10:34 @@ -532,13 +532,15 @@ Model->Coder.SubRange.LowCount=HiCnt; Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; i=NumStats-Model->NumMasked; - pps--; + + // 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 { - pps++; if (pps>=ps+ASIZE(ps)) // Extra safety check. return false; Model->CharMask[(*pps)->Symbol]=Model->EscCount; + pps++; } while ( --i ); psee2c->Summ += Model->Coder.SubRange.scale; Model->NumMasked = NumStats; diff -ur unrar-6.1.7/options.cpp unrar-6.2.7/options.cpp --- unrar-6.1.7/options.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/options.cpp 2023-05-14 13:10:34 @@ -6,14 +6,6 @@ } -RAROptions::~RAROptions() -{ - // It is important for security reasons, so we do not have the unnecessary - // password data left in memory. - memset(this,0,sizeof(RAROptions)); -} - - void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); diff -ur unrar-6.1.7/options.hpp unrar-6.2.7/options.hpp --- unrar-6.1.7/options.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/options.hpp 2023-05-14 13:10:34 @@ -45,6 +45,12 @@ OVERWRITE_FORCE_ASK }; +enum ARC_METADATA +{ + ARCMETA_NONE=0, + ARCMETA_SAVE, // -ams + ARCMETA_RESTORE // -amr +}; enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS }; @@ -84,11 +90,12 @@ #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(); - ~RAROptions(); void Init(); uint ExclFileAttr; @@ -118,7 +125,6 @@ wchar ArcPath[NM]; // For -ap. wchar ExclArcPath[NM]; // For -ep4 switch. - SecPassword Password; bool EncryptHeaders; bool SkipEncrypted; @@ -132,6 +138,7 @@ HASH_TYPE HashType; int Recovery; int RecVolNumber; + ARC_METADATA ArcMetadata; bool DisablePercentage; bool DisableCopyright; bool DisableDone; @@ -147,7 +154,6 @@ PATH_EXCL_MODE ExclPath; RECURSE_MODE Recurse; int64 VolSize; - Array NextVolSizes; uint CurVolNum; bool AllYes; bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. diff -ur unrar-6.1.7/os.hpp unrar-6.2.7/os.hpp --- unrar-6.1.7/os.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/os.hpp 2023-05-14 13:10:34 @@ -13,6 +13,8 @@ #endif #include +#include +#include #if defined(_WIN_ALL) || defined(_EMX) @@ -39,8 +41,15 @@ #define _UNICODE // Set _T() macro to convert from narrow to wide strings. #endif +#if 0 +// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR, +// but still keep SFX modules XP compatible. +#define WINVER _WIN32_WINNT_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#else #define WINVER _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP +#endif #if !defined(ZIPSFX) #define RAR_SMP @@ -72,9 +81,6 @@ #include #endif #ifdef _MSC_VER - #if _MSC_VER<1500 - #define for if (0) ; else for - #endif #include #include @@ -97,7 +103,6 @@ #include #include #include - #define SAVE_LINKS diff -ur unrar-6.1.7/pathfn.cpp unrar-6.2.7/pathfn.cpp --- unrar-6.1.7/pathfn.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/pathfn.cpp 2023-05-14 13:10:34 @@ -31,11 +31,17 @@ const wchar *s=DestPtr; if (s[0]!=0 && IsDriveDiv(s[1])) s+=2; - if (s[0]=='\\' && s[1]=='\\') + + // Skip UNC Windows \\server\share\ or Unix //server/share/ + if (IsPathDiv(s[0]) && IsPathDiv(s[1])) { - const wchar *Slash=wcschr(s+2,'\\'); - if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) - s=Slash+1; + uint SlashCount=0; + for (const wchar *t=s+2;*t!=0;t++) + if (IsPathDiv(*t) && ++SlashCount==2) + { + s=t+1; // Found two more path separators after leading two. + break; + } } for (const wchar *t=s;*t!=0;t++) if (IsPathDiv(*t)) @@ -422,50 +428,39 @@ bool IsNameUsable(const wchar *Name) { -#ifndef _UNIX - if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) + // 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. +#ifdef _UNIX + // Windows shares in Unix do not allow the drive letter, + // so unlike Windows version, we check all characters here. + if (wcschr(Name,':')!=NULL) return false; +#else + if (Name[0]!=0 && Name[1]!=0 && wcschr(Name+2,':')!=NULL) + return false; +#endif for (const wchar *s=Name;*s!=0;s++) { if ((uint)*s<32) return false; + + // It is for Windows shares in Unix. We can create such names in Windows. +#ifdef _UNIX + // No spaces or dots before the path separator are allowed in Windows + // shares. But they are allowed and automtically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) return false; - } #endif + } return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; } -void MakeNameUsable(char *Name,bool Extended) -{ -#ifdef _WIN_ALL - // In Windows we also need to convert characters not defined in current - // code page. This double conversion changes them to '?', which is - // catched by code below. - size_t NameLength=strlen(Name); - wchar NameW[NM]; - CharToWide(Name,NameW,ASIZE(NameW)); - WideToChar(NameW,Name,NameLength+1); - Name[NameLength]=0; -#endif - for (char *s=Name;*s!=0;s=charnext(s)) - { - if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32) - *s='_'; -#ifdef _EMX - if (*s=='=') - *s='_'; -#endif -#ifndef _UNIX - if (s-Name>1 && *s==':') - *s='_'; - // Remove ' ' and '.' before path separator, but allow .\ and ..\. - if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1])) - *s='_'; -#endif - } -} void MakeNameUsable(wchar *Name,bool Extended) @@ -474,7 +469,27 @@ { if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) *s='_'; -#ifndef _UNIX +#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 (*s==':') + *s='_'; + + // No spaces or dots before the path separator are allowed on Windows + // shares. But they are allowed and automtically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + *s='_'; + } +#else if (s-Name>1 && *s==':') *s='_'; #if 0 // We already can create such files. diff -ur unrar-6.1.7/pathfn.hpp unrar-6.2.7/pathfn.hpp --- unrar-6.1.7/pathfn.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/pathfn.hpp 2023-05-14 13:10:34 @@ -29,7 +29,6 @@ wchar* GetVolNumPart(const wchar *ArcName); void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); bool IsNameUsable(const wchar *Name); -void MakeNameUsable(char *Name,bool Extended); void MakeNameUsable(wchar *Name,bool Extended); void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); diff -ur unrar-6.1.7/qopen.cpp unrar-6.2.7/qopen.cpp --- unrar-6.1.7/qopen.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/qopen.cpp 2023-05-14 13:10:34 @@ -97,7 +97,7 @@ if (Arc->SubHead.Encrypted) { - RAROptions *Cmd=Arc->GetRAROptions(); + CommandData *Cmd=Arc->GetCommandData(); #ifndef RAR_NOCRYPT if (Cmd->Password.IsSet()) Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, diff -ur unrar-6.1.7/rar.hpp unrar-6.2.7/rar.hpp --- unrar-6.1.7/rar.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rar.hpp 2023-05-14 13:10:34 @@ -12,6 +12,7 @@ #include "version.hpp" #include "rardefs.hpp" #include "rarlang.hpp" +#include "rawint.hpp" #include "unicode.hpp" #include "errhnd.hpp" #include "secpassword.hpp" @@ -34,7 +35,6 @@ #endif #include "file.hpp" #include "crc.hpp" -#include "ui.hpp" #include "filefn.hpp" #include "filestr.hpp" #include "find.hpp" @@ -47,11 +47,11 @@ #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 "rawint.hpp" #include "rawread.hpp" #include "encname.hpp" #include "resource.hpp" diff -ur unrar-6.1.7/rardefs.hpp unrar-6.2.7/rardefs.hpp --- unrar-6.1.7/rardefs.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rardefs.hpp 2023-05-14 13:10:34 @@ -9,9 +9,13 @@ #define ASIZE(x) (sizeof(x)/sizeof(x[0])) -// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16) -// for CryptProtectMemory in SecPassword. -#define MAXPASSWORD 128 +// 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 #define MAXSFXSIZE 0x200000 diff -ur unrar-6.1.7/rawint.hpp unrar-6.2.7/rawint.hpp --- unrar-6.1.7/rawint.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rawint.hpp 2023-05-14 13:10:35 @@ -84,7 +84,7 @@ { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) return _byteswap_ulong(*(uint32 *)m); -#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#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]; @@ -97,7 +97,7 @@ { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) *(uint32*)mem = _byteswap_ulong(i); -#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) *(uint32*)mem = __builtin_bswap32(i); #else mem[0]=byte(i>>24); @@ -112,7 +112,7 @@ { #ifdef _MSC_VER return _byteswap_ulong(i); -#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#elif defined(__clang__) || defined(__GNUC__) return __builtin_bswap32(i); #else return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); diff -ur unrar-6.1.7/rdwrfn.cpp unrar-6.2.7/rdwrfn.cpp --- unrar-6.1.7/rdwrfn.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rdwrfn.cpp 2023-05-14 13:10:35 @@ -155,7 +155,7 @@ { #ifdef RARDLL - RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions(); + CommandData *Cmd=((Archive *)SrcFile)->GetCommandData(); if (Cmd->DllOpMode!=RAR_SKIP) { if (Cmd->Callback!=NULL && @@ -204,7 +204,7 @@ ArcPos+=ProcessedArcSize; Archive *SrcArc=(Archive *)SrcFile; - RAROptions *Cmd=SrcArc->GetRAROptions(); + CommandData *Cmd=SrcArc->GetCommandData(); int CurPercent=ToPercent(ArcPos,ArcSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) diff -ur unrar-6.1.7/recvol.cpp unrar-6.2.7/recvol.cpp --- unrar-6.1.7/recvol.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/recvol.cpp 2023-05-14 13:10:35 @@ -5,7 +5,7 @@ -bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent) { Archive Arc(Cmd); if (!Arc.Open(Name)) @@ -42,7 +42,7 @@ } -void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) { wchar RevName[NM]; *RevName=0; diff -ur unrar-6.1.7/recvol.hpp unrar-6.2.7/recvol.hpp --- unrar-6.1.7/recvol.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/recvol.hpp 2023-05-14 13:10:35 @@ -14,11 +14,11 @@ ThreadPool *RSThreadPool; #endif public: - RecVolumes3(RAROptions *Cmd,bool TestOnly); + RecVolumes3(CommandData *Cmd,bool TestOnly); ~RecVolumes3(); - void Make(RAROptions *Cmd,wchar *ArcName); - bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); - void Test(RAROptions *Cmd,const wchar *Name); + void Make(CommandData *Cmd,wchar *ArcName); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); }; @@ -48,8 +48,8 @@ class RecVolumes5 { private: - void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); - void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode); + 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); Array RecItems; @@ -76,13 +76,13 @@ public: // 'public' only because called from thread functions. void ProcessAreaRS(RecRSThreadData *td); public: - RecVolumes5(RAROptions *Cmd,bool TestOnly); + RecVolumes5(CommandData *Cmd,bool TestOnly); ~RecVolumes5(); - bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); - void Test(RAROptions *Cmd,const wchar *Name); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); }; -bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent); -void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name); +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent); +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name); #endif diff -ur unrar-6.1.7/recvol3.cpp unrar-6.2.7/recvol3.cpp --- unrar-6.1.7/recvol3.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/recvol3.cpp 2023-05-14 13:10:35 @@ -36,7 +36,7 @@ } #endif -RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly) +RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly) { memset(SrcFile,0,sizeof(SrcFile)); if (TestOnly) @@ -99,7 +99,7 @@ } -bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); @@ -497,7 +497,7 @@ } -void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name) +void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) { if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32. { diff -ur unrar-6.1.7/recvol5.cpp unrar-6.2.7/recvol5.cpp --- unrar-6.1.7/recvol5.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/recvol5.cpp 2023-05-14 13:10:35 @@ -4,7 +4,7 @@ // rev files by mistake. #define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. -RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) +RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly) { RealBuf=NULL; RealReadBuffer=NULL; @@ -70,7 +70,7 @@ #endif -void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) +void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) { /* RSCoder16 RS; @@ -141,7 +141,7 @@ -bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); @@ -494,7 +494,7 @@ } -void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name) +void RecVolumes5::Test(CommandData *Cmd,const wchar *Name) { wchar VolName[NM]; wcsncpyz(VolName,Name,ASIZE(VolName)); diff -ur unrar-6.1.7/rijndael.cpp unrar-6.2.7/rijndael.cpp --- unrar-6.1.7/rijndael.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rijndael.cpp 2023-05-14 13:10:35 @@ -90,18 +90,20 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { -#ifdef USE_SSE - // Check SSE here instead of constructor, so if object is a part of some - // structure memset'ed before use, this variable is not lost. + // 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) int CPUInfo[4]; - __cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function. - if ((CPUInfo[0] & 0x7fffffff)>=1) + __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(USE_NEON) + AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; #endif // Other developers asked us to initialize it to suppress "may be used @@ -141,18 +143,25 @@ keyEncToDec(); } + void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) { if (inputLen <= 0) return; size_t numBlocks = inputLen/16; -#ifdef USE_SSE +#if defined(USE_SSE) if (AES_NI) { blockEncryptSSE(input,numBlocks,outBuffer); return; } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockEncryptNeon(input,numBlocks,outBuffer); + return; + } #endif byte *prevBlock = m_initVector; @@ -239,6 +248,40 @@ } #endif + +#ifdef USE_NEON +void Rijndael::blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer) +{ + byte *prevBlock = m_initVector; + while (numBlocks > 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) { @@ -246,12 +289,18 @@ return; size_t numBlocks=inputLen/16; -#ifdef USE_SSE +#if defined(USE_SSE) if (AES_NI) { blockDecryptSSE(input,numBlocks,outBuffer); return; } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockDecryptNeon(input,numBlocks,outBuffer); + return; + } #endif byte block[16], iv[4][4]; @@ -339,6 +388,41 @@ numBlocks--; } _mm_storeu_si128((__m128i*)m_initVector,initVector); +} +#endif + + +#ifdef USE_NEON +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 diff -ur unrar-6.1.7/rijndael.hpp unrar-6.2.7/rijndael.hpp --- unrar-6.1.7/rijndael.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/rijndael.hpp 2023-05-14 13:10:35 @@ -18,6 +18,16 @@ bool AES_NI; #endif +#ifdef USE_NEON + // Set "crypto" attribute as replacement of -march=armv8-a+crypto switch. + __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(); diff -ur unrar-6.1.7/scantree.cpp unrar-6.2.7/scantree.cpp --- unrar-6.1.7/scantree.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/scantree.cpp 2023-05-14 13:10:35 @@ -215,10 +215,21 @@ UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); #endif - // We wish to scan entire disk if mask like c:\ is specified - // regardless of recursion mode. Use c:\*.* mask when need to scan only - // the root directory. - ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + // We prefer to scan entire disk if mask like \\server\share\ or c:\ + // is specified regardless of recursion mode. Use \\server\share\*.* + // or c:\*.* mask to scan only the root directory. + if (CurMask[0]=='\\' && CurMask[1]=='\\') + { + const wchar *Slash=wcschr(CurMask+2,'\\'); + if (Slash!=NULL) + { + Slash=wcschr(Slash+1,'\\'); + ScanEntireDisk=Slash!=NULL && *(Slash+1)==0; + } + } + else + ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + wchar *Name=PointToName(CurMask); if (*Name==0) diff -ur unrar-6.1.7/secpassword.cpp unrar-6.2.7/secpassword.cpp --- unrar-6.1.7/secpassword.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/secpassword.cpp 2023-05-14 13:10:35 @@ -56,7 +56,6 @@ SecPassword::SecPassword() { - CrossProcess=false; Set(L""); } @@ -70,7 +69,8 @@ void SecPassword::Clean() { PasswordSet=false; - cleandata(Password,sizeof(Password)); + if (Password.size()>0) + cleandata(&Password[0],Password.size()); } @@ -104,7 +104,7 @@ // Source string can be shorter than destination as in case when we process // -p parameter, so we need to take into account both sizes. memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); - SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess); + SecHideData(Dst,DstSize*sizeof(*Dst),Encode,false); } @@ -112,7 +112,7 @@ { if (PasswordSet) { - Process(Password,ASIZE(Password),Psw,MaxSize,false); + Process(&Password[0],Password.size(),Psw,MaxSize,false); Psw[MaxSize-1]=0; } else @@ -124,15 +124,14 @@ void SecPassword::Set(const wchar *Psw) { - if (*Psw==0) + // Eliminate any traces of previously stored password for security reason + // in case it was longer than new one. + Clean(); + + if (*Psw!=0) { - PasswordSet=false; - memset(Password,0,sizeof(Password)); - } - else - { PasswordSet=true; - Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true); + Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true); } } @@ -163,6 +162,9 @@ } +// 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 diff -ur unrar-6.1.7/secpassword.hpp unrar-6.2.7/secpassword.hpp --- unrar-6.1.7/secpassword.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/secpassword.hpp 2023-05-14 13:10:35 @@ -8,10 +8,7 @@ private: void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); - wchar Password[MAXPASSWORD]; - - // It is important to have this 'bool' value, so if our object is cleaned - // with memset as a part of larger structure, it is handled correctly. + std::vector Password = std::vector(MAXPASSWORD); bool PasswordSet; public: SecPassword(); @@ -22,10 +19,6 @@ bool IsSet() {return PasswordSet;} size_t Length(); bool operator == (SecPassword &psw); - - // Set to true if we need to pass a password to another process. - // We use it when transferring parameters to UAC elevated WinRAR. - bool CrossProcess; }; diff -ur unrar-6.1.7/strfn.cpp unrar-6.2.7/strfn.cpp --- unrar-6.1.7/strfn.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/strfn.cpp 2023-05-14 13:10:35 @@ -357,6 +357,32 @@ } +// Convert the number to string using thousand separators. +void fmtitoa(int64 n,wchar *Str,size_t MaxSize) +{ + static wchar ThSep=0; // Thousands separator. +#ifdef _WIN_ALL + wchar Info[10]; + if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0) + 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=7) { diff -ur unrar-6.1.7/threadmisc.cpp unrar-6.2.7/threadmisc.cpp --- unrar-6.1.7/threadmisc.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/threadmisc.cpp 2023-05-14 13:10:35 @@ -149,3 +149,5 @@ return NumCPU; } + + diff -ur unrar-6.1.7/timefn.hpp unrar-6.2.7/timefn.hpp --- unrar-6.1.7/timefn.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/timefn.hpp 2023-05-14 13:10:35 @@ -22,6 +22,17 @@ // 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. diff -ur unrar-6.1.7/ui.hpp unrar-6.2.7/ui.hpp --- unrar-6.1.7/ui.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/ui.hpp 2023-05-14 13:10:35 @@ -49,7 +49,7 @@ 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_SKIPENCARC, UIMSG_FILERENAME, UIWAIT_FIRST, UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, @@ -77,7 +77,7 @@ }; UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); -UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); void uiInit(SOUND_NOTIFY_MODE Sound); @@ -88,7 +88,7 @@ void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd); bool uiIsGlobalPasswordSet(); enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; @@ -145,30 +145,31 @@ // Templates recognize usual NULL as integer, not wchar*. #define UINULL ((wchar *)NULL) -inline void uiMsg(UIMESSAGE_CODE Code) +inline void uiMsgBase(uiMsgStore &Store) { - uiMsgStore Store(Code); - Store.Msg(); + // Called last, when no parameters are left. } -template void uiMsg(UIMESSAGE_CODE Code,T1 a1) +template void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN) { - uiMsgStore Store(Code); + // Process first parameter and pass the rest to same uiMsgBase. Store< void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2) -{ - uiMsgStore Store(Code); - Store< void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3) +// Use variadic templates. +// +// We must pass variable parameters by reference, so no temporary copies are +// created for custom string objects like CStringBase in 7-Zip decompression +// code. Such temporary copies would be destroyed inside of recursive +// uiMsgBase calls, leaving us with Str[] items pointing at released memory. +// Since we pass integer values as well, we can't use & references +// and must resort to && rvalue references. +template void uiMsg(UIMESSAGE_CODE Code,TN&&... aN) { - uiMsgStore Store(code); - Store<Overwrite==OVERWRITE_NONE) return UIASKREP_R_SKIP; diff -ur unrar-6.1.7/uiconsole.cpp unrar-6.2.7/uiconsole.cpp --- unrar-6.1.7/uiconsole.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/uiconsole.cpp 2023-05-14 13:10:35 @@ -262,6 +262,7 @@ break; case UIERROR_MISSINGVOL: Log(Str[0],St(MAbsNextVol),Str[0]); + mprintf(L" "); // For progress percent. break; #ifndef SFX_MODULE case UIERROR_NEEDPREVVOL: @@ -395,7 +396,8 @@ } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *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 diff -ur unrar-6.1.7/uisilent.cpp unrar-6.2.7/uisilent.cpp --- unrar-6.1.7/uisilent.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/uisilent.cpp 2023-05-14 13:10:35 @@ -33,7 +33,8 @@ } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, + SecPassword *Password,CheckPassword *CheckPwd) { return false; } diff -ur unrar-6.1.7/ulinks.cpp unrar-6.2.7/ulinks.cpp --- unrar-6.1.7/ulinks.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/ulinks.cpp 2023-05-14 13:10:35 @@ -70,7 +70,8 @@ } -bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, + const wchar *LinkName,bool &UpLink) { char Target[NM]; if (IsLink(Arc.FileHead.FileAttr)) @@ -100,13 +101,14 @@ if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) return false; + UpLink=strstr(Target,"..")!=NULL; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; } -bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) +static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) { char Target[NM]; WideToChar(hd->RedirName,Target,ASIZE(Target)); @@ -127,8 +129,6 @@ // Use hd->FileName instead of LinkName, since LinkName can include // the destination path as a prefix, which can confuse // IsRelativeSymlinkSafe algorithm. - // 2022.05.04: Use TargetW instead of previously used hd->RedirName - // for security reason. if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW))) return false; diff -ur unrar-6.1.7/unicode.cpp unrar-6.2.7/unicode.cpp --- unrar-6.1.7/unicode.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unicode.cpp 2023-05-14 13:10:35 @@ -229,10 +229,11 @@ #endif -// SrcSize is in wide characters, not in bytes. -byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) +// 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); diff -ur unrar-6.1.7/unicode.hpp unrar-6.2.7/unicode.hpp --- unrar-6.1.7/unicode.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unicode.hpp 2023-05-14 13:10:35 @@ -7,7 +7,7 @@ bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); -byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize); +byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize); wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); size_t WideToUtfSize(const wchar *Src); diff -ur unrar-6.1.7/unpack.cpp unrar-6.2.7/unpack.cpp --- unrar-6.1.7/unpack.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unpack.cpp 2023-05-14 13:10:35 @@ -309,7 +309,7 @@ Dec->QuickBits=MAX_QUICK_DECODE_BITS; break; default: - Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; + Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0; break; } diff -ur unrar-6.1.7/unpack.hpp unrar-6.2.7/unpack.hpp --- unrar-6.1.7/unpack.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unpack.hpp 2023-05-14 13:10:35 @@ -93,17 +93,17 @@ #ifdef RAR_SMP enum UNP_DEC_TYPE { - UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER + UNPDT_LITERAL=0,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER }; struct UnpackDecodedItem { - UNP_DEC_TYPE Type; + byte Type; // 'byte' instead of enum type to reduce memory use. ushort Length; union { uint Distance; - byte Literal[4]; + byte Literal[8]; // Store up to 8 chars here to speed up extraction. }; }; diff -ur unrar-6.1.7/unpack30.cpp unrar-6.2.7/unpack30.cpp --- unrar-6.1.7/unpack30.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unpack30.cpp 2023-05-14 13:10:35 @@ -55,7 +55,7 @@ if (!UnpReadBuf30()) break; } - if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) + if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr) { UnpWriteBuf30(); if (WrittenFileSize>DestUnpSize) diff -ur unrar-6.1.7/unpack50.cpp unrar-6.2.7/unpack50.cpp --- unrar-6.1.7/unpack50.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unpack50.cpp 2023-05-14 13:10:35 @@ -42,7 +42,7 @@ break; } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) @@ -93,7 +93,7 @@ } else { - Distance+=Inp.getbits32()>>(32-DBits); + Distance+=Inp.getbits()>>(16-DBits); Inp.addbits(DBits); } } diff -ur unrar-6.1.7/unpack50mt.cpp unrar-6.2.7/unpack50mt.cpp --- unrar-6.1.7/unpack50mt.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/unpack50mt.cpp 2023-05-14 13:10:35 @@ -345,7 +345,7 @@ if (D.DecodedSize>1) { UnpackDecodedItem *PrevItem=CurItem-1; - if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3) + if (PrevItem->Type==UNPDT_LITERAL && PrevItem->LengthLiteral)-1) { PrevItem->Length++; PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; @@ -388,7 +388,7 @@ } else { - Distance+=D.Inp.getbits32()>>(32-DBits); + Distance+=D.Inp.getbits()>>(16-DBits); D.Inp.addbits(DBits); } } @@ -451,7 +451,7 @@ while (ItemDestUnpSize) @@ -461,10 +461,10 @@ if (Item->Type==UNPDT_LITERAL) { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) - if (Item->Length==3 && UnpPtrLength==7 && UnpPtrLiteral; - UnpPtr+=4; + *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal); + UnpPtr+=8; } else #endif @@ -559,7 +559,7 @@ break; } } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) diff -ur unrar-6.1.7/version.hpp unrar-6.2.7/version.hpp --- unrar-6.1.7/version.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/version.hpp 2023-05-14 13:10:35 @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 12 -#define RARVER_BETA 0 -#define RARVER_DAY 4 +#define RARVER_MINOR 22 +#define RARVER_BETA 1 +#define RARVER_DAY 14 #define RARVER_MONTH 5 -#define RARVER_YEAR 2022 +#define RARVER_YEAR 2023 diff -ur unrar-6.1.7/volume.cpp unrar-6.2.7/volume.cpp --- unrar-6.1.7/volume.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/volume.cpp 2023-05-14 13:10:35 @@ -1,15 +1,15 @@ #include "rar.hpp" #ifdef RARDLL -static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); -static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); +static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize); +static bool DllVolNotify(CommandData *Cmd,wchar *NextName); #endif bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { - RAROptions *Cmd=Arc.GetRAROptions(); + CommandData *Cmd=Arc.GetCommandData(); HEADER_TYPE HeaderType=Arc.GetHeaderType(); FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; @@ -190,7 +190,7 @@ #ifdef RARDLL -bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) +bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize) { bool DllVolChanged=false,DllVolAborted=false; @@ -246,7 +246,7 @@ #ifdef RARDLL -bool DllVolNotify(RAROptions *Cmd,wchar *NextName) +bool DllVolNotify(CommandData *Cmd,wchar *NextName) { char NextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); diff -ur unrar-6.1.7/volume.hpp unrar-6.2.7/volume.hpp --- unrar-6.1.7/volume.hpp 2022-10-19 22:05:12 +++ unrar-6.2.7/volume.hpp 2023-05-14 13:10:35 @@ -1,10 +1,7 @@ #ifndef _RAR_VOLUME_ #define _RAR_VOLUME_ -void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos, - ComprDataIO *DataIO); bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, wchar Command); -void SetVolWrite(Archive &Dest,int64 VolSize); #endif diff -ur unrar-6.1.7/win32stm.cpp unrar-6.2.7/win32stm.cpp --- unrar-6.1.7/win32stm.cpp 2022-10-19 22:05:12 +++ unrar-6.2.7/win32stm.cpp 2023-05-14 13:10:35 @@ -111,16 +111,23 @@ wcsncatz(FullName,StreamName,ASIZE(FullName)); + FindData fd; - bool Found=FindFile::FastFind(FileName,&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) && Arc.ReadSubData(NULL,&CurFile,false)) - CurFile.Close(); + + if (CurFile.WCreate(FullName)) + { + if (Arc.ReadSubData(NULL,&CurFile,false)) + CurFile.Close(); + } + + // Restoring original file timestamps. File HostFile; - if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) + if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime);