Update to UnRAR v6.2.10

This commit is contained in:
Micah Snyder 2023-08-22 10:26:55 -07:00 committed by Micah Snyder
parent 95df41b5bf
commit 6e5db6eaaa
86 changed files with 1434 additions and 586 deletions

View file

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

View file

@ -3,15 +3,15 @@
#include "arccmt.cpp" #include "arccmt.cpp"
Archive::Archive(RAROptions *InitCmd) Archive::Archive(CommandData *InitCmd)
{ {
Cmd=NULL; // Just in case we'll have an exception in 'new' below. Cmd=NULL; // Just in case we'll have an exception in 'new' below.
DummyCmd=(InitCmd==NULL); DummyCmd=(InitCmd==NULL);
Cmd=DummyCmd ? (new RAROptions):InitCmd; Cmd=DummyCmd ? (new CommandData):InitCmd;
OpenShared=Cmd->OpenShared; OpenShared=Cmd->OpenShared;
Format=RARFMT15; Format=RARFMT_NONE;
Solid=false; Solid=false;
Volume=false; Volume=false;
MainComment=false; MainComment=false;
@ -31,9 +31,9 @@ Archive::Archive(RAROptions *InitCmd)
NextBlockPos=0; NextBlockPos=0;
memset(&MainHead,0,sizeof(MainHead)); MainHead.Reset();
memset(&CryptHead,0,sizeof(CryptHead)); CryptHead={};
memset(&EndArcHead,0,sizeof(EndArcHead)); EndArcHead.Reset();
VolNumber=0; VolNumber=0;
VolWrite=0; VolWrite=0;

View file

@ -32,8 +32,8 @@ class Archive:public File
size_t ReadHeader14(); size_t ReadHeader14();
size_t ReadHeader15(); size_t ReadHeader15();
size_t ReadHeader50(); size_t ReadHeader50();
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
void RequestArcPassword(); void RequestArcPassword(RarCheckPassword *SelPwd);
void UnexpEndArcMsg(); void UnexpEndArcMsg();
void BrokenHeaderMsg(); void BrokenHeaderMsg();
void UnkEncVerMsg(const wchar *Name,const wchar *Info); void UnkEncVerMsg(const wchar *Name,const wchar *Info);
@ -45,7 +45,7 @@ class Archive:public File
#endif #endif
ComprDataIO SubDataIO; ComprDataIO SubDataIO;
bool DummyCmd; bool DummyCmd;
RAROptions *Cmd; CommandData *Cmd;
RarTime LatestTime; RarTime LatestTime;
@ -58,7 +58,7 @@ class Archive:public File
bool ProhibitQOpen; bool ProhibitQOpen;
#endif #endif
public: public:
Archive(RAROptions *InitCmd=NULL); Archive(CommandData *InitCmd=NULL);
~Archive(); ~Archive();
static RARFORMAT IsSignature(const byte *D,size_t Size); static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken); bool IsArchive(bool EnableBroken);
@ -83,7 +83,7 @@ class Archive:public File
const wchar *Name,uint Flags); const wchar *Name,uint Flags);
bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode); bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode);
HEADER_TYPE GetHeaderType() {return CurHeaderType;} HEADER_TYPE GetHeaderType() {return CurHeaderType;}
RAROptions* GetRAROptions() {return Cmd;} CommandData* GetCommandData() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
#if 0 #if 0
void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
@ -107,7 +107,6 @@ class Archive:public File
FileHeader SubHead; FileHeader SubHead;
CommentHeader CommHead; CommentHeader CommHead;
ProtectHeader ProtectHead; ProtectHeader ProtectHead;
UnixOwnersHeader UOHead;
EAHeader EAHead; EAHeader EAHead;
StreamHeader StreamHead; StreamHeader StreamHead;

View file

@ -100,6 +100,9 @@ void Archive::UnexpEndArcMsg()
// If block positions are equal to file size, this is not an error. // 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, // It can happen when we reached the end of older RAR 1.5 archive,
// which did not have the end of archive block. // 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) if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{ {
uiMsg(UIERROR_UNEXPEOF,FileName); uiMsg(UIERROR_UNEXPEOF,FileName);
@ -145,7 +148,7 @@ size_t Archive::ReadHeader15()
#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. #ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
return 0; return 0;
#else #else
RequestArcPassword(); RequestArcPassword(NULL);
byte Salt[SIZE_SALT30]; byte Salt[SIZE_SALT30];
if (Read(Salt,SIZE_SALT30)!=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->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
hd->SaltSet=(hd->Flags & LHD_SALT)!=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->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
@ -469,19 +476,6 @@ size_t Archive::ReadHeader15()
SubBlockHead.Level=Raw.Get1(); SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType) 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: case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead; *(SubBlockHeader *)&EAHead=SubBlockHead;
EAHead.UnpSize=Raw.Get4(); EAHead.UnpSize=Raw.Get4();
@ -513,8 +507,12 @@ size_t Archive::ReadHeader15()
ushort HeaderCRC=Raw.GetCRC15(false); ushort HeaderCRC=Raw.GetCRC15(false);
// Old AV header does not have header CRC properly set. // 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 && 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; bool Recovered=false;
if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) 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. // in -p<pwd> to not stop batch processing for encrypted archives.
bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); 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. while (true) // Repeat the password prompt for wrong passwords.
{ {
RequestArcPassword(); RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL);
byte PswCheck[SIZE_PSWCHECK]; byte PswCheck[SIZE_PSWCHECK];
HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
// Verify password validity. // Verify password validity. If header is damaged, we cannot rely on
if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) // 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. if (GlobalPassword) // For -p<pwd> or Ctrl+P.
{ {
@ -850,8 +854,6 @@ size_t Archive::ReadHeader50()
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
char FileName[NM*4]; char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize); Raw.GetB((byte *)FileName,ReadNameSize);
@ -875,25 +877,6 @@ size_t Archive::ReadHeader50()
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true; 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. if (BadCRC) // Add the file name to broken header message displayed above.
uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
@ -916,7 +899,7 @@ size_t Archive::ReadHeader50()
#if !defined(RAR_NOCRYPT) #if !defined(RAR_NOCRYPT)
void Archive::RequestArcPassword() void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
{ {
if (!Cmd->Password.IsSet()) if (!Cmd->Password.IsSet())
{ {
@ -946,7 +929,7 @@ void Archive::RequestArcPassword()
ErrHandler.Exit(RARX_USERBREAK); ErrHandler.Exit(RARX_USERBREAK);
} }
#else #else
if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd))
{ {
Close(); Close();
uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
@ -959,7 +942,7 @@ void Archive::RequestArcPassword()
#endif #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. // Read extra data from the end of block skipping any fields before it.
size_t ExtraStart=Raw->Size()-ExtraSize; 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) if (bb->HeaderType==HEAD_MAIN)
{ {
MainHeader *hd=(MainHeader *)bb; MainHeader *hd=(MainHeader *)bb;
if (FieldType==MHEXTRA_LOCATOR) switch(FieldType)
{ {
hd->Locator=true; case MHEXTRA_LOCATOR:
uint Flags=(uint)Raw->GetV(); {
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) hd->Locator=true;
{ uint Flags=(uint)Raw->GetV();
uint64 Offset=Raw->GetV(); if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
if (Offset!=0) // 0 means that reserved space was not enough to write the offset. {
hd->QOpenOffset=Offset+CurBlockPos; uint64 Offset=Raw->GetV();
} if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
if ((Flags & MHEXTRA_LOCATOR_RR)!=0) hd->QOpenOffset=Offset+CurBlockPos;
{ }
uint64 Offset=Raw->GetV(); if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
if (Offset!=0) // 0 means that reserved space was not enough to write the offset. {
hd->RROffset=Offset+CurBlockPos; 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) 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); uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false; return false;
} }

View file

@ -10,7 +10,6 @@ template <class T> class Array
size_t BufSize; size_t BufSize;
size_t AllocSize; size_t AllocSize;
size_t MaxSize; size_t MaxSize;
bool Secure; // Clean memory if true.
public: public:
Array(); Array();
Array(size_t Size); Array(size_t Size);
@ -24,14 +23,13 @@ template <class T> class Array
void Alloc(size_t Items); void Alloc(size_t Items);
void Reset(); void Reset();
void SoftReset(); void SoftReset();
void operator = (Array<T> &Src); Array<T>& operator = (const Array<T> &Src);
void Push(T Item); void Push(T Item);
void Append(T *Item,size_t Count); void Append(T *Item,size_t Count);
T* Addr(size_t Item) {return Buffer+Item;} T* Addr(size_t Item) {return Buffer+Item;}
void SetMaxSize(size_t Size) {MaxSize=Size;} void SetMaxSize(size_t Size) {MaxSize=Size;}
T* Begin() {return Buffer;} T* Begin() {return Buffer;}
T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} 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; BufSize=0;
AllocSize=0; AllocSize=0;
MaxSize=0; MaxSize=0;
Secure=false;
} }
@ -71,11 +68,7 @@ template <class T> Array<T>::Array(const Array &Src)
template <class T> Array<T>::~Array() template <class T> Array<T>::~Array()
{ {
if (Buffer!=NULL) if (Buffer!=NULL)
{
if (Secure)
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer); 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 Suggested=AllocSize+AllocSize/4+32;
size_t NewSize=Max(BufSize,Suggested); size_t NewSize=Max(BufSize,Suggested);
T *NewBuffer; T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
if (Secure) if (NewBuffer==NULL)
{ ErrHandler.MemoryError();
NewBuffer=(T *)malloc(NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
if (Buffer!=NULL)
{
memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
else
{
NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
}
Buffer=NewBuffer; Buffer=NewBuffer;
AllocSize=NewSize; 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(); Reset();
Alloc(Src.BufSize); Alloc(Src.BufSize);
if (Src.BufSize!=0) if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
return *this;
} }

View file

@ -20,10 +20,15 @@ enum blake2s_constant
// 'new' operator. // 'new' operator.
struct blake2s_state 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]; // 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]; byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];

View file

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

View file

@ -2,7 +2,7 @@
#define _RAR_CMDDATA_ #define _RAR_CMDDATA_
#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" #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}; enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
@ -65,6 +65,10 @@ class CommandData:public RAROptions
StringList InclArgs; StringList InclArgs;
StringList ArcNames; StringList ArcNames;
StringList StoreArgs; StringList StoreArgs;
SecPassword Password;
std::vector<int64> NextVolSizes;
}; };
#endif #endif

View file

@ -289,8 +289,8 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy
return 0; return 0;
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
return 0; return 0;
if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
FileHead.Dir && !InclDir)) (!FileHead.Dir || !InclDir))
return 0; return 0;
if (!Dir && SizeCheck(FileHead.UnpSize)) if (!Dir && SizeCheck(FileHead.UnpSize))
return 0; return 0;

View file

@ -92,6 +92,13 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
if (Found) if (Found)
continue; continue;
#endif #endif
#ifdef _UNIX
if (CmpMSGID(Help[I],MRARTitle2))
{
mprintf(St(MFwrSlTitle2));
continue;
}
#endif
#if !defined(_UNIX) && !defined(_WIN_ALL) #if !defined(_UNIX) && !defined(_WIN_ALL)
if (CmpMSGID(Help[I],MCHelpSwOW)) if (CmpMSGID(Help[I],MCHelpSwOW))
continue; continue;

View file

@ -17,6 +17,7 @@ class PackDef
static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; 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_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 LOW_DIST_REP_COUNT = 16;
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */

View file

@ -110,3 +110,164 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
#endif #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

View file

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

View file

@ -11,21 +11,12 @@
CryptData::CryptData() CryptData::CryptData()
{ {
Method=CRYPT_NONE; Method=CRYPT_NONE;
memset(KDF3Cache,0,sizeof(KDF3Cache));
memset(KDF5Cache,0,sizeof(KDF5Cache));
KDF3CachePos=0; KDF3CachePos=0;
KDF5CachePos=0; KDF5CachePos=0;
memset(CRCTab,0,sizeof(CRCTab)); memset(CRCTab,0,sizeof(CRCTab));
} }
CryptData::~CryptData()
{
cleandata(KDF3Cache,sizeof(KDF3Cache));
cleandata(KDF5Cache,sizeof(KDF5Cache));
}
void CryptData::DecryptBlock(byte *Buf,size_t Size) 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, SecPassword *Password,const byte *Salt,
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck) const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{ {
if (!Password->IsSet() || Method==CRYPT_NONE) if (Method==CRYPT_NONE || !Password->IsSet())
return false; return false;
CryptData::Method=Method; CryptData::Method=Method;
wchar PwdW[MAXPASSWORD]; wchar PwdW[MAXPASSWORD];
Password->Get(PwdW,ASIZE(PwdW)); Password->Get(PwdW,ASIZE(PwdW));
PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
char PwdA[MAXPASSWORD]; char PwdA[MAXPASSWORD];
WideToChar(PwdW,PwdA,ASIZE(PwdA)); WideToChar(PwdW,PwdA,ASIZE(PwdA));
PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
switch(Method) switch(Method)
{ {

View file

@ -30,6 +30,18 @@ class CryptData
uint Lg2Count; // Log2 of PBKDF2 repetition count. uint Lg2Count; // Log2 of PBKDF2 repetition count.
byte PswCheckValue[SHA256_DIGEST_SIZE]; byte PswCheckValue[SHA256_DIGEST_SIZE];
byte HashKeyValue[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 struct KDF3CacheItem
@ -39,6 +51,17 @@ class CryptData
byte Key[16]; byte Key[16];
byte Init[16]; byte Init[16];
bool SaltPresent; 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]; ushort Key15[4];
public: public:
CryptData(); CryptData();
~CryptData();
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt, const byte *Salt,const byte *InitV,uint Lg2Cnt,
byte *HashKey,byte *PswCheck); byte *HashKey,byte *PswCheck);
@ -88,6 +110,54 @@ class CryptData
static void SetSalt(byte *Salt,size_t SaltSize); 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 GetRnd(byte *RndBuf,size_t BufSize);
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,

View file

@ -18,8 +18,9 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co
if (!Cached) if (!Cached)
{ {
byte RawPsw[2*MAXPASSWORD+SIZE_SALT30]; byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
WideToRaw(PwdW,RawPsw,ASIZE(RawPsw)); size_t PswLength=wcslen(PwdW);
size_t RawLength=2*wcslen(PwdW); size_t RawLength=2*PswLength;
WideToRaw(PwdW,PswLength,RawPsw,RawLength);
if (Salt!=NULL) if (Salt!=NULL)
{ {
memcpy(RawPsw+RawLength,Salt,SIZE_SALT30); memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);

View file

@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
sha256_context ICtx; sha256_context ICtx;
if (ICtxOpt!=NULL && *SetIOpt) if (ICtxOpt!=NULL && *SetIOpt)
ICtx=*ICtxOpt; // Use already calculated first block context. ICtx=*ICtxOpt; // Use already calculated the first block context.
else else
{ {
// This calculation is the same for all iterations with same password. // 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]; byte SaltData[MaxSalt+4];
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. SaltData[SaltLength + 0] = 0; // Block index appened to salt.
SaltData[SaltLength + 1] = 0; SaltData[SaltLength + 1] = 0; //
SaltData[SaltLength + 2] = 0; SaltData[SaltLength + 2] = 0; // Since we do not request the key width
SaltData[SaltLength + 3] = 1; SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
// First iteration: HMAC of password, salt and block index (1). // First iteration: HMAC of password, salt and block index (1).
byte U1[SHA256_DIGEST_SIZE]; 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++) for (uint I=0;I<ASIZE(KDF5Cache);I++)
{ {
KDF5CacheItem *Item=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) memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{ {
memcpy(Key,Item->Key,sizeof(Key)); memcpy(Key,Item->Key,sizeof(Key));

View file

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

View file

@ -2,8 +2,8 @@
#include <commctrl.h> #include <commctrl.h>
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 6, 12, 100, 489 FILEVERSION 6, 23, 100, 944
PRODUCTVERSION 6, 12, 100, 489 PRODUCTVERSION 6, 23, 100, 944
FILEOS VOS__WINDOWS32 FILEOS VOS__WINDOWS32
FILETYPE VFT_APP FILETYPE VFT_APP
{ {
@ -14,9 +14,9 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0" VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0" VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0"
VALUE "FileVersion", "6.12.0\0" VALUE "FileVersion", "6.23.0\0"
VALUE "ProductVersion", "6.12.0\0" VALUE "ProductVersion", "6.23.0\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0"
VALUE "OriginalFilename", "Unrar.dll\0" VALUE "OriginalFilename", "Unrar.dll\0"
} }
} }

View file

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

View file

@ -21,17 +21,11 @@
#ifndef SFX_MODULE #ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{ {
#ifdef _WIN_ALL
if (Cmd->Test) if (Cmd->Test)
return; return;
switch(Arc.SubBlockHead.SubType) switch(Arc.SubBlockHead.SubType)
{ {
#ifdef _UNIX
case UO_HEAD:
if (Cmd->ProcessOwners)
ExtractUnixOwner20(Arc,Name);
break;
#endif
#ifdef _WIN_ALL
case NTACL_HEAD: case NTACL_HEAD:
if (Cmd->ProcessOwners) if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name); ExtractACL20(Arc,Name);
@ -39,8 +33,8 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
case STREAM_HEAD: case STREAM_HEAD:
ExtractStreams20(Arc,Name); ExtractStreams20(Arc,Name);
break; break;
#endif
} }
#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) bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
{ {
// Catch root dir based /path/file paths also as stuff like \\?\. // 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++; UpLevels++;
TargetName++; TargetName++;
} }
// If link target includes "..", it must not have another links // If link target includes "..", it must not have another links in its
// in the path, because they can bypass our safety check. For example, // source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next // 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)) if (UpLevels>0 && LinkInPath(PrepSrcName))
return false; 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) #if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data. // For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15) if (Arc.Format==RARFMT15)
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink);
if (Arc.Format==RARFMT50) if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); 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 // 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. // no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50) if (Arc.Format==RARFMT50)

View file

@ -1,8 +1,9 @@
#ifndef _RAR_EXTINFO_ #ifndef _RAR_EXTINFO_
#define _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 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 #ifdef _UNIX
void SetUnixOwner(Archive &Arc,const wchar *FileName); void SetUnixOwner(Archive &Arc,const wchar *FileName);
#endif #endif

View file

@ -5,10 +5,30 @@ CmdExtract::CmdExtract(CommandData *Cmd)
CmdExtract::Cmd=Cmd; CmdExtract::Cmd=Cmd;
*ArcName=0; *ArcName=0;
*DestFileName=0; *DestFileName=0;
ArcAnalyzed=false;
Analyze=new AnalyzeData;
memset(Analyze,0,sizeof(*Analyze));
TotalFileCount=0; 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); Unp=new Unpack(&DataIO);
#ifdef RAR_SMP #ifdef RAR_SMP
Unp->SetThreads(Cmd->Threads); Unp->SetThreads(Cmd->Threads);
@ -18,7 +38,26 @@ CmdExtract::CmdExtract(CommandData *Cmd)
CmdExtract::~CmdExtract() CmdExtract::~CmdExtract()
{ {
FreeAnalyzeData();
delete Unp; 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; PasswordCancelled=false;
DataIO.SetCurrentCommand(Cmd->Command[0]); DataIO.SetCurrentCommand(Cmd->Command[0]);
FindData FD; if (*Cmd->UseStdin==0)
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) {
if (FindFile::FastFind(ArcName,&FD)) FindData FD;
DataIO.TotalArcSize+=FD.Size; while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
if (FindFile::FastFind(ArcName,&FD))
DataIO.TotalArcSize+=FD.Size;
}
Cmd->ArcNames.Rewind(); Cmd->ArcNames.Rewind();
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
@ -97,7 +139,11 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
AllMatchesExact=true; AllMatchesExact=true;
AnySolidDataUnpackedWell=false; AnySolidDataUnpackedWell=false;
ArcAnalyzed=false;
StartTime.SetCurrentTime(); StartTime.SetCurrentTime();
LastCheckedSymlink.clear();
} }
@ -168,20 +214,33 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
} }
#endif #endif
Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT.
int64 VolumeSetSize=0; // Total size of volumes after the current volume. 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) if (Arc.Volume)
{ {
#ifndef SFX_MODULE #ifndef SFX_MODULE
// Try to speed up extraction for independent solid volumes by starting // Try to speed up extraction for independent solid volumes by starting
// extraction from non-first volume if we can. // 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; UseExactVolName=true;
return EXTRACT_ARC_REPEAT; return EXTRACT_ARC_REPEAT;
} }
#endif #endif
// Calculate the total size of all accessible volumes. // Calculate the total size of all accessible volumes.
// This size is necessary to display the correct total progress indicator. // This size is necessary to display the correct total progress indicator.
@ -216,7 +275,13 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
else else
uiStartArchiveExtract(!Cmd->Test,ArcName); 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) while (1)
@ -272,7 +337,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
return false; return false;
HEADER_TYPE HeaderType=Arc.GetHeaderType(); 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 #ifndef SFX_MODULE
if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) 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) if (Arc.FileHead.UnpSize<0)
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) if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
return false; return false;
@ -413,13 +488,39 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
FirstFile=false; FirstFile=false;
#endif #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.FileHead.Encrypted && Cmd->SkipEncrypted)
if (Arc.Solid) if (Arc.Solid)
return false; // Abort the entire extraction for solid archive. return false; // Abort the entire extraction for solid archive.
else else
MatchFound=false; // Skip only the current file for non-solid archive. 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 // First common call of uiStartFileExtract. It is done before overwrite
// prompts, so if SkipSolid state is changed below, we'll need to make // 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)) if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
return false; 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. // DestFileName can be set empty in case of excessive -ap switch.
ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; 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. 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. // Stop archive extracting if user cancelled a password prompt.
#ifdef RARDLL #ifdef RARDLL
@ -476,77 +582,83 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
return false; return false;
} }
#else #else
if (!ExtrGetPassword(Arc,ArcFileName)) if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL))
{ {
PasswordCancelled=true; PasswordCancelled=true;
return false; return false;
} }
#endif #endif
}
// Set a password before creating the file, so we can skip creating // Set a password before creating the file, so we can skip creating
// in case of wrong password. // in case of wrong password.
SecPassword FilePassword=Cmd->Password; SecPassword FilePassword=Cmd->Password;
#if defined(_WIN_ALL) && !defined(SFX_MODULE) #if defined(_WIN_ALL) && !defined(SFX_MODULE)
ConvertDosPassword(Arc,FilePassword); ConvertDosPassword(Arc,FilePassword);
#endif #endif
byte PswCheck[SIZE_PSWCHECK]; byte PswCheck[SIZE_PSWCHECK];
DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
Arc.FileHead.InitV,Arc.FileHead.Lg2Count, Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
Arc.FileHead.HashKey,PswCheck); Arc.FileHead.HashKey,PswCheck);
// If header is damaged, we cannot rely on password check value, // If header is damaged, we cannot rely on password check value,
// because it can be damaged too. // because it can be damaged too.
if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader &&
memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0)
!Arc.BrokenHeader)
{
if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
{ {
// This message is used by Android GUI to reset cached passwords. if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
// Update appropriate code if changed. {
uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); // This message is used by Android GUI to reset cached passwords.
} // Update appropriate code if changed.
else // For passwords entered manually. uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
{ }
// This message is used by Android GUI and Windows GUI and SFX to else // For passwords entered manually.
// reset cached passwords. Update appropriate code if changed. {
uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); // This message is used by Android GUI and Windows GUI and SFX to
Cmd->Password.Clean(); // 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 // Avoid new requests for unrar.dll to prevent the infinite loop
// if app always returns the same password. // if app always returns the same password.
#ifndef RARDLL #ifndef RARDLL
continue; // Request a password again. continue; // Request a password again.
#endif #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 break;
// If we already have ERAR_EOPEN as result of missing volume,
// we should not replace it with less precise ERAR_BAD_PASSWORD.
if (Cmd->DllError!=ERAR_EOPEN)
Cmd->DllError=ERAR_BAD_PASSWORD;
#endif
ErrHandler.SetErrorCode(RARX_BADPWD);
ExtrFile=false;
} }
break;
} }
else
DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL);
#ifdef RARDLL #ifdef RARDLL
if (*Cmd->DllDestName!=0) if (*Cmd->DllDestName!=0)
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
#endif #endif
if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks &&
ConvertSymlinkPaths)
ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
File CurFile; File CurFile;
bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; 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) 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; bool UserReject=false;
if (FileExist(DestFileName) && !UserReject) if (FileExist(DestFileName) && !UserReject)
FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); 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) if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
{ {
wchar RedirName[NM]; 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]; wchar NameExisting[NM];
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
@ -675,17 +797,37 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_HARDLINK) if (Type==FSREDIR_HARDLINK)
LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting));
else else
LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize);
} }
else else
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
{ {
if (FileCreateMode) 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 else
{ {
uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName); uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName);
LinkSuccess=false; 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->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
Unp->SetDestSize(Arc.FileHead.UnpSize); Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE #ifndef SFX_MODULE
// RAR 1.3 - 1.5 archives do not set per file solid flag.
if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
Unp->DoUnpack(15,FileCount>1 && Arc.Solid); Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
else 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; File Existing;
if (!Existing.WOpen(NameExisting)) if (!Existing.Open(NameExisting))
{ {
uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); bool OpenFailed=true;
uiMsg(UIERROR_FILECOPYHINT,ArcName); // 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 #ifdef RARDLL
Cmd->DllError=ERAR_EREFERENCE; Cmd->DllError=ERAR_EREFERENCE;
#endif #endif
return false; return false;
}
} }
Array<char> Buffer(0x100000); Array<byte> Buffer(0x100000);
int64 CopySize=0; int64 CopySize=0;
while (true) while (true)
@ -890,6 +1075,10 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *
int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
if (ReadSize==0) if (ReadSize==0)
break; 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); New.Write(&Buffer[0],ReadSize);
CopySize+=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) 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); wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
if (*Cmd->ExtrPath!=0) if (*Cmd->ExtrPath!=0)
@ -1033,11 +1232,11 @@ bool CmdExtract::ExtrDllGetPassword()
#ifndef RARDLL #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 (!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. // Suppress "test is ok" message if user cancelled the password prompt.
uiMsg(UIERROR_INCERRCOUNT); uiMsg(UIERROR_INCERRCOUNT);
@ -1055,7 +1254,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
case -1: case -1:
ErrHandler.Exit(RARX_USERBREAK); ErrHandler.Exit(RARX_USERBREAK);
case 2: case 2:
if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd))
return false; return false;
break; break;
case 3: case 3:
@ -1131,6 +1330,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
if (!DirExist) if (!DirExist)
{ {
if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames); CreatePath(DestFileName,true,Cmd->DisableNames);
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
} }
@ -1212,6 +1413,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
MakeNameUsable(DestFileName,true); MakeNameUsable(DestFileName,true);
if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames); CreatePath(DestFileName,true,Cmd->DisableNames);
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) 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 #ifndef SFX_MODULE
// To speed up solid volumes extraction, try to find a non-first start volume, // Find non-matched reference sources in solid and non-solid archives.
// which still allows to unpack all files. It is possible for independent // Detect the optimal start position for semi-solid archives
// solid volumes with solid statistics reset in the beginning. // and optimal start volume for independent solid volumes.
bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) //
// 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(); wchar *ArgName=Cmd->FileArgs.GetString();
Cmd->FileArgs.Rewind(); Cmd->FileArgs.Rewind();
if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) 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. // Start search from first volume if all volumes preceding current are available.
wchar NextName[NM]; 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; bool FirstVolume=true;
while (!Matched)
// 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); 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; break;
}
bool OpenNext=false; OpenNext=false;
while (Arc.ReadHeader()>0) while (Arc.ReadHeader()>0)
{ {
Wait(); Wait();
@ -1295,17 +1526,88 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
} }
if (HeaderType==HEAD_FILE) 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.SplitBefore)
{ {
if (!Arc.FileHead.Solid) // Can start extraction from here. if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here.
wcsncpyz(StartName,NextName,ASIZE(StartName)); {
// 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) if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0)
{ {
Matched=true; // First matched file found, must stop further scan. MatchFound = true;
break; 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) if (Arc.FileHead.SplitAfter)
{ {
OpenNext=true; // Allow open next volume. OpenNext=true; // Allow open next volume.
@ -1316,16 +1618,25 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
} }
Arc.Close(); Arc.Close();
if (!OpenNext) if (Volume && OpenNext)
break; {
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. // If file references are present, we can't reliably skip in semi-solid
wcsncpyz(ArcName,StartName,ASIZE(ArcName)); // archives, because reference source can be present in skipped data.
if (RefList.Size()!=0)
return NewStartFound; memset(Analyze,0,sizeof(*Analyze));
} }
#endif #endif

View file

@ -6,13 +6,32 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract class CmdExtract
{ {
private: 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(); 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); void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
#ifdef RARDLL #ifdef RARDLL
bool ExtrDllGetPassword(); bool ExtrDllGetPassword();
#else #else
bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd);
#endif #endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE) #if defined(_WIN_ALL) && !defined(SFX_MODULE)
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
@ -21,7 +40,7 @@ class CmdExtract
bool ExtrCreateFile(Archive &Arc,File &CurFile); bool ExtrCreateFile(Archive &Arc,File &CurFile);
bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
#ifndef SFX_MODULE #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); void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize);
#endif #endif
@ -52,6 +71,15 @@ class CmdExtract
bool PrevProcessed; // If previous file was successfully extracted or tested. bool PrevProcessed; // If previous file was successfully extracted or tested.
wchar DestFileName[NM]; wchar DestFileName[NM];
bool PasswordCancelled; 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) #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32; bool Fat32,NotFat32;
#endif #endif

View file

@ -3,7 +3,7 @@
// If NewFile==NULL, we delete created file after user confirmation. // If NewFile==NULL, we delete created file after user confirmation.
// It is useful if we need to overwrite an existing folder or file, // It is useful if we need to overwrite an existing folder or file,
// but need user confirmation for that. // 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) bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{ {
if (UserReject!=NULL) if (UserReject!=NULL)

View file

@ -1,7 +1,7 @@
#ifndef _RAR_FILECREATE_ #ifndef _RAR_FILECREATE_
#define _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, bool *UserReject,int64 FileSize=INT64NDF,
RarTime *FileTime=NULL,bool WriteOnly=false); RarTime *FileTime=NULL,bool WriteOnly=false);

View file

@ -522,29 +522,35 @@ bool File::RawSeek(int64 Offset,int Method)
{ {
if (hFile==FILE_BAD_HANDLE) if (hFile==FILE_BAD_HANDLE)
return true; return true;
if (!IsSeekable()) if (!IsSeekable()) // To extract archives from stdin with -si.
{ {
if (Method==SEEK_CUR) // We tried to dynamically allocate 32 KB buffer here, but it improved
// speed in Windows 10 by mere ~1.5%.
byte Buf[4096];
if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
{ {
Offset+=CurFilePos; uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
Method=SEEK_SET; while (SkipSize>0) // Reading to emulate seek forward.
}
if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward.
{
uint64 SkipSize=Offset-CurFilePos;
while (SkipSize>0)
{ {
byte Buf[4096];
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
if (ReadSize<=0) if (ReadSize<=0)
return false; return false;
SkipSize-=ReadSize; 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 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) 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 UNIX_TIME_NS
#if defined(_APPLE)
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
#else
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
#endif
#else
if (ftm!=NULL) ftm->SetUnix(st.st_mtime);
if (ftc!=NULL) ftc->SetUnix(st.st_ctime);
if (fta!=NULL) fta->SetUnix(st.st_atime);
#endif
}
#endif
void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{ {
#ifdef _WIN_ALL #ifdef _WIN_ALL
FILETIME FileTime; FILETIME ctime,atime,mtime;
GetFileTime(hFile,NULL,NULL,&FileTime); GetFileTime(hFile,&ctime,&atime,&mtime);
ft->SetWinFT(&FileTime); if (ftm!=NULL) ftm->SetWinFT(&mtime);
#endif if (ftc!=NULL) ftc->SetWinFT(&ctime);
#if defined(_UNIX) || defined(_EMX) if (fta!=NULL) fta->SetWinFT(&atime);
#elif defined(_UNIX)
struct stat st; struct stat st;
fstat(GetFD(),&st); fstat(GetFD(),&st);
ft->SetUnix(st.st_mtime); StatToRarTime(st,ftm,ftc,fta);
#endif #endif
} }

View file

@ -14,8 +14,6 @@
#define FILE_BAD_HANDLE NULL #define FILE_BAD_HANDLE NULL
#endif #endif
class RAROptions;
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
@ -88,6 +86,9 @@ class File
wchar FileName[NM]; wchar FileName[NM];
FILE_ERRORTYPE ErrorType; FILE_ERRORTYPE ErrorType;
byte *SeekBuf; // To read instead of seek for stdin files.
static const size_t SeekBufSize=0x10000;
public: public:
File(); File();
virtual ~File(); virtual ~File();
@ -118,7 +119,10 @@ class File
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); 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. virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
int64 FileLength(); int64 FileLength();
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}

View file

@ -320,7 +320,6 @@ bool SetFileAttr(const wchar *Name,uint Attr)
} }
#if 0
wchar *MkTemp(wchar *Name,size_t MaxSize) wchar *MkTemp(wchar *Name,size_t MaxSize)
{ {
size_t Length=wcslen(Name); size_t Length=wcslen(Name);
@ -354,7 +353,6 @@ wchar *MkTemp(wchar *Name,size_t MaxSize)
} }
return Name; return Name;
} }
#endif
#if !defined(SFX_MODULE) #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) if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
{ {
// Update only the current file progress in WinRAR, set the total to 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); uiExtractProgress(TotalRead,FileLength,0,0);
} }
else else

View file

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

View file

@ -117,7 +117,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
if (hFind==INVALID_HANDLE_VALUE) if (hFind==INVALID_HANDLE_VALUE)
return false; return false;
FindClose(hFind); FindClose(hFind);
#else #elif defined(_UNIX)
char FindMaskA[NM]; char FindMaskA[NM];
WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); 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->FileAttr=st.st_mode;
fd->Size=st.st_size; fd->Size=st.st_size;
#ifdef UNIX_TIME_NS File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
#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
wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
#endif #endif

View file

@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer)
ExternalBuffer=false; ExternalBuffer=false;
if (AllocBuffer) if (AllocBuffer)
{ {
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions. // getbits*() attempt to read data from InAddr, ... InAddr+4 positions.
// So let's allocate 3 additional bytes for situation, when we need to // 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 // 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. // from access to next 4 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+3; size_t BufSize=MAX_SIZE+4;
InBuf=new byte[BufSize]; InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area // Ensure that we get predictable results when accessing bytes in area

View file

@ -28,26 +28,38 @@ class BitInput
InAddr+=Bits>>3; InAddr+=Bits>>3;
InBit=Bits&7; InBit=Bits&7;
} }
// Return 16 bits from current position in the buffer. // Return 16 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data. // Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits() 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; uint BitField=(uint)InBuf[InAddr] << 16;
BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2]; BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit); BitField >>= (8-InBit);
#endif
return BitField & 0xffff; return BitField & 0xffff;
} }
// Return 32 bits from current position in the buffer. // Return 32 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data. // Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits32() uint getbits32()
{ {
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
uint32 BitField=*(uint32*)(InBuf+InAddr);
BitField=ByteSwap32(BitField);
#else
uint BitField=(uint)InBuf[InAddr] << 24; uint BitField=(uint)InBuf[InAddr] << 24;
BitField|=(uint)InBuf[InAddr+1] << 16; BitField|=(uint)InBuf[InAddr+1] << 16;
BitField|=(uint)InBuf[InAddr+2] << 8; BitField|=(uint)InBuf[InAddr+2] << 8;
BitField|=(uint)InBuf[InAddr+3]; BitField|=(uint)InBuf[InAddr+3];
#endif
BitField <<= InBit; BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
return BitField & 0xffffffff; return BitField & 0xffffffff;

View file

@ -1,7 +1,5 @@
bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) 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)) if (!FileExist(NameExisting))
{ {
uiMsg(UIERROR_HLINKCREATE,NameNew); uiMsg(UIERROR_HLINKCREATE,NameNew);

View file

@ -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) if (Type==HASH_NONE || cmp.Type==HASH_NONE)
return true; return true;

View file

@ -6,8 +6,14 @@ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
struct HashValue struct HashValue
{ {
void Init(HASH_TYPE Type); 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; HASH_TYPE Type;
union union

View file

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

View file

@ -6,12 +6,11 @@
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. #define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. #define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 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_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13 #define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26 #define SIZEOF_PROTECTHEAD 26
#define SIZEOF_UOHEAD 18
#define SIZEOF_STREAMHEAD 26 #define SIZEOF_STREAMHEAD 26
#define VER_PACK 29U #define VER_PACK 29U
@ -162,12 +161,16 @@ struct MainHeader:BaseBlock
ushort HighPosAV; ushort HighPosAV;
uint PosAV; uint PosAV;
bool CommentInHeader; bool CommentInHeader;
bool PackComment; // For RAR 1.4 archive format only. bool PackComment; // For RAR 1.4 archive format only.
bool Locator; bool Locator;
uint64 QOpenOffset; // Offset of quick list record. uint64 QOpenOffset; // Offset of quick list record.
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
uint64 RROffset; // Offset of recovery record. uint64 RROffset; // Offset of recovery record.
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. 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(); void Reset();
}; };
@ -230,7 +233,7 @@ struct FileHeader:BlockHeader
bool LargeFile; bool LargeFile;
// 'true' for HEAD_SERVICE block, which is a child of preceding file block. // '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; bool SubBlock;
HOST_SYSTEM_TYPE HSType; 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 struct EAHeader:SubBlockHeader
{ {
uint UnpSize; uint UnpSize;

View file

@ -59,11 +59,18 @@
// Main header extra field values. // Main header extra field values.
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
#define MHEXTRA_METADATA 0x02 // Archive metadata.
// Flags for MHEXTRA_LOCATOR. // Flags for MHEXTRA_LOCATOR.
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. #define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record 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. // File and service header extra field values.
#define FHEXTRA_CRYPT 0x01 // Encryption parameters. #define FHEXTRA_CRYPT 0x01 // Encryption parameters.
#define FHEXTRA_HASH 0x02 // File hash. #define FHEXTRA_HASH 0x02 // File hash.

View file

@ -40,7 +40,7 @@ static bool WMI_IsWindows10()
IWbemServices *pSvc = NULL; 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)) if (FAILED(hres))
{ {

View file

@ -36,6 +36,7 @@ void ListArchive(CommandData *Cmd)
{ {
Arc.ViewComment(); Arc.ViewComment();
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
mprintf(L"\n%s: ",St(MListDetails)); mprintf(L"\n%s: ",St(MListDetails));
uint SetCount=0; uint SetCount=0;
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); 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)); mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
if (Arc.Encrypted) if (Arc.Encrypted)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); 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"); mprintf(L"\n");
} }

View file

@ -28,6 +28,7 @@
#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>" #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 MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>" #define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>"
#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>"
#define MCHelpCmd L"\n\n<Commands>" #define MCHelpCmd L"\n\n<Commands>"
#define MCHelpCmdA L"\n a Add files to archive" #define MCHelpCmdA L"\n a Add files to archive"
#define MCHelpCmdC L"\n c Add archive comment" #define MCHelpCmdC L"\n c Add archive comment"
@ -44,7 +45,7 @@
#define MCHelpCmdR L"\n r Repair archive" #define MCHelpCmdR L"\n r Repair archive"
#define MCHelpCmdRC L"\n rc Reconstruct missing volumes" #define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
#define MCHelpCmdRN L"\n rn Rename archived files" #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 MCHelpCmdRV L"\n rv[N] Create recovery volumes"
#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX" #define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
#define MCHelpCmdT L"\n t Test archive files" #define MCHelpCmdT L"\n t Test archive files"
@ -58,6 +59,7 @@
#define MCHelpSwAD L"\n ad[1,2] Alternate destination path" #define MCHelpSwAD L"\n ad[1,2] Alternate destination path"
#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" #define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
#define MCHelpSwAI L"\n ai Ignore file attributes" #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 MCHelpSwAO L"\n ao Add files with Archive attribute set"
#define MCHelpSwAP L"\n ap<path> Set path inside archive" #define MCHelpSwAP L"\n ap<path> Set path inside archive"
#define MCHelpSwAS L"\n as Synchronize archive contents" #define MCHelpSwAS L"\n as Synchronize archive contents"
@ -394,3 +396,6 @@
#define MAdjustValue L"\nAdjusting %s value to %s." #define MAdjustValue L"\nAdjusting %s value to %s."
#define MOpFailed L"\nOperation failed" #define MOpFailed L"\nOperation failed"
#define MSkipEncArc L"\nSkipping the encrypted archive %s" #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"

View file

@ -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 \ 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 \ 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 \ 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: .cpp.o:
$(COMPILE) -D$(WHAT) -c $< $(COMPILE) -D$(WHAT) -c $<
@ -142,20 +142,23 @@ clean:
@rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ)
@rm -f unrar libunrar.* @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 @rm -f unrar
$(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
$(STRIP) unrar $(STRIP) unrar
sfx: WHAT=SFX_MODULE sfx: WHAT=SFX_MODULE
sfx: clean $(OBJECTS) sfx: $(OBJECTS)
@rm -f default.sfx @rm -f default.sfx
$(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS) $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS)
$(STRIP) default.sfx $(STRIP) default.sfx
lib: WHAT=RARDLL lib: WHAT=RARDLL
lib: CXXFLAGS+=$(LIBFLAGS) lib: CXXFLAGS+=$(LIBFLAGS)
lib: clean $(OBJECTS) $(LIB_OBJ) lib: $(OBJECTS) $(LIB_OBJ)
@rm -f libunrar.* @rm -f libunrar.*
$(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
$(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)

View file

@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt; Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked; 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 do
{ {
pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check. if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false; return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount; Model->CharMask[(*pps)->Symbol]=Model->EscCount;
pps++;
} while ( --i ); } while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale; psee2c->Summ += Model->Coder.SubRange.scale;
Model->NumMasked = NumStats; Model->NumMasked = NumStats;

View file

@ -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() void RAROptions::Init()
{ {
memset(this,0,sizeof(RAROptions)); memset(this,0,sizeof(RAROptions));

View file

@ -45,6 +45,12 @@ enum OVERWRITE_MODE
OVERWRITE_FORCE_ASK OVERWRITE_FORCE_ASK
}; };
enum ARC_METADATA
{
ARCMETA_NONE=0,
ARCMETA_SAVE, // -ams
ARCMETA_RESTORE // -amr
};
enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS }; enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
@ -84,11 +90,12 @@ struct FilterMode
#define MAX_GENERATE_MASK 128 #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 class RAROptions
{ {
public: public:
RAROptions(); RAROptions();
~RAROptions();
void Init(); void Init();
uint ExclFileAttr; uint ExclFileAttr;
@ -118,7 +125,6 @@ class RAROptions
wchar ArcPath[NM]; // For -ap<path>. wchar ArcPath[NM]; // For -ap<path>.
wchar ExclArcPath[NM]; // For -ep4<path> switch. wchar ExclArcPath[NM]; // For -ep4<path> switch.
SecPassword Password;
bool EncryptHeaders; bool EncryptHeaders;
bool SkipEncrypted; bool SkipEncrypted;
@ -132,6 +138,7 @@ class RAROptions
HASH_TYPE HashType; HASH_TYPE HashType;
int Recovery; int Recovery;
int RecVolNumber; int RecVolNumber;
ARC_METADATA ArcMetadata;
bool DisablePercentage; bool DisablePercentage;
bool DisableCopyright; bool DisableCopyright;
bool DisableDone; bool DisableDone;
@ -147,7 +154,6 @@ class RAROptions
PATH_EXCL_MODE ExclPath; PATH_EXCL_MODE ExclPath;
RECURSE_MODE Recurse; RECURSE_MODE Recurse;
int64 VolSize; int64 VolSize;
Array<int64> NextVolSizes;
uint CurVolNum; uint CurVolNum;
bool AllYes; bool AllYes;
bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.

View file

@ -13,6 +13,8 @@
#endif #endif
#include <new> #include <new>
#include <string>
#include <vector>
#if defined(_WIN_ALL) || defined(_EMX) #if defined(_WIN_ALL) || defined(_EMX)
@ -39,8 +41,15 @@
#define _UNICODE // Set _T() macro to convert from narrow to wide strings. #define _UNICODE // Set _T() macro to convert from narrow to wide strings.
#endif #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 WINVER _WIN32_WINNT_WINXP
#define _WIN32_WINNT _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP
#endif
#if !defined(ZIPSFX) #if !defined(ZIPSFX)
#define RAR_SMP #define RAR_SMP
@ -72,9 +81,6 @@
#include <dir.h> #include <dir.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
#if _MSC_VER<1500
#define for if (0) ; else for
#endif
#include <direct.h> #include <direct.h>
#include <intrin.h> #include <intrin.h>
@ -98,7 +104,6 @@
#include <time.h> #include <time.h>
#include <signal.h> #include <signal.h>
#define SAVE_LINKS #define SAVE_LINKS
#define ENABLE_ACCESS #define ENABLE_ACCESS

View file

@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
const wchar *s=DestPtr; const wchar *s=DestPtr;
if (s[0]!=0 && IsDriveDiv(s[1])) if (s[0]!=0 && IsDriveDiv(s[1]))
s+=2; 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,'\\'); uint SlashCount=0;
if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) for (const wchar *t=s+2;*t!=0;t++)
s=Slash+1; 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++) for (const wchar *t=s;*t!=0;t++)
if (IsPathDiv(*t)) if (IsPathDiv(*t))
@ -422,50 +428,39 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
bool IsNameUsable(const wchar *Name) bool IsNameUsable(const wchar *Name)
{ {
#ifndef _UNIX // We were asked to apply Windows-like conversion in Linux in case
if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) // 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; 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++) for (const wchar *s=Name;*s!=0;s++)
{ {
if ((uint)*s<32) if ((uint)*s<32)
return false; 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])) if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
return false; return false;
}
#endif #endif
}
return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; 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) 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) if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32)
*s='_'; *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==':') if (s-Name>1 && *s==':')
*s='_'; *s='_';
#if 0 // We already can create such files. #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 // Here we ensure that we have enough 'N' characters to fit all digits
// of archive number. We'll replace them by actual number later // of archive number. We'll replace them by actual number later
// in this function. // 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); wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1);
wmemset(Mask+I,'N',Digits); 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) if (StartWeekDay%7>=4)
CurWeek++; CurWeek++;
char Field[10][6]; char Field[10][11];
sprintf(Field[0],"%04u",rlt.Year); sprintf(Field[0],"%04u",rlt.Year);
sprintf(Field[1],"%02u",rlt.Month); sprintf(Field[1],"%02u",rlt.Month);

View file

@ -29,7 +29,6 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx
wchar* GetVolNumPart(const wchar *ArcName); wchar* GetVolNumPart(const wchar *ArcName);
void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering);
bool IsNameUsable(const wchar *Name); bool IsNameUsable(const wchar *Name);
void MakeNameUsable(char *Name,bool Extended);
void MakeNameUsable(wchar *Name,bool Extended); void MakeNameUsable(wchar *Name,bool Extended);
void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);

View file

@ -97,7 +97,7 @@ void QuickOpen::Load(uint64 BlockPos)
if (Arc->SubHead.Encrypted) if (Arc->SubHead.Encrypted)
{ {
RAROptions *Cmd=Arc->GetRAROptions(); CommandData *Cmd=Arc->GetCommandData();
#ifndef RAR_NOCRYPT #ifndef RAR_NOCRYPT
if (Cmd->Password.IsSet()) if (Cmd->Password.IsSet())
Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,

View file

@ -12,6 +12,7 @@
#include "version.hpp" #include "version.hpp"
#include "rardefs.hpp" #include "rardefs.hpp"
#include "rarlang.hpp" #include "rarlang.hpp"
#include "rawint.hpp"
#include "unicode.hpp" #include "unicode.hpp"
#include "errhnd.hpp" #include "errhnd.hpp"
#include "secpassword.hpp" #include "secpassword.hpp"
@ -34,7 +35,6 @@
#endif #endif
#include "file.hpp" #include "file.hpp"
#include "crc.hpp" #include "crc.hpp"
#include "ui.hpp"
#include "filefn.hpp" #include "filefn.hpp"
#include "filestr.hpp" #include "filestr.hpp"
#include "find.hpp" #include "find.hpp"
@ -47,11 +47,11 @@
#include "archive.hpp" #include "archive.hpp"
#include "match.hpp" #include "match.hpp"
#include "cmddata.hpp" #include "cmddata.hpp"
#include "ui.hpp"
#include "filcreat.hpp" #include "filcreat.hpp"
#include "consio.hpp" #include "consio.hpp"
#include "system.hpp" #include "system.hpp"
#include "log.hpp" #include "log.hpp"
#include "rawint.hpp"
#include "rawread.hpp" #include "rawread.hpp"
#include "encname.hpp" #include "encname.hpp"
#include "resource.hpp" #include "resource.hpp"

View file

@ -9,9 +9,13 @@
#define ASIZE(x) (sizeof(x)/sizeof(x[0])) #define ASIZE(x) (sizeof(x)/sizeof(x[0]))
// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16) // MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of
// for CryptProtectMemory in SecPassword. // CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword.
#define MAXPASSWORD 128 // 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 #define MAXSFXSIZE 0x200000

View file

@ -84,7 +84,7 @@ inline uint32 RawGetBE4(const byte *m)
{ {
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_ulong(*(uint32 *)m); 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); return __builtin_bswap32(*(uint32 *)m);
#else #else
return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; 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) #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint32*)mem = _byteswap_ulong(i); *(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); *(uint32*)mem = __builtin_bswap32(i);
#else #else
mem[0]=byte(i>>24); mem[0]=byte(i>>24);
@ -112,7 +112,7 @@ inline uint32 ByteSwap32(uint32 i)
{ {
#ifdef _MSC_VER #ifdef _MSC_VER
return _byteswap_ulong(i); return _byteswap_ulong(i);
#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) #elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap32(i); return __builtin_bswap32(i);
#else #else
return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);

View file

@ -155,7 +155,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{ {
#ifdef RARDLL #ifdef RARDLL
RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions(); CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP) if (Cmd->DllOpMode!=RAR_SKIP)
{ {
if (Cmd->Callback!=NULL && if (Cmd->Callback!=NULL &&
@ -204,7 +204,7 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
ArcPos+=ProcessedArcSize; ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile; Archive *SrcArc=(Archive *)SrcFile;
RAROptions *Cmd=SrcArc->GetRAROptions(); CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize); int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent) if (!Cmd->DisablePercentage && CurPercent!=LastPercent)

View file

@ -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); Archive Arc(Cmd);
if (!Arc.Open(Name)) 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]; wchar RevName[NM];
*RevName=0; *RevName=0;

View file

@ -14,11 +14,11 @@ class RecVolumes3
ThreadPool *RSThreadPool; ThreadPool *RSThreadPool;
#endif #endif
public: public:
RecVolumes3(RAROptions *Cmd,bool TestOnly); RecVolumes3(CommandData *Cmd,bool TestOnly);
~RecVolumes3(); ~RecVolumes3();
void Make(RAROptions *Cmd,wchar *ArcName); void Make(CommandData *Cmd,wchar *ArcName);
bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
void Test(RAROptions *Cmd,const wchar *Name); void Test(CommandData *Cmd,const wchar *Name);
}; };
@ -48,8 +48,8 @@ struct RecRSThreadData
class RecVolumes5 class RecVolumes5
{ {
private: private:
void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode); void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode);
uint ReadHeader(File *RecFile,bool FirstRev); uint ReadHeader(File *RecFile,bool FirstRev);
Array<RecVolItem> RecItems; Array<RecVolItem> RecItems;
@ -76,13 +76,13 @@ class RecVolumes5
public: // 'public' only because called from thread functions. public: // 'public' only because called from thread functions.
void ProcessAreaRS(RecRSThreadData *td); void ProcessAreaRS(RecRSThreadData *td);
public: public:
RecVolumes5(RAROptions *Cmd,bool TestOnly); RecVolumes5(CommandData *Cmd,bool TestOnly);
~RecVolumes5(); ~RecVolumes5();
bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
void Test(RAROptions *Cmd,const wchar *Name); void Test(CommandData *Cmd,const wchar *Name);
}; };
bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent); bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent);
void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name); void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name);
#endif #endif

View file

@ -36,7 +36,7 @@ THREAD_PROC(RSDecodeThread)
} }
#endif #endif
RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly) RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly)
{ {
memset(SrcFile,0,sizeof(SrcFile)); memset(SrcFile,0,sizeof(SrcFile));
if (TestOnly) 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]; wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName)); wcsncpyz(ArcName,Name,ASIZE(ArcName));
@ -226,7 +226,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (WrongParam) if (WrongParam)
continue; 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; continue;
if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) 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)); wcsncpyz(PrevName,CurName,ASIZE(PrevName));
File *NewFile=new File; File *NewFile=new File;
NewFile->TOpen(CurName); 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++; FoundRecVolumes++;
if (RecFileSize==0) 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. if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
{ {

View file

@ -4,7 +4,7 @@ static const uint MaxVolumes=65535;
// rev files by mistake. // rev files by mistake.
#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. #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; RealBuf=NULL;
RealReadBuffer=NULL; RealReadBuffer=NULL;
@ -70,7 +70,7 @@ THREAD_PROC(RecThreadRS)
#endif #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; 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]; wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName)); 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]; wchar VolName[NM];
wcsncpyz(VolName,Name,ASIZE(VolName)); wcsncpyz(VolName,Name,ASIZE(VolName));

View file

@ -90,18 +90,20 @@ Rijndael::Rijndael()
void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector)
{ {
#ifdef USE_SSE // Check SIMD here instead of constructor, so if object is a part of some
// Check SSE here instead of constructor, so if object is a part of some // structure memset'ed before use, these variables are not lost.
// structure memset'ed before use, this variable is not lost. #if defined(USE_SSE)
int CPUInfo[4]; int CPUInfo[4];
__cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function. __cpuid(CPUInfo, 0);
if ((CPUInfo[0] & 0x7fffffff)>=1) if (CPUInfo[0]>=1) // Check the maximum supported cpuid function.
{ {
__cpuid(CPUInfo, 1); __cpuid(CPUInfo, 1);
AES_NI=(CPUInfo[2] & 0x2000000)!=0; AES_NI=(CPUInfo[2] & 0x2000000)!=0;
} }
else else
AES_NI=false; AES_NI=false;
#elif defined(USE_NEON)
AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0;
#endif #endif
// Other developers asked us to initialize it to suppress "may be used // 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(); keyEncToDec();
} }
void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer)
{ {
if (inputLen <= 0) if (inputLen <= 0)
return; return;
size_t numBlocks = inputLen/16; size_t numBlocks = inputLen/16;
#ifdef USE_SSE #if defined(USE_SSE)
if (AES_NI) if (AES_NI)
{ {
blockEncryptSSE(input,numBlocks,outBuffer); blockEncryptSSE(input,numBlocks,outBuffer);
return; return;
} }
#elif defined(USE_NEON)
if (AES_Neon)
{
blockEncryptNeon(input,numBlocks,outBuffer);
return;
}
#endif #endif
byte *prevBlock = m_initVector; byte *prevBlock = m_initVector;
@ -239,6 +248,40 @@ void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffe
} }
#endif #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) 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; return;
size_t numBlocks=inputLen/16; size_t numBlocks=inputLen/16;
#ifdef USE_SSE #if defined(USE_SSE)
if (AES_NI) if (AES_NI)
{ {
blockDecryptSSE(input,numBlocks,outBuffer); blockDecryptSSE(input,numBlocks,outBuffer);
return; return;
} }
#elif defined(USE_NEON)
if (AES_Neon)
{
blockDecryptNeon(input,numBlocks,outBuffer);
return;
}
#endif #endif
byte block[16], iv[4][4]; byte block[16], iv[4][4];
@ -343,6 +392,41 @@ void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuf
#endif #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 // ALGORITHM
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -18,6 +18,16 @@ class Rijndael
bool AES_NI; bool AES_NI;
#endif #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 keySched(byte key[_MAX_KEY_COLUMNS][4]);
void keyEncToDec(); void keyEncToDec();
void GenerateTables(); void GenerateTables();

View file

@ -215,10 +215,21 @@ bool ScanTree::GetNextMask()
UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask));
#endif #endif
// We wish to scan entire disk if mask like c:\ is specified // We prefer to scan entire disk if mask like \\server\share\ or c:\
// regardless of recursion mode. Use c:\*.* mask when need to scan only // is specified regardless of recursion mode. Use \\server\share\*.*
// the root directory. // or c:\*.* mask to scan only the root directory.
ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; 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); wchar *Name=PointToName(CurMask);
if (*Name==0) if (*Name==0)

View file

@ -56,7 +56,6 @@ static CryptLoader GlobalCryptLoader;
SecPassword::SecPassword() SecPassword::SecPassword()
{ {
CrossProcess=false;
Set(L""); Set(L"");
} }
@ -70,7 +69,8 @@ SecPassword::~SecPassword()
void SecPassword::Clean() void SecPassword::Clean()
{ {
PasswordSet=false; 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 // 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. // -p<pwd> parameter, so we need to take into account both sizes.
memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); 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) if (PasswordSet)
{ {
Process(Password,ASIZE(Password),Psw,MaxSize,false); Process(&Password[0],Password.size(),Psw,MaxSize,false);
Psw[MaxSize-1]=0; Psw[MaxSize-1]=0;
} }
else else
@ -124,15 +124,14 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize)
void SecPassword::Set(const wchar *Psw) void SecPassword::Set(const wchar *Psw)
{ {
if (*Psw==0) // Eliminate any traces of previously stored password for security reason
{ // in case it was longer than new one.
PasswordSet=false; Clean();
memset(Password,0,sizeof(Password));
} if (*Psw!=0)
else
{ {
PasswordSet=true; 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]; wchar Plain[MAXPASSWORD];
Get(Plain,ASIZE(Plain)); Get(Plain,ASIZE(Plain));
size_t Length=wcslen(Plain); size_t Length=wcslen(Plain);
cleandata(Plain,ASIZE(Plain)); cleandata(Plain,sizeof(Plain));
return Length; return Length;
} }
@ -157,12 +156,15 @@ bool SecPassword::operator == (SecPassword &psw)
Get(Plain1,ASIZE(Plain1)); Get(Plain1,ASIZE(Plain1));
psw.Get(Plain2,ASIZE(Plain2)); psw.Get(Plain2,ASIZE(Plain2));
bool Result=wcscmp(Plain1,Plain2)==0; bool Result=wcscmp(Plain1,Plain2)==0;
cleandata(Plain1,ASIZE(Plain1)); cleandata(Plain1,sizeof(Plain1));
cleandata(Plain2,ASIZE(Plain2)); cleandata(Plain2,sizeof(Plain2));
return Result; 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) void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
{ {
// CryptProtectMemory is not available in UWP and CryptProtectData // CryptProtectMemory is not available in UWP and CryptProtectData

View file

@ -8,10 +8,7 @@ class SecPassword
private: private:
void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode);
wchar Password[MAXPASSWORD]; std::vector<wchar> Password = std::vector<wchar>(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.
bool PasswordSet; bool PasswordSet;
public: public:
SecPassword(); SecPassword();
@ -22,10 +19,6 @@ class SecPassword
bool IsSet() {return PasswordSet;} bool IsSet() {return PasswordSet;}
size_t Length(); size_t Length();
bool operator == (SecPassword &psw); 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;
}; };

View file

@ -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 wchar* GetWide(const char *Src)
{ {
const size_t MaxLength=NM; const size_t MaxLength=NM;

View file

@ -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,char *Str,size_t MaxSize);
void itoa(int64 n,wchar *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* GetWide(const char *Src);
const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize); const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize);
#ifndef RARDLL #ifndef RARDLL

View file

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

View file

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

View file

@ -22,6 +22,17 @@ class RarTime
// Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601. // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601.
// We use nanoseconds here to handle the high precision Unix time. // 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; uint64 itime;
public: public:
// RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND. // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND.

View file

@ -49,7 +49,7 @@ enum UIMESSAGE_CODE {
UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING,
UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING,
UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE,
UIMSG_SKIPENCARC, UIMSG_SKIPENCARC, UIMSG_FILERENAME,
UIWAIT_FIRST, UIWAIT_FIRST,
UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, 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 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); 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); void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize);
enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; 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(); bool uiIsGlobalPasswordSet();
enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION};
@ -145,30 +145,31 @@ class uiMsgStore
// Templates recognize usual NULL as integer, not wchar*. // Templates recognize usual NULL as integer, not wchar*.
#define UINULL ((wchar *)NULL) #define UINULL ((wchar *)NULL)
inline void uiMsg(UIMESSAGE_CODE Code) inline void uiMsgBase(uiMsgStore &Store)
{ {
uiMsgStore Store(Code); // Called last, when no parameters are left.
Store.Msg();
} }
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<<a1;
Store.Msg(); uiMsgBase(Store,aN...);
} }
template<class T1,class T2> void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2)
// 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); uiMsgStore Store(Code);
Store<<a1<<a2; uiMsgBase(Store,aN...);
Store.Msg();
}
template<class T1,class T2,class T3> void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3)
{
uiMsgStore Store(code);
Store<<a1<<a2<<a3;
Store.Msg(); Store.Msg();
} }

View file

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

View file

@ -183,6 +183,7 @@ void uiMsgStore::Msg()
Log(NULL,St(MNeedAdmin)); Log(NULL,St(MNeedAdmin));
break; break;
case UIERROR_ARCBROKEN: case UIERROR_ARCBROKEN:
mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN.
Log(Str[0],St(MErrBrokenArc)); Log(Str[0],St(MErrBrokenArc));
break; break;
case UIERROR_HEADERBROKEN: case UIERROR_HEADERBROKEN:
@ -262,6 +263,7 @@ void uiMsgStore::Msg()
break; break;
case UIERROR_MISSINGVOL: case UIERROR_MISSINGVOL:
Log(Str[0],St(MAbsNextVol),Str[0]); Log(Str[0],St(MAbsNextVol),Str[0]);
mprintf(L" "); // For progress percent.
break; break;
#ifndef SFX_MODULE #ifndef SFX_MODULE
case UIERROR_NEEDPREVVOL: 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 // 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 // password to abort. Otherwise user not knowing a password would need to

View file

@ -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; return false;
} }

View file

@ -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]; char Target[NM];
if (IsLink(Arc.FileHead.FileAttr)) if (IsLink(Arc.FileHead.FileAttr))
@ -100,13 +101,14 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW)))
return false; return false;
UpLink=strstr(Target,"..")!=NULL;
return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
} }
return false; return false;
} }
bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
{ {
char Target[NM]; char Target[NM];
WideToChar(hd->RedirName,Target,ASIZE(Target)); 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 // Use hd->FileName instead of LinkName, since LinkName can include
// the destination path as a prefix, which can confuse // the destination path as a prefix, which can confuse
// IsRelativeSymlinkSafe algorithm. // IsRelativeSymlinkSafe algorithm.
// 2022.05.04: Use TargetW instead of previously used hd->RedirName
// for security reason.
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW))) !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW)))
return false; return false;

View file

@ -229,10 +229,11 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
#endif #endif
// SrcSize is in wide characters, not in bytes. // SrcSize is source data size in wide characters, not in bytes.
byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) // 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]=(byte)*Src;
Dest[I*2+1]=(byte)(*Src>>8); Dest[I*2+1]=(byte)(*Src>>8);

View file

@ -7,7 +7,7 @@
bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); bool WideToChar(const wchar *Src,char *Dest,size_t DestSize);
bool CharToWide(const char *Src,wchar *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); wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize);
void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize);
size_t WideToUtfSize(const wchar *Src); size_t WideToUtfSize(const wchar *Src);

View file

@ -91,12 +91,6 @@ void Unpack::Init(size_t WinSize,bool Solid)
if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB. if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB.
return; 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 // 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 // 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 // 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; Dec->DecodeLen[I]=(uint)LeftAligned;
// Every item of this array contains the sum of all preceding items. // 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]; 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; Dec->QuickBits=MAX_QUICK_DECODE_BITS;
break; break;
default: default:
Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0;
break; break;
} }
@ -334,7 +328,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
uint BitField=Code<<(16-Dec->QuickBits); uint BitField=Code<<(16-Dec->QuickBits);
// Prepare the table for quick decoding of bit lengths. // Prepare the table for quick decoding of bit lengths.
// Find the upper limit for current bit field and adjust the bit length // Find the upper limit for current bit field and adjust the bit length
// accordingly if necessary. // accordingly if necessary.
while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength])

View file

@ -93,17 +93,17 @@ struct UnpackBlockTables
#ifdef RAR_SMP #ifdef RAR_SMP
enum UNP_DEC_TYPE { 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 struct UnpackDecodedItem
{ {
UNP_DEC_TYPE Type; byte Type; // 'byte' instead of enum type to reduce memory use.
ushort Length; ushort Length;
union union
{ {
uint Distance; uint Distance;
byte Literal[4]; byte Literal[8]; // Store up to 8 chars here to speed up extraction.
}; };
}; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,15 @@
#include "rar.hpp" #include "rar.hpp"
#ifdef RARDLL #ifdef RARDLL
static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize);
static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); static bool DllVolNotify(CommandData *Cmd,wchar *NextName);
#endif #endif
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command)
{ {
RAROptions *Cmd=Arc.GetRAROptions(); CommandData *Cmd=Arc.GetCommandData();
HEADER_TYPE HeaderType=Arc.GetHeaderType(); HEADER_TYPE HeaderType=Arc.GetHeaderType();
FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; 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 #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; bool DllVolChanged=false,DllVolAborted=false;
@ -246,7 +246,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
#ifdef RARDLL #ifdef RARDLL
bool DllVolNotify(RAROptions *Cmd,wchar *NextName) bool DllVolNotify(CommandData *Cmd,wchar *NextName)
{ {
char NextNameA[NM]; char NextNameA[NM];
WideToChar(NextName,NextNameA,ASIZE(NextNameA)); WideToChar(NextName,NextNameA,ASIZE(NextNameA));

View file

@ -1,10 +1,7 @@
#ifndef _RAR_VOLUME_ #ifndef _RAR_VOLUME_
#define _RAR_VOLUME_ #define _RAR_VOLUME_
void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos,
ComprDataIO *DataIO);
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,
wchar Command); wchar Command);
void SetVolWrite(Archive &Dest,int64 VolSize);
#endif #endif

View file

@ -40,7 +40,7 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd)
PrivSet=true; 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); Array<byte> Buf(BufSize);
REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0];

View file

@ -39,6 +39,7 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName)
CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName));
ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1);
wcsncatz(StreamName,StoredName,ASIZE(StreamName)); wcsncatz(StreamName,StoredName,ASIZE(StreamName));
FindData fd; FindData fd;
@ -111,16 +112,23 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode)
wcsncatz(FullName,StreamName,ASIZE(FullName)); wcsncatz(FullName,StreamName,ASIZE(FullName));
FindData fd; FindData fd;
bool Found=FindFile::FastFind(FileName,&fd); bool HostFound=FindFile::FastFind(FileName,&fd);
if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
File CurFile; 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; 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, SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime,
&fd.ftLastWriteTime); &fd.ftLastWriteTime);