Update to UnRAR v6.2.10

pull/1007/head
Micah Snyder 2 years ago committed by Micah Snyder
parent 95df41b5bf
commit 6e5db6eaaa
  1. 12
      libclamunrar/UnRARDll.vcxproj
  2. 12
      libclamunrar/archive.cpp
  3. 11
      libclamunrar/archive.hpp
  4. 136
      libclamunrar/arcread.cpp
  5. 34
      libclamunrar/array.hpp
  6. 9
      libclamunrar/blake2s.hpp
  7. 25
      libclamunrar/cmddata.cpp
  8. 6
      libclamunrar/cmddata.hpp
  9. 4
      libclamunrar/cmdfilter.cpp
  10. 7
      libclamunrar/cmdmix.cpp
  11. 1
      libclamunrar/compress.hpp
  12. 161
      libclamunrar/crc.cpp
  13. 4
      libclamunrar/crc.hpp
  14. 14
      libclamunrar/crypt.cpp
  15. 72
      libclamunrar/crypt.hpp
  16. 5
      libclamunrar/crypt3.cpp
  17. 12
      libclamunrar/crypt5.cpp
  18. 2
      libclamunrar/dll.cpp
  19. 10
      libclamunrar/dll.rc
  20. 5
      libclamunrar/errhnd.cpp
  21. 99
      libclamunrar/extinfo.cpp
  22. 3
      libclamunrar/extinfo.hpp
  23. 517
      libclamunrar/extract.cpp
  24. 34
      libclamunrar/extract.hpp
  25. 2
      libclamunrar/filcreat.cpp
  26. 2
      libclamunrar/filcreat.hpp
  27. 67
      libclamunrar/file.cpp
  28. 10
      libclamunrar/file.hpp
  29. 4
      libclamunrar/filefn.cpp
  30. 2
      libclamunrar/filefn.hpp
  31. 18
      libclamunrar/find.cpp
  32. 8
      libclamunrar/getbits.cpp
  33. 14
      libclamunrar/getbits.hpp
  34. 2
      libclamunrar/hardlinks.cpp
  35. 2
      libclamunrar/hash.cpp
  36. 10
      libclamunrar/hash.hpp
  37. 10
      libclamunrar/headers.cpp
  38. 29
      libclamunrar/headers.hpp
  39. 7
      libclamunrar/headers5.hpp
  40. 2
      libclamunrar/isnt.cpp
  41. 11
      libclamunrar/list.cpp
  42. 7
      libclamunrar/loclang.hpp
  43. 11
      libclamunrar/makefile
  44. 6
      libclamunrar/model.cpp
  45. 8
      libclamunrar/options.cpp
  46. 12
      libclamunrar/options.hpp
  47. 13
      libclamunrar/os.hpp
  48. 93
      libclamunrar/pathfn.cpp
  49. 1
      libclamunrar/pathfn.hpp
  50. 2
      libclamunrar/qopen.cpp
  51. 4
      libclamunrar/rar.hpp
  52. 10
      libclamunrar/rardefs.hpp
  53. 6
      libclamunrar/rawint.hpp
  54. 4
      libclamunrar/rdwrfn.cpp
  55. 4
      libclamunrar/recvol.cpp
  56. 22
      libclamunrar/recvol.hpp
  57. 17
      libclamunrar/recvol3.cpp
  58. 8
      libclamunrar/recvol5.cpp
  59. 98
      libclamunrar/rijndael.cpp
  60. 10
      libclamunrar/rijndael.hpp
  61. 19
      libclamunrar/scantree.cpp
  62. 30
      libclamunrar/secpassword.cpp
  63. 9
      libclamunrar/secpassword.hpp
  64. 26
      libclamunrar/strfn.cpp
  65. 1
      libclamunrar/strfn.hpp
  66. 6
      libclamunrar/system.cpp
  67. 2
      libclamunrar/threadmisc.cpp
  68. 11
      libclamunrar/timefn.hpp
  69. 37
      libclamunrar/ui.hpp
  70. 2
      libclamunrar/uicommon.cpp
  71. 5
      libclamunrar/uiconsole.cpp
  72. 3
      libclamunrar/uisilent.cpp
  73. 8
      libclamunrar/ulinks.cpp
  74. 7
      libclamunrar/unicode.cpp
  75. 2
      libclamunrar/unicode.hpp
  76. 12
      libclamunrar/unpack.cpp
  77. 6
      libclamunrar/unpack.hpp
  78. 2
      libclamunrar/unpack30.cpp
  79. 4
      libclamunrar/unpack50.cpp
  80. 14
      libclamunrar/unpack50mt.cpp
  81. 48
      libclamunrar/uowners.cpp
  82. 8
      libclamunrar/version.hpp
  83. 10
      libclamunrar/volume.cpp
  84. 3
      libclamunrar/volume.hpp
  85. 2
      libclamunrar/win32lnk.cpp
  86. 16
      libclamunrar/win32stm.cpp

@ -138,7 +138,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@ -168,7 +168,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@ -198,7 +198,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@ -239,7 +239,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@ -274,7 +274,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@ -315,7 +315,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>

@ -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 @@ Archive::Archive(RAROptions *InitCmd)
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;

@ -32,8 +32,8 @@ class Archive:public File
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 @@ class Archive:public File
#endif
ComprDataIO SubDataIO;
bool DummyCmd;
RAROptions *Cmd;
CommandData *Cmd;
RarTime LatestTime;
@ -58,7 +58,7 @@ class Archive:public File
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 @@ class Archive:public File
const wchar *Name,uint Flags);
bool ReadSubData(Array<byte> *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);
@ -107,7 +107,6 @@ class Archive:public File
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
UnixOwnersHeader UOHead;
EAHeader EAHead;
StreamHeader StreamHead;

@ -100,6 +100,9 @@ void Archive::UnexpEndArcMsg()
// 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 @@ size_t Archive::ReadHeader15()
#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 @@ size_t Archive::ReadHeader15()
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);
@ -469,19 +476,6 @@ size_t Archive::ReadHeader15()
SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
case UO_HEAD:
*(SubBlockHeader *)&UOHead=SubBlockHead;
UOHead.OwnerNameSize=Raw.Get2();
UOHead.GroupNameSize=Raw.Get2();
if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
UOHead.OwnerName[UOHead.OwnerNameSize]=0;
UOHead.GroupName[UOHead.GroupNameSize]=0;
break;
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
EAHead.UnpSize=Raw.Get4();
@ -513,8 +507,12 @@ size_t Archive::ReadHeader15()
ushort HeaderCRC=Raw.GetCRC15(false);
// Old AV header does not have header CRC properly set.
// Old Unix owners header didn't include string fields into header size,
// but included them into CRC, so it couldn't be verified with generic
// approach here.
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
ShortBlock.HeaderType!=HEAD3_AV)
ShortBlock.HeaderType!=HEAD3_AV &&
(ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD))
{
bool Recovered=false;
if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
@ -577,14 +575,20 @@ size_t Archive::ReadHeader50()
// in -p<pwd> 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<pwd> or Ctrl+P.
{
@ -850,8 +854,6 @@ size_t Archive::ReadHeader50()
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,25 +877,6 @@ size_t Archive::ReadHeader50()
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 +899,7 @@ size_t Archive::ReadHeader50()
#if !defined(RAR_NOCRYPT)
void Archive::RequestArcPassword()
void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
{
if (!Cmd->Password.IsSet())
{
@ -946,7 +929,7 @@ void Archive::RequestArcPassword()
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 +942,7 @@ void Archive::RequestArcPassword()
#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 +965,57 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
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<char> 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<wchar> 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 +1471,9 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
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;
}

@ -10,7 +10,6 @@ template <class T> class Array
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 @@ template <class T> class Array
void Alloc(size_t Items);
void Reset();
void SoftReset();
void operator = (Array<T> &Src);
Array<T>& operator = (const Array<T> &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 @@ template <class T> void Array<T>::CleanData()
BufSize=0;
AllocSize=0;
MaxSize=0;
Secure=false;
}
@ -71,11 +68,7 @@ template <class T> Array<T>::Array(const Array &Src)
template <class T> Array<T>::~Array()
{
if (Buffer!=NULL)
{
if (Secure)
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
@ -111,25 +104,9 @@ template <class T> void Array<T>::Add(size_t Items)
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 <class T> void Array<T>::SoftReset()
}
template <class T> void Array<T>::operator =(Array<T> &Src)
template <class T> Array<T>& Array<T>::operator =(const Array<T> &Src)
{
Reset();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
return *this;
}

@ -20,10 +20,15 @@ enum blake2s_constant
// '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];

@ -26,9 +26,10 @@ void CommandData::Init()
FileArgs.Reset();
ExclArgs.Reset();
InclArgs.Reset();
StoreArgs.Reset();
ArcNames.Reset();
NextVolSizes.Reset();
StoreArgs.Reset();
Password.Clean();
NextVolSizes.clear();
}
@ -314,6 +315,21 @@ void CommandData::ProcessSwitch(const wchar *Switch)
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 @@ void CommandData::ProcessSwitch(const wchar *Switch)
else
if (!Password.IsSet())
{
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
eprintf(L"\n");
}
break;
@ -685,7 +701,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'P':
if (Switch[1]==0)
{
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
eprintf(L"\n");
}
else
@ -927,6 +943,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
void CommandData::BadSwitch(const wchar *Switch)
{
mprintf(St(MUnknownOption),Switch);
mprintf(L"\n");
ErrHandler.Exit(RARX_USERERROR);
}
#endif

@ -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 @@ class CommandData:public RAROptions
StringList InclArgs;
StringList ArcNames;
StringList StoreArgs;
SecPassword Password;
std::vector<int64> NextVolSizes;
};
#endif

@ -289,8 +289,8 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy
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;

@ -92,6 +92,13 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
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;

@ -17,6 +17,7 @@ class PackDef
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} */

@ -110,3 +110,164 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
#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<DataEnd; Data+=8 )
{
uint64 Index=StartCRC;
#ifdef BIG_ENDIAN
Index ^= (uint64(Data[0])|(uint64(Data[1])<<8)|(uint64(Data[2])<<16)|(uint64(Data[3])<<24))|
(uint64(Data[4])<<32)|(uint64(Data[5])<<40)|(uint64(Data[6])<<48)|(uint64(Data[7])<<56);
#else
Index ^= *(uint64 *)Data;
#endif
StartCRC = crc64_tables[ 7 ] [ ( byte ) (Index ) ] ^
crc64_tables[ 6 ] [ ( byte ) (Index >> 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;I<FirstSize;I++)
b[I]=(byte)I;
uint r32=CRC32(0xffffffff,b,FirstSize);
for (uint I=FirstSize;I<1024;I++)
{
b[0]=(byte)I;
r32=CRC32(r32,b,1);
}
if ((r32^0xffffffff)==0xB70B4C26)
mprintf(L"\nCRC32 test4 OK");
else
mprintf(L"\nCRC32 test4 FAILED");
if ((CRC64(0xffffffffffffffff,(byte*)"testtesttest",12)^0xffffffffffffffff)==0x7B1C2D230EDEB436)
mprintf(L"\nCRC64 test1 OK");
else
mprintf(L"\nCRC64 test1 FAILED");
if (CRC64(0,(byte*)"te\x80st",5)==0xB5DBF9583A6EED4A)
mprintf(L"\nCRC64 test2 OK");
else
mprintf(L"\nCRC64 test2 FAILED");
for (uint I=0;I<14;I++) // Check for possible int sign extension.
b[I]=(byte)0x7f+I;
if ((CRC64(0xffffffffffffffff,b,14)^0xffffffffffffffff)==0xE019941C05B2820C)
mprintf(L"\nCRC64 test3 OK");
else
mprintf(L"\nCRC64 test3 FAILED");
for (uint I=0;I<FirstSize;I++)
b[I]=(byte)I;
uint64 r64=CRC64(0xffffffffffffffff,b,FirstSize);
for (uint I=FirstSize;I<1024;I++)
{
b[0]=(byte)I;
r64=CRC64(r64,b,1);
}
if ((r64^0xffffffffffffffff)==0xD51FB58DC789C400)
mprintf(L"\nCRC64 test4 OK");
else
mprintf(L"\nCRC64 test4 FAILED");
const size_t BufSize=0x100000;
byte *Buf=new byte[BufSize];
memset(Buf,0,BufSize);
clock_t StartTime=clock();
r32=0xffffffff;
const uint BufCount=5000;
for (uint I=0;I<BufCount;I++)
r32=CRC32(r32,Buf,BufSize);
if (r32!=0) // Otherwise compiler optimizer removes CRC calculation.
mprintf(L"\nCRC32 speed: %d MB/s",BufCount*1000/(clock()-StartTime));
StartTime=clock();
r64=0xffffffffffffffff;
for (uint I=0;I<BufCount;I++)
r64=CRC64(r64,Buf,BufSize);
if (r64!=0) // Otherwise compiler optimizer removes CRC calculation.
mprintf(L"\nCRC64 speed: %d MB/s",BufCount*1000/(clock()-StartTime));
}
#endif
#endif

@ -11,5 +11,9 @@ uint CRC32(uint StartCRC,const void *Addr,size_t Size);
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
#endif
#if 0
void InitCRC64(uint64 *CRCTab);
uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size);
#endif
#endif

@ -11,21 +11,12 @@
CryptData::CryptData()
{
Method=CRYPT_NONE;
memset(KDF3Cache,0,sizeof(KDF3Cache));
memset(KDF5Cache,0,sizeof(KDF5Cache));
KDF3CachePos=0;
KDF5CachePos=0;
memset(CRCTab,0,sizeof(CRCTab));
}
CryptData::~CryptData()
{
cleandata(KDF3Cache,sizeof(KDF3Cache));
cleandata(KDF5Cache,sizeof(KDF5Cache));
}
void CryptData::DecryptBlock(byte *Buf,size_t Size)
@ -56,15 +47,18 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
if (!Password->IsSet() || 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)
{

@ -30,6 +30,18 @@ class CryptData
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 @@ class CryptData
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 @@ class CryptData
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);
@ -88,6 +110,54 @@ class CryptData
static void SetSalt(byte *Salt,size_t SaltSize);
};
class CheckPassword
{
public:
enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
virtual CONFIDENCE GetConfidence()=0;
virtual bool Check(SecPassword *Password)=0;
};
class RarCheckPassword:public CheckPassword
{
private:
CryptData *Crypt;
uint Lg2Count;
byte Salt[SIZE_SALT50];
byte InitV[SIZE_INITV];
byte PswCheck[SIZE_PSWCHECK];
public:
RarCheckPassword()
{
Crypt=NULL;
}
~RarCheckPassword()
{
delete Crypt;
}
void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
{
if (Crypt==NULL)
Crypt=new CryptData;
memcpy(this->Salt,Salt,sizeof(this->Salt));
memcpy(this->InitV,InitV,sizeof(this->InitV));
this->Lg2Count=Lg2Count;
memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
}
bool IsSet() {return Crypt!=NULL;}
// RAR5 provides the higly reliable 64 bit password verification value.
CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
bool Check(SecPassword *Password)
{
byte PswCheck[SIZE_PSWCHECK];
Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
}
};
void GetRnd(byte *RndBuf,size_t BufSize);
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,

@ -18,8 +18,9 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co
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);

@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
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 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
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 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
for (uint I=0;I<ASIZE(KDF5Cache);I++)
{
KDF5CacheItem *Item=KDF5Cache+I;
if (Item->Lg2Count==Lg2Cnt && Item->Pwd==*Password &&
if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{
memcpy(Key,Item->Key,sizeof(Key));

@ -329,7 +329,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
{
Data->Cmd.DllError=0;
if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
Operation==RAR_SKIP) // && !Data->Arc.Solid)
Operation==RAR_SKIP && !Data->Arc.Solid)
{
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
Data->Arc.FileHead.SplitAfter)

@ -2,8 +2,8 @@
#include <commctrl.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6, 12, 100, 489
PRODUCTVERSION 6, 12, 100, 489
FILEVERSION 6, 23, 100, 944
PRODUCTVERSION 6, 23, 100, 944
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
@ -14,9 +14,9 @@ FILETYPE VFT_APP
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.23.0\0"
VALUE "ProductVersion", "6.23.0\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
}

@ -169,10 +169,13 @@ void ErrorHandler::OpenErrorMsg(const wchar *FileName)
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();
}

@ -21,17 +21,11 @@
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{
#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
#ifdef _UNIX
case UO_HEAD:
if (Cmd->ProcessOwners)
ExtractUnixOwner20(Arc,Name);
break;
#endif
#ifdef _WIN_ALL
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
@ -39,8 +33,8 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
case STREAM_HEAD:
ExtractStreams20(Arc,Name);
break;
#endif
}
#endif
}
#endif
@ -112,6 +106,68 @@ static bool LinkInPath(const wchar *Name)
}
// 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 && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
if (IsPathDiv(Path[I]) && I>SkipLength)
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 +187,14 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
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 +220,26 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
}
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)

@ -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

@ -5,10 +5,30 @@ CmdExtract::CmdExtract(CommandData *Cmd)
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,7 +38,26 @@ CmdExtract::CmdExtract(CommandData *Cmd)
CmdExtract::~CmdExtract()
{
FreeAnalyzeData();
delete Unp;
delete Analyze;
}
void CmdExtract::FreeAnalyzeData()
{
for (size_t I=0;I<RefList.Size();I++)
{
// We can have undeleted temporary reference source here if extraction
// was interrupted early or if user refused to overwrite prompt.
if (RefList[I].TmpName!=NULL)
DelFile(RefList[I].TmpName);
free(RefList[I].RefName);
free(RefList[I].TmpName);
}
RefList.Reset();
memset(Analyze,0,sizeof(*Analyze));
}
@ -30,10 +69,13 @@ void CmdExtract::DoExtract()
PasswordCancelled=false;
DataIO.SetCurrentCommand(Cmd->Command[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 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
AllMatchesExact=true;
AnySolidDataUnpackedWell=false;
ArcAnalyzed=false;
StartTime.SetCurrentTime();
LastCheckedSymlink.clear();
}
@ -168,20 +214,33 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
}
#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 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
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,7 +337,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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)
@ -315,6 +387,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
FirstFile=false;
#endif
bool RefTarget=false;
if (!MatchFound)
for (size_t I=0;I<RefList.Size();I++)
if (wcscmp(ArcFileName,RefList[I].RefName)==0)
{
ExtractRef *MatchedRef=&RefList[I];
if (!Cmd->Test) // 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 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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
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<pwd> or Ctrl+P to avoid the infinite loop.
// Set a password before creating the file, so we can skip creating
// in case of wrong password.
SecPassword FilePassword=Cmd->Password;
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
ConvertDosPassword(Arc,FilePassword);
#endif
byte PswCheck[SIZE_PSWCHECK];
DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
Arc.FileHead.HashKey,PswCheck);
// If header is damaged, we cannot rely on password check value,
// because it can be damaged too.
if (Arc.FileHead.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();
// 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
if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
{
// This message is used by Android GUI to reset cached passwords.
// Update appropriate code if changed.
uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
}
else // For passwords entered manually.
{
// This message is used by Android GUI and Windows GUI and SFX to
// reset cached passwords. Update appropriate code if changed.
uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
Cmd->Password.Clean();
// Avoid new requests for unrar.dll to prevent the infinite loop
// if app always returns the same password.
#ifndef RARDLL
continue; // Request a password again.
#endif
}
#ifdef RARDLL
// If we already have ERAR_EOPEN as result of missing volume,
// we should not replace it with less precise ERAR_BAD_PASSWORD.
if (Cmd->DllError!=ERAR_EOPEN)
Cmd->DllError=ERAR_BAD_PASSWORD;
#endif
ErrHandler.SetErrorCode(RARX_BADPWD);
ExtrFile=false;
}
#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,7 +779,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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));
@ -675,17 +797,37 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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);
// Unix symlink can have its own owner data.
if (LinkSuccess)
SetFileHeaderExtra(Cmd,Arc,DestFileName);
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 +851,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
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 +1009,64 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
}
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;I<RefList.Size();I++)
if (wcscmp(RedirName,RefList[I].RefName)==0 && RefList[I].TmpName!=NULL)
{
// If only one reference left targeting to this temporary file,
// it is faster to move the file instead of copying and deleting it.
bool RefMove=RefList[I].RefCount-- == 1;
NameExisting=RefList[I].TmpName;
if (RefMove) // Only one reference left for this temporary file.
{
New.Delete(); // Delete the previously opened destination file.
// Try moving the file first.
bool MoveFailed=!RenameFile(NameExisting,NameNew);
if (MoveFailed)
{
// If move failed, re-create the destination and try coping.
if (!New.WCreate(NameNew,FMF_WRITE|FMF_SHAREREAD))
return false;
RefMove=false; // Try copying below.
}
else
{
// If moved successfully, reopen the destination file and seek to
// end for SetOpenFileTime() and possible Truncate() calls later.
if (New.Open(NameNew))
New.Seek(0,SEEK_END);
// We already moved the file, so clean the name to not try
// deleting non-existent temporary file later.
free(RefList[I].TmpName);
RefList[I].TmpName=NULL;
return true;
}
}
if (!RefMove)
OpenFailed=!Existing.Open(NameExisting);
break;
}
if (OpenFailed)
{
ErrHandler.OpenErrorMsg(NameExisting);
uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
uiMsg(UIERROR_FILECOPYHINT,ArcName);
#ifdef RARDLL
Cmd->DllError=ERAR_EREFERENCE;
Cmd->DllError=ERAR_EREFERENCE;
#endif
return false;
return false;
}
}
Array<char> Buffer(0x100000);
Array<byte> Buffer(0x100000);
int64 CopySize=0;
while (true)
@ -890,6 +1075,10 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *
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 +1089,16 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *
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 +1232,11 @@ bool CmdExtract::ExtrDllGetPassword()
#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 +1254,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
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 +1330,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
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 +1413,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
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 +1461,59 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
#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 +1526,88 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
}
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;I<RefList.Size();I++)
if (wcscmp(Arc.FileHead.RedirName,RefList[I].RefName)==0)
{
// Increment the reference count if we added such reference
// source earlier.
RefList[I].RefCount++;
AlreadyAdded=true;
break;
}
// Limit the maximum size of reference sources list to some
// sensible value to prevent the excessive memory allocation.
size_t MaxListSize=1000000;
if (!AlreadyAdded && RefList.Size()<MaxListSize)
{
ExtractRef Ref={0};
Ref.RefName=wcsdup(Arc.FileHead.RedirName);
Ref.RefCount=1;
RefList.Push(Ref);
}
}
}
else
{
if (PrevMatched) // First non-matched item after matched.
{
// We would perform the unnecessarily string comparison
// when extracting if we set this value for first volume
// or non-volume archive.
if (!FirstVolume)
wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName));
Analyze->EndPos=Arc.CurBlockPos;
}
PrevMatched=false;
}
}
FirstFile=false;
if (Arc.FileHead.SplitAfter)
{
OpenNext=true; // Allow open next volume.
@ -1316,16 +1618,25 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
}
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

@ -6,13 +6,32 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
struct ExtractRef
{
wchar *RefName;
wchar *TmpName;
uint64 RefCount;
};
Array<ExtractRef> 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 @@ class CmdExtract
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 @@ class CmdExtract
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

@ -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)

@ -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);

@ -522,29 +522,35 @@ bool File::RawSeek(int64 Offset,int Method)
{
if (hFile==FILE_BAD_HANDLE)
return true;
if (!IsSeekable())
if (!IsSeekable()) // To extract archives from stdin with -si.
{
if (Method==SEEK_CUR)
{
Offset+=CurFilePos;
Method=SEEK_SET;
}
if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward.
// We tried to dynamically allocate 32 KB buffer here, but it improved
// speed in Windows 10 by mere ~1.5%.
byte Buf[4096];
if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
{
uint64 SkipSize=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::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
}
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
#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
#if defined(_UNIX) || defined(_EMX)
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
}

@ -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 @@ class File
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 @@ class File
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;}

@ -320,7 +320,6 @@ bool SetFileAttr(const wchar *Name,uint Attr)
}
#if 0
wchar *MkTemp(wchar *Name,size_t MaxSize)
{
size_t Length=wcslen(Name);
@ -354,7 +353,6 @@ wchar *MkTemp(wchar *Name,size_t MaxSize)
}
return Name;
}
#endif
#if !defined(SFX_MODULE)
@ -399,7 +397,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
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

@ -27,9 +27,7 @@ bool IsDeleteAllowed(uint FileAttr);
void PrepareToDelete(const wchar *Name);
uint GetFileAttr(const wchar *Name);
bool SetFileAttr(const wchar *Name,uint Attr);
#if 0
wchar* MkTemp(wchar *Name,size_t MaxSize);
#endif
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};

@ -117,7 +117,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
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 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
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

@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer)
ExternalBuffer=false;
if (AllocBuffer)
{
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
// So let's allocate 3 additional bytes for situation, when we need to
// getbits*() attempt to read data from InAddr, ... InAddr+4 positions.
// So let's allocate 4 additional bytes for situation, when we need to
// read only 1 byte from the last position of buffer and avoid a crash
// from access to next 3 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+3;
// from access to next 4 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+4;
InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area

@ -28,26 +28,38 @@ class BitInput
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;

@ -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);

@ -26,7 +26,7 @@ void HashValue::Init(HASH_TYPE Type)
}
bool HashValue::operator == (const HashValue &cmp)
bool HashValue::operator == (const HashValue &cmp) const
{
if (Type==HASH_NONE || cmp.Type==HASH_NONE)
return true;

@ -6,8 +6,14 @@ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
struct HashValue
{
void Init(HASH_TYPE Type);
bool operator == (const HashValue &cmp);
bool operator != (const HashValue &cmp) {return !(*this==cmp);}
// 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

@ -49,13 +49,5 @@ FileHeader& FileHeader::operator = (FileHeader &hd)
void MainHeader::Reset()
{
HighPosAV=0;
PosAV=0;
CommentInHeader=false;
PackComment=false;
Locator=false;
QOpenOffset=0;
QOpenMaxSize=0;
RROffset=0;
RRMaxSize=0;
*this={};
}

@ -6,12 +6,11 @@
#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
#define SIZEOF_PROTECTHEAD 26
#define SIZEOF_UOHEAD 18
#define SIZEOF_STREAMHEAD 26
#define VER_PACK 29U
@ -162,12 +161,16 @@ struct MainHeader:BaseBlock
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 +233,7 @@ struct FileHeader:BlockHeader
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;
@ -321,16 +324,6 @@ struct ProtectHeader:BlockHeader
};
struct UnixOwnersHeader:SubBlockHeader
{
ushort OwnerNameSize;
ushort GroupNameSize;
/* dummy */
char OwnerName[256];
char GroupName[256];
};
struct EAHeader:SubBlockHeader
{
uint UnpSize;

@ -59,11 +59,18 @@
// Main header extra field values.
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
#define MHEXTRA_METADATA 0x02 // Archive metadata.
// Flags for MHEXTRA_LOCATOR.
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
// Flags for MHEXTRA_METADATA.
#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
// File and service header extra field values.
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
#define FHEXTRA_HASH 0x02 // File hash.

@ -40,7 +40,7 @@ static bool WMI_IsWindows10()
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))
{

@ -36,6 +36,7 @@ void ListArchive(CommandData *Cmd)
{
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 @@ void ListArchive(CommandData *Cmd)
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");
}

@ -28,6 +28,7 @@
#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>"
#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>"
#define MCHelpCmd L"\n\n<Commands>"
#define MCHelpCmdA L"\n a Add files to archive"
#define MCHelpCmdC L"\n c Add archive comment"
@ -44,7 +45,7 @@
#define MCHelpCmdR L"\n r Repair archive"
#define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
#define MCHelpCmdRN L"\n rn Rename archived files"
#define MCHelpCmdRR L"\n rr[N] Add data recovery record"
#define MCHelpCmdRR L"\n rr[N] Add the data recovery record"
#define MCHelpCmdRV L"\n rv[N] Create recovery volumes"
#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
#define MCHelpCmdT L"\n t Test archive files"
@ -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<path> 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"

@ -126,7 +126,7 @@ OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filc
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 @@ clean:
@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)

@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
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;

@ -6,14 +6,6 @@ RAROptions::RAROptions()
}
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));

@ -45,6 +45,12 @@ enum OVERWRITE_MODE
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 @@ struct FilterMode
#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 @@ class RAROptions
wchar ArcPath[NM]; // For -ap<path>.
wchar ExclArcPath[NM]; // For -ep4<path> switch.
SecPassword Password;
bool EncryptHeaders;
bool SkipEncrypted;
@ -132,6 +138,7 @@ class RAROptions
HASH_TYPE HashType;
int Recovery;
int RecVolNumber;
ARC_METADATA ArcMetadata;
bool DisablePercentage;
bool DisableCopyright;
bool DisableDone;
@ -147,7 +154,6 @@ class RAROptions
PATH_EXCL_MODE ExclPath;
RECURSE_MODE Recurse;
int64 VolSize;
Array<int64> NextVolSizes;
uint CurVolNum;
bool AllYes;
bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.

@ -13,6 +13,8 @@
#endif
#include <new>
#include <string>
#include <vector>
#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 <dir.h>
#endif
#ifdef _MSC_VER
#if _MSC_VER<1500
#define for if (0) ; else for
#endif
#include <direct.h>
#include <intrin.h>
@ -98,7 +104,6 @@
#include <time.h>
#include <signal.h>
#define SAVE_LINKS
#define ENABLE_ACCESS

@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
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 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
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 @@ void MakeNameUsable(wchar *Name,bool Extended)
{
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 automatically 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.
@ -731,7 +746,7 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u
// Here we ensure that we have enough 'N' characters to fit all digits
// of archive number. We'll replace them by actual number later
// in this function.
if (NCount<Digits)
if (NCount<Digits && wcslen(Mask)+Digits-NCount<ASIZE(Mask))
{
wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1);
wmemset(Mask+I,'N',Digits);
@ -768,7 +783,7 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u
if (StartWeekDay%7>=4)
CurWeek++;
char Field[10][6];
char Field[10][11];
sprintf(Field[0],"%04u",rlt.Year);
sprintf(Field[1],"%02u",rlt.Month);

@ -29,7 +29,6 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx
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);

@ -97,7 +97,7 @@ void QuickOpen::Load(uint64 BlockPos)
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,

@ -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"

@ -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

@ -84,7 +84,7 @@ inline uint32 RawGetBE4(const byte *m)
{
#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 @@ inline void RawPutBE4(uint32 i,byte *mem)
{
#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 @@ inline uint32 ByteSwap32(uint32 i)
{
#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);

@ -155,7 +155,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#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 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
RAROptions *Cmd=SrcArc->GetRAROptions();
CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)

@ -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 @@ bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent)
}
void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name)
void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name)
{
wchar RevName[NM];
*RevName=0;

@ -14,11 +14,11 @@ class RecVolumes3
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 @@ struct RecRSThreadData
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<RecVolItem> RecItems;
@ -76,13 +76,13 @@ class RecVolumes5
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

@ -36,7 +36,7 @@ THREAD_PROC(RSDecodeThread)
}
#endif
RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly)
{
memset(SrcFile,0,sizeof(SrcFile));
if (TestOnly)
@ -99,7 +99,7 @@ static bool IsNewStyleRev(const wchar *Name)
}
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));
@ -226,7 +226,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (WrongParam)
continue;
}
if (P[1]+P[2]>255)
if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255)
continue;
if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
{
@ -238,7 +238,14 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
wcsncpyz(PrevName,CurName,ASIZE(PrevName));
File *NewFile=new File;
NewFile->TOpen(CurName);
SrcFile[FileNumber+P[0]-1]=NewFile;
// This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255
// checks above. Still we keep it here for better clarity and security.
int SrcPos=FileNumber+P[0]-1;
if (SrcPos<0 || SrcPos>=ASIZE(SrcFile))
continue;
SrcFile[SrcPos]=NewFile;
FoundRecVolumes++;
if (RecFileSize==0)
@ -497,7 +504,7 @@ void RSEncode::DecodeBuf()
}
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.
{

@ -4,7 +4,7 @@ static const uint MaxVolumes=65535;
// 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 @@ THREAD_PROC(RecThreadRS)
#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 @@ void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
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 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
}
void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
void RecVolumes5::Test(CommandData *Cmd,const wchar *Name)
{
wchar VolName[NM];
wcsncpyz(VolName,Name,ASIZE(VolName));

@ -90,18 +90,20 @@ Rijndael::Rijndael()
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 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe
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 @@ void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffe
}
#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 @@ void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer)
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];
@ -343,6 +392,41 @@ void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuf
#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
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ALGORITHM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@ -18,6 +18,16 @@ class Rijndael
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();

@ -215,10 +215,21 @@ bool ScanTree::GetNextMask()
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)

@ -56,7 +56,6 @@ static CryptLoader GlobalCryptLoader;
SecPassword::SecPassword()
{
CrossProcess=false;
Set(L"");
}
@ -70,7 +69,8 @@ SecPassword::~SecPassword()
void SecPassword::Clean()
{
PasswordSet=false;
cleandata(Password,sizeof(Password));
if (Password.size()>0)
cleandata(&Password[0],Password.size()*sizeof(Password[0]));
}
@ -104,7 +104,7 @@ void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstS
// Source string can be shorter than destination as in case when we process
// -p<pwd> 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 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize)
{
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::Get(wchar *Psw,size_t MaxSize)
void SecPassword::Set(const wchar *Psw)
{
if (*Psw==0)
{
PasswordSet=false;
memset(Password,0,sizeof(Password));
}
else
// Eliminate any traces of previously stored password for security reason
// in case it was longer than new one.
Clean();
if (*Psw!=0)
{
PasswordSet=true;
Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true);
Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true);
}
}
@ -142,7 +141,7 @@ size_t SecPassword::Length()
wchar Plain[MAXPASSWORD];
Get(Plain,ASIZE(Plain));
size_t Length=wcslen(Plain);
cleandata(Plain,ASIZE(Plain));
cleandata(Plain,sizeof(Plain));
return Length;
}
@ -157,12 +156,15 @@ bool SecPassword::operator == (SecPassword &psw)
Get(Plain1,ASIZE(Plain1));
psw.Get(Plain2,ASIZE(Plain2));
bool Result=wcscmp(Plain1,Plain2)==0;
cleandata(Plain1,ASIZE(Plain1));
cleandata(Plain2,ASIZE(Plain2));
cleandata(Plain1,sizeof(Plain1));
cleandata(Plain2,sizeof(Plain2));
return Result;
}
// Set CrossProcess to true if we need to pass a password to another process.
// We use CrossProcess when transferring parameters to UAC elevated WinRAR
// and Windows GUI SFX modules.
void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
{
// CryptProtectMemory is not available in UWP and CryptProtectData

@ -8,10 +8,7 @@ class SecPassword
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<wchar> Password = std::vector<wchar>(MAXPASSWORD);
bool PasswordSet;
public:
SecPassword();
@ -22,10 +19,6 @@ class SecPassword
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;
};

@ -357,6 +357,32 @@ void itoa(int64 n,wchar *Str,size_t MaxSize)
}
// 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<MaxSize)
{
if (S!=0 && (S+3-L)%3==0)
Str[D++]=ThSep;
Str[D++]=RawText[S++];
}
Str[D]=0;
}
const wchar* GetWide(const char *Src)
{
const size_t MaxLength=NM;

@ -42,6 +42,7 @@ int wcsnicompc(const wchar *s1,const wchar *s2,size_t n);
void itoa(int64 n,char *Str,size_t MaxSize);
void itoa(int64 n,wchar *Str,size_t MaxSize);
void fmtitoa(int64 n,wchar *Str,size_t MaxSize);
const wchar* GetWide(const char *Src);
const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize);
#ifndef RARDLL

@ -187,10 +187,10 @@ SSE_VERSION _SSE_Version=GetSSEVersion();
SSE_VERSION GetSSEVersion()
{
int CPUInfo[4];
__cpuid(CPUInfo, 0x80000000);
__cpuid(CPUInfo, 0);
// Maximum supported cpuid function. For example, Pentium M 755 returns 4 here.
uint MaxSupported=CPUInfo[0] & 0x7fffffff;
// Maximum supported cpuid function.
uint MaxSupported=CPUInfo[0];
if (MaxSupported>=7)
{

@ -149,3 +149,5 @@ uint GetNumberOfThreads()
return NumCPU;
}

@ -22,6 +22,17 @@ class RarTime
// 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.

@ -49,7 +49,7 @@ enum UIMESSAGE_CODE {
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 @@ enum UIASKREP_RESULT {
};
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 uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64
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 @@ class uiMsgStore
// 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<class T1> void uiMsg(UIMESSAGE_CODE Code,T1 a1)
template<class T1,class... TN> void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN)
{
uiMsgStore Store(Code);
// Process first parameter and pass the rest to same uiMsgBase.
Store<<a1;
Store.Msg();
uiMsgBase(Store,aN...);
}
template<class T1,class T2> void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2)
{
uiMsgStore Store(Code);
Store<<a1<<a2;
Store.Msg();
}
template<class T1,class T2,class T3> 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<class... TN> void uiMsg(UIMESSAGE_CODE Code,TN&&... aN)
{
uiMsgStore Store(code);
Store<<a1<<a2<<a3;
uiMsgStore Store(Code);
uiMsgBase(Store,aN...);
Store.Msg();
}

@ -8,7 +8,7 @@ void uiInit(SOUND_NOTIFY_MODE Sound)
// Additionally to handling user input, it analyzes and sets command options.
// Returns only 'replace', 'skip' and 'cancel' codes.
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)
{
if (Cmd->Overwrite==OVERWRITE_NONE)
return UIASKREP_R_SKIP;

@ -183,6 +183,7 @@ void uiMsgStore::Msg()
Log(NULL,St(MNeedAdmin));
break;
case UIERROR_ARCBROKEN:
mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN.
Log(Str[0],St(MErrBrokenArc));
break;
case UIERROR_HEADERBROKEN:
@ -262,6 +263,7 @@ void uiMsgStore::Msg()
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 +397,8 @@ void uiMsgStore::Msg()
}
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

@ -33,7 +33,8 @@ void uiMsgStore::Msg()
}
bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,
SecPassword *Password,CheckPassword *CheckPwd)
{
return false;
}

@ -70,7 +70,8 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize)
}
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 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w
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 @@ bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
// 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;

@ -229,10 +229,11 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
#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<SrcSize;I++,Src++)
for (size_t I=0;I<SrcSize && I*2+1<DestSize;I++,Src++)
{
Dest[I*2]=(byte)*Src;
Dest[I*2+1]=(byte)(*Src>>8);

@ -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);

@ -91,12 +91,6 @@ void Unpack::Init(size_t WinSize,bool Solid)
if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB.
return;
// Unrar does not support window size greather than 1GB at this time.
// Any request for a window larger than 1GB should be ignored.
const size_t MaxAllocSize=0x40000000;
if (WinSize>MaxAllocSize)
WinSize=MaxAllocSize;
// Archiving code guarantees that window size does not grow in the same
// solid stream. So if we are here, we are either creating a new window
// or increasing the size of non-solid window. So we could safely reject
@ -271,7 +265,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
Dec->DecodeLen[I]=(uint)LeftAligned;
// Every item of this array contains the sum of all preceding items.
// So it contains the start position in code list for every bit length.
// So it contains the start position in code list for every bit length.
Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1];
}
@ -315,7 +309,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
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;
}
@ -334,7 +328,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
uint BitField=Code<<(16-Dec->QuickBits);
// Prepare the table for quick decoding of bit lengths.
// Find the upper limit for current bit field and adjust the bit length
// accordingly if necessary.
while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength])

@ -93,17 +93,17 @@ struct UnpackBlockTables
#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.
};
};

@ -55,7 +55,7 @@ void Unpack::Unpack29(bool Solid)
if (!UnpReadBuf30())
break;
}
if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr)
if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr)
{
UnpWriteBuf30();
if (WrittenFileSize>DestUnpSize)

@ -42,7 +42,7 @@ void Unpack::Unpack5(bool Solid)
break;
}
if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
{
UnpWriteBuf();
if (WrittenFileSize>DestUnpSize)
@ -93,7 +93,7 @@ void Unpack::Unpack5(bool Solid)
}
else
{
Distance+=Inp.getbits32()>>(32-DBits);
Distance+=Inp.getbits()>>(16-DBits);
Inp.addbits(DBits);
}
}

@ -345,7 +345,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
if (D.DecodedSize>1)
{
UnpackDecodedItem *PrevItem=CurItem-1;
if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3)
if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<ASIZE(PrevItem->Literal)-1)
{
PrevItem->Length++;
PrevItem->Literal[PrevItem->Length]=(byte)MainSlot;
@ -388,7 +388,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
}
else
{
Distance+=D.Inp.getbits32()>>(32-DBits);
Distance+=D.Inp.getbits()>>(16-DBits);
D.Inp.addbits(DBits);
}
}
@ -451,7 +451,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
while (Item<Border)
{
UnpPtr&=MaxWinMask;
if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
{
UnpWriteBuf();
if (WrittenFileSize>DestUnpSize)
@ -461,10 +461,10 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
if (Item->Type==UNPDT_LITERAL)
{
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
if (Item->Length==3 && UnpPtr<MaxWinSize-4)
if (Item->Length==7 && UnpPtr<MaxWinSize-8)
{
*(uint32 *)(Window+UnpPtr)=*(uint32 *)Item->Literal;
UnpPtr+=4;
*(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal);
UnpPtr+=8;
}
else
#endif
@ -559,7 +559,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D)
break;
}
}
if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
{
UnpWriteBuf();
if (WrittenFileSize>DestUnpSize)

@ -1,56 +1,12 @@
void ExtractUnixOwner20(Archive &Arc,const wchar *FileName)
void ExtractUnixOwner30(Archive &Arc,const wchar *FileName)
{
char NameA[NM];
WideToChar(FileName,NameA,ASIZE(NameA));
if (Arc.BrokenHeader)
{
uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName);
ErrHandler.SetErrorCode(RARX_CRC);
if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL)
return;
}
struct passwd *pw;
errno=0; // Required by getpwnam specification if we need to check errno.
if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL)
{
uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName));
ErrHandler.SysErrMsg();
ErrHandler.SetErrorCode(RARX_WARNING);
return;
}
uid_t OwnerID=pw->pw_uid;
struct group *gr;
errno=0; // Required by getgrnam specification if we need to check errno.
if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL)
{
uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName));
ErrHandler.SysErrMsg();
ErrHandler.SetErrorCode(RARX_CRC);
return;
}
uint Attr=GetFileAttr(FileName);
gid_t GroupID=gr->gr_gid;
#if defined(SAVE_LINKS) && !defined(_APPLE)
if (lchown(NameA,OwnerID,GroupID)!=0)
#else
if (chown(NameA,OwnerID,GroupID)!=0)
#endif
{
uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName);
ErrHandler.SetErrorCode(RARX_CREATE);
}
SetFileAttr(FileName,Attr);
}
void ExtractUnixOwner30(Archive &Arc,const wchar *FileName)
{
char NameA[NM];
WideToChar(FileName,NameA,ASIZE(NameA));
char *OwnerName=(char *)&Arc.SubHead.SubData[0];
int OwnerSize=strlen(OwnerName)+1;

@ -1,6 +1,6 @@
#define RARVER_MAJOR 6
#define RARVER_MINOR 12
#define RARVER_MINOR 23
#define RARVER_BETA 0
#define RARVER_DAY 4
#define RARVER_MONTH 5
#define RARVER_YEAR 2022
#define RARVER_DAY 1
#define RARVER_MONTH 8
#define RARVER_YEAR 2023

@ -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 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma
#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 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
#ifdef RARDLL
bool DllVolNotify(RAROptions *Cmd,wchar *NextName)
bool DllVolNotify(CommandData *Cmd,wchar *NextName)
{
char NextNameA[NM];
WideToChar(NextName,NextNameA,ASIZE(NextNameA));

@ -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

@ -40,7 +40,7 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd)
PrivSet=true;
}
const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024;
const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024;
Array<byte> Buf(BufSize);
REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0];

@ -39,6 +39,7 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName)
CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName));
ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1);
wcsncatz(StreamName,StoredName,ASIZE(StreamName));
FindData fd;
@ -111,16 +112,23 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode)
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);

Loading…
Cancel
Save