2 Commits

Author SHA1 Message Date
macintoshplus
e783678a51 ⬆️ update to UnRAR 7.2.4 2026-02-25 09:14:39 +01:00
macintoshplus
b2c9c08fe0 ⬆️ update to UnRAR 6.0.2 2026-02-25 09:11:26 +01:00
142 changed files with 7868 additions and 4247 deletions

View File

@@ -238,8 +238,10 @@
<ClCompile Include="hash.cpp" />
<ClCompile Include="headers.cpp" />
<ClCompile Include="isnt.cpp" />
<ClCompile Include="largepage.cpp" />
<ClCompile Include="list.cpp" />
<ClCompile Include="match.cpp" />
<ClCompile Include="motw.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="pathfn.cpp" />
<ClCompile Include="qopen.cpp" />

View File

@@ -138,7 +138,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@@ -168,7 +168,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@@ -198,7 +198,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@@ -239,7 +239,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -274,7 +274,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@@ -315,7 +315,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -374,7 +374,9 @@
<ClCompile Include="hash.cpp" />
<ClCompile Include="headers.cpp" />
<ClCompile Include="isnt.cpp" />
<ClCompile Include="largepage.cpp" />
<ClCompile Include="match.cpp" />
<ClCompile Include="motw.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="pathfn.cpp" />
<ClCompile Include="qopen.cpp" />

View File

@@ -7,51 +7,18 @@
for samples and ideas allowed to make Reed-Solomon coding
more efficient.
* RAR text compression algorithm is based on Dmitry Shkarin PPMII
* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
and Dmitry Subbotin carryless rangecoder public domain source code.
You may find it in ftp.elf.stuba.sk/pub/pc/pack.
You can find it in ftp.elf.stuba.sk/pub/pc/pack.
* RAR encryption includes parts of code from Szymon Stefanek
and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
* RAR encryption includes parts of public domain code
from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
---------------------------------------------------------------------------
Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
All rights reserved.
* With exception of SFX modules, RAR uses CRC32 function based
on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
is available here:
LICENSE TERMS
The free distribution and use of this software in both source and binary
form is allowed (with or without changes) provided that:
1. distributions of this source code include the above copyright
notice, this list of conditions and the following disclaimer;
2. distributions in binary form include the above copyright
notice, this list of conditions and the following disclaimer
in the documentation and/or other associated materials;
3. the copyright holder's name is not used to endorse products
built using this software without specific written permission.
ALTERNATIVELY, provided that this notice is retained in full, this product
may be distributed under the terms of the GNU General Public License (GPL),
in which case the provisions of the GPL apply INSTEAD OF those given above.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Source code of this package also as other cryptographic technology
and computing project related links are available on Brian Gladman's
web site: http://www.gladman.me.uk
* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm.
Original Intel Slicing-by-8 code is available here:
http://sourceforge.net/projects/slicing-by-8/
https://sourceforge.net/projects/slicing-by-8/
Original Intel Slicing-by-8 code is licensed under BSD License
available at http://www.opensource.org/licenses/bsd-license.html

View File

@@ -1,6 +1,6 @@
static bool IsAnsiEscComment(const wchar *Data,size_t Size);
bool Archive::GetComment(Array<wchar> *CmtData)
bool Archive::GetComment(std::wstring &CmtData)
{
if (!MainComment)
return false;
@@ -11,7 +11,7 @@ bool Archive::GetComment(Array<wchar> *CmtData)
}
bool Archive::DoGetComment(Array<wchar> *CmtData)
bool Archive::DoGetComment(std::wstring &CmtData)
{
#ifndef SFX_MODULE
uint CmtLength;
@@ -36,7 +36,12 @@ bool Archive::DoGetComment(Array<wchar> *CmtData)
{
// Current (RAR 3.0+) version of archive comment.
Seek(GetStartPos(),SEEK_SET);
return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
if (SearchSubBlock(SUBHEAD_TYPE_CMT)!=0)
if (ReadCommentData(CmtData))
return true;
else
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
#ifndef SFX_MODULE
// Old style (RAR 2.9) comment header embedded into the main
@@ -101,10 +106,8 @@ bool Archive::DoGetComment(Array<wchar> *CmtData)
// 4x memory for OEM to UTF-8 output here.
OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
#endif
CmtData->Alloc(UnpDataSize+1);
memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
CmtData->Alloc(wcslen(CmtData->Addr(0)));
std::string UnpStr((char*)UnpData,UnpDataSize);
CharToWide(UnpStr,CmtData);
}
}
}
@@ -112,12 +115,12 @@ bool Archive::DoGetComment(Array<wchar> *CmtData)
{
if (CmtLength==0)
return false;
Array<byte> CmtRaw(CmtLength);
int ReadSize=Read(&CmtRaw[0],CmtLength);
std::vector<byte> CmtRaw(CmtLength);
int ReadSize=Read(CmtRaw.data(),CmtLength);
if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared.
{
CmtLength=ReadSize;
CmtRaw.Alloc(CmtLength);
CmtRaw.resize(CmtLength);
}
if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
@@ -125,43 +128,41 @@ bool Archive::DoGetComment(Array<wchar> *CmtData)
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
CmtData->Alloc(CmtLength+1);
CmtRaw.Push(0);
// CmtData.resize(CmtLength+1);
CmtRaw.push_back(0);
#ifdef _WIN_ALL
// If we ever decide to extend it to Android, we'll need to alloc
// 4x memory for OEM to UTF-8 output here.
OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
OemToCharA((char *)CmtRaw.data(),(char *)CmtRaw.data());
#endif
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
CmtData->Alloc(wcslen(CmtData->Addr(0)));
CharToWide((const char *)CmtRaw.data(),CmtData);
// CmtData->resize(wcslen(CmtData->data()));
}
#endif
return CmtData->Size() > 0;
return CmtData.size() > 0;
}
bool Archive::ReadCommentData(Array<wchar> *CmtData)
bool Archive::ReadCommentData(std::wstring &CmtData)
{
Array<byte> CmtRaw;
std::vector<byte> CmtRaw;
if (!ReadSubData(&CmtRaw,NULL,false))
return false;
size_t CmtSize=CmtRaw.Size();
CmtRaw.Push(0);
CmtData->Alloc(CmtSize+1);
size_t CmtSize=CmtRaw.size();
CmtRaw.push_back(0);
// CmtData->resize(CmtSize+1);
if (Format==RARFMT50)
UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
UtfToWide((char *)CmtRaw.data(),CmtData);
else
if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
{
RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
(*CmtData)[CmtSize/2]=0;
CmtData=RawToWide(CmtRaw);
}
else
{
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
CharToWide((const char *)CmtRaw.data(),CmtData);
}
CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
// CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length.
return true;
}
@@ -170,15 +171,16 @@ void Archive::ViewComment()
{
if (Cmd->DisableComment)
return;
Array<wchar> CmtBuf;
if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments.
std::wstring CmtBuf;
if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments.
{
size_t CmtSize=CmtBuf.Size();
wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
if (ChPtr!=NULL)
CmtSize=ChPtr-&CmtBuf[0];
mprintf(L"\n");
OutComment(&CmtBuf[0],CmtSize);
size_t CmtSize=CmtBuf.size();
auto EndPos=CmtBuf.find(0x1A);
if (EndPos!=std::wstring::npos)
CmtSize=EndPos;
mprintf(St(MArcComment));
mprintf(L":\n");
OutComment(CmtBuf);
}
}

View File

@@ -3,15 +3,15 @@
#include "arccmt.cpp"
Archive::Archive(RAROptions *InitCmd)
Archive::Archive(CommandData *InitCmd)
{
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
DummyCmd=(InitCmd==NULL);
Cmd=DummyCmd ? (new RAROptions):InitCmd;
Cmd=DummyCmd ? (new CommandData):InitCmd;
OpenShared=Cmd->OpenShared;
Format=RARFMT15;
Format=RARFMT_NONE;
Solid=false;
Volume=false;
MainComment=false;
@@ -26,20 +26,21 @@ Archive::Archive(RAROptions *InitCmd)
FailedHeaderDecryption=false;
BrokenHeader=false;
LastReadBlock=0;
CurHeaderType=HEAD_UNKNOWN;
CurBlockPos=0;
NextBlockPos=0;
RecoveryPercent=-1;
memset(&MainHead,0,sizeof(MainHead));
memset(&CryptHead,0,sizeof(CryptHead));
memset(&EndArcHead,0,sizeof(EndArcHead));
MainHead.Reset();
CryptHead={};
EndArcHead.Reset();
VolNumber=0;
VolWrite=0;
AddingFilesSize=0;
AddingHeadersSize=0;
*FirstVolumeName=0;
Splitting=false;
NewArchive=false;
@@ -68,13 +69,13 @@ void Archive::CheckArc(bool EnableBroken)
// password is incorrect.
if (!FailedHeaderDecryption)
uiMsg(UIERROR_BADARCHIVE,FileName);
ErrHandler.Exit(RARX_FATAL);
ErrHandler.Exit(RARX_BADARC);
}
}
#if !defined(SFX_MODULE)
void Archive::CheckOpen(const wchar *Name)
void Archive::CheckOpen(const std::wstring &Name)
{
TOpen(Name);
CheckArc(false);
@@ -82,7 +83,7 @@ void Archive::CheckOpen(const wchar *Name)
#endif
bool Archive::WCheckOpen(const wchar *Name)
bool Archive::WCheckOpen(const std::wstring &Name)
{
if (!WOpen(Name))
return false;
@@ -110,9 +111,11 @@ RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
// We check the last signature byte, so we can return a sensible
// warning in case we'll want to change the archive format
// sometimes in the future.
#ifndef SFX_MODULE
if (D[6]==0)
Type=RARFMT15;
else
#endif
if (D[6]==1)
Type=RARFMT50;
else
@@ -148,9 +151,9 @@ bool Archive::IsArchive(bool EnableBroken)
}
else
{
Array<char> Buffer(MAXSFXSIZE);
std::vector<char> Buffer(MAXSFXSIZE);
long CurPos=(long)Tell();
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
int ReadSize=Read(Buffer.data(),Buffer.size()-16);
for (int I=0;I<ReadSize;I++)
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
{
@@ -231,7 +234,7 @@ bool Archive::IsArchive(bool EnableBroken)
// first file header to set "comment" flag when reading service header.
// Unless we are in silent mode, we need to know about presence of comment
// immediately after IsArchive call.
if (HeadersLeft && (!SilentOpen || !Encrypted))
if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
{
int64 SavePos=Tell();
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
@@ -265,7 +268,7 @@ bool Archive::IsArchive(bool EnableBroken)
Seek(SavePos,SEEK_SET);
}
if (!Volume || FirstVolume)
wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
FirstVolumeName=FileName;
return true;
}
@@ -301,7 +304,7 @@ uint Archive::FullHeaderSize(size_t Size)
#ifdef USE_QOPEN
bool Archive::Open(const wchar *Name,uint Mode)
bool Archive::Open(const std::wstring &Name,uint Mode)
{
// Important if we reuse Archive object and it has virtual QOpen
// file position not matching real. For example, for 'l -v volname'.
@@ -336,3 +339,23 @@ int64 Archive::Tell()
}
#endif
// Return 0 if dictionary size is invalid. If size is RAR7 only, return
// the adjusted nearest bottom value. Return header flags in Flags.
uint64 Archive::GetWinSize(uint64 Size,uint &Flags)
{
Flags=0;
// Allow 128 KB - 1 TB range.
if (Size<0x20000 || Size>0x10000000000ULL)
return 0;
uint64 Pow2=0x20000; // Power of 2 dictionary size.
for (;2*Pow2<=Size;Pow2*=2)
Flags+=FCI_DICT_BIT0;
if (Size==Pow2)
return Size; // If 'Size' is the power of 2, return it as is.
// Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'.
uint64 Fraction=(Size-Pow2)/(Pow2/32);
Flags+=(uint)Fraction*FCI_DICT_FRACT0;
return Pow2+Fraction*(Pow2/32);
}

View File

@@ -27,26 +27,27 @@ class Archive:public File
{
private:
void UpdateLatestTime(FileHeader *CurBlock);
void ConvertNameCase(wchar *Name);
void ConvertNameCase(std::wstring &Name);
void ConvertFileHeader(FileHeader *hd);
size_t ReadHeader14();
size_t ReadHeader15();
size_t ReadHeader50();
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
void RequestArcPassword();
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
void RequestArcPassword(RarCheckPassword *SelPwd);
void UnexpEndArcMsg();
void BrokenHeaderMsg();
void UnkEncVerMsg(const wchar *Name,const wchar *Info);
bool DoGetComment(Array<wchar> *CmtData);
bool ReadCommentData(Array<wchar> *CmtData);
void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info);
bool DoGetComment(std::wstring &CmtData);
bool ReadCommentData(std::wstring &CmtData);
#if !defined(RAR_NOCRYPT)
CryptData HeadersCrypt;
#endif
ComprDataIO SubDataIO;
bool DummyCmd;
RAROptions *Cmd;
CommandData *Cmd;
int RecoveryPercent;
RarTime LatestTime;
int LastReadBlock;
@@ -58,18 +59,19 @@ class Archive:public File
bool ProhibitQOpen;
#endif
public:
Archive(RAROptions *InitCmd=NULL);
Archive(CommandData *InitCmd=nullptr);
~Archive();
static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
size_t SearchBlock(HEADER_TYPE HeaderType);
size_t SearchSubBlock(const wchar *Type);
size_t SearchRR();
int GetRecoveryPercent() {return RecoveryPercent;}
size_t ReadHeader();
void CheckArc(bool EnableBroken);
void CheckOpen(const wchar *Name);
bool WCheckOpen(const wchar *Name);
bool GetComment(Array<wchar> *CmtData);
void CheckOpen(const std::wstring &Name);
bool WCheckOpen(const std::wstring &Name);
bool GetComment(std::wstring &CmtData);
void ViewComment();
void SetLatestTime(RarTime *NewTime);
void SeekToNext();
@@ -79,23 +81,25 @@ class Archive:public File
void VolSubtractHeaderSize(size_t SubSize);
uint FullHeaderSize(size_t Size);
int64 GetStartPos();
void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
void AddSubData(const byte *SrcData,uint64 DataSize,File *SrcFile,
const wchar *Name,uint Flags);
bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode);
bool ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode);
HEADER_TYPE GetHeaderType() {return CurHeaderType;}
RAROptions* GetRAROptions() {return Cmd;}
CommandData* GetCommandData() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
#if 0
void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
#endif
#ifdef USE_QOPEN
bool Open(const wchar *Name,uint Mode=FMF_READ);
int Read(void *Data,size_t Size);
void Seek(int64 Offset,int Method);
int64 Tell();
bool Open(const std::wstring &Name,uint Mode=FMF_READ) override;
int Read(void *Data,size_t Size) override;
void Seek(int64 Offset,int Method) override;
int64 Tell() override;
void QOpenUnload() {QOpen.Unload();}
void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
#endif
static uint64 GetWinSize(uint64 Size,uint &Flags);
// Needed to see wstring based Open from File. Otherwise compiler finds
// Open in Archive and doesn't check the base class overloads.
using File::Open;
BaseBlock ShortBlock;
MarkHeader MarkHead;
@@ -107,7 +111,6 @@ class Archive:public File
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
UnixOwnersHeader UOHead;
EAHeader EAHead;
StreamHeader StreamHead;
@@ -136,12 +139,19 @@ class Archive:public File
uint VolNumber;
int64 VolWrite;
// Total size of files adding to archive. Might also include the size of
// files repacked in solid archive.
uint64 AddingFilesSize;
uint64 AddingHeadersSize;
bool NewArchive;
wchar FirstVolumeName[NM];
std::wstring FirstVolumeName;
#ifdef PROPAGATE_MOTW
MarkOfTheWeb Motw;
#endif
};

View File

@@ -20,10 +20,10 @@ size_t Archive::ReadHeader()
case RARFMT14:
ReadSize=ReadHeader14();
break;
#endif
case RARFMT15:
ReadSize=ReadHeader15();
break;
#endif
case RARFMT50:
ReadSize=ReadHeader50();
break;
@@ -100,9 +100,15 @@ void Archive::UnexpEndArcMsg()
// If block positions are equal to file size, this is not an error.
// It can happen when we reached the end of older RAR 1.5 archive,
// which did not have the end of archive block.
// We can't replace this check by checking that read size is exactly 0
// in the beginning of file header, because in this case the read position
// still can be beyond the end of archive.
if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{
uiMsg(UIERROR_UNEXPEOF,FileName);
if (CurHeaderType!=HEAD_FILE && CurHeaderType!=HEAD_UNKNOWN)
uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName);
ErrHandler.SetErrorCode(RARX_WARNING);
}
}
@@ -116,10 +122,10 @@ void Archive::BrokenHeaderMsg()
}
void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info)
{
uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
ErrHandler.SetErrorCode(RARX_WARNING);
ErrHandler.SetErrorCode(RARX_FATAL);
}
@@ -134,6 +140,7 @@ inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
}
#ifndef SFX_MODULE
size_t Archive::ReadHeader15()
{
RawRead Raw(this);
@@ -142,10 +149,10 @@ size_t Archive::ReadHeader15()
if (Decrypt)
{
#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
#ifdef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll.
return 0;
#else
RequestArcPassword();
RequestArcPassword(NULL);
byte Salt[SIZE_SALT30];
if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
@@ -219,7 +226,7 @@ size_t Archive::ReadHeader15()
{
case HEAD_MAIN:
MainHead.Reset();
*(BaseBlock *)&MainHead=ShortBlock;
MainHead.SetBaseBlock(ShortBlock);
MainHead.HighPosAV=Raw.Get2();
MainHead.PosAV=Raw.Get4();
@@ -245,13 +252,17 @@ size_t Archive::ReadHeader15()
FileHeader *hd=FileBlock ? &FileHead:&SubHead;
hd->Reset();
*(BaseBlock *)hd=ShortBlock;
hd->SetBaseBlock(ShortBlock);
hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
// RAR versions earlier than 2.0 do not set the solid flag
// in file header. They use only a global solid archive flag.
hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
@@ -300,7 +311,7 @@ size_t Archive::ReadHeader15()
if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
{
hd->RedirType=FSREDIR_UNIXSYMLINK;
*hd->RedirName=0;
hd->RedirName.clear();
}
hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
@@ -327,27 +338,26 @@ size_t Archive::ReadHeader15()
if (hd->UnknownUnpSize)
hd->UnpSize=INT64NDF;
char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
size_t ReadNameSize=Min(NameSize,MAXPATHSIZE);
std::string FileName(ReadNameSize,0);
Raw.GetB((byte *)&FileName[0],ReadNameSize);
if (FileBlock)
{
*hd->FileName=0;
hd->FileName.clear();
if ((hd->Flags & LHD_UNICODE)!=0)
{
EncodeFileName NameCoder;
size_t Length=strlen(FileName);
size_t Length=strlen(FileName.data());
Length++;
if (ReadNameSize>Length)
NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
ReadNameSize-Length,hd->FileName,
ASIZE(hd->FileName));
NameCoder.Decode(FileName.data(),ReadNameSize,
(byte *)&FileName[Length],
ReadNameSize-Length,hd->FileName);
}
if (*hd->FileName==0)
ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
if (hd->FileName.empty())
ArcCharToWide(FileName.data(),hd->FileName,ACTW_OEM);
#ifndef SFX_MODULE
ConvertNameCase(hd->FileName);
@@ -356,7 +366,7 @@ size_t Archive::ReadHeader15()
}
else
{
CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
CharToWide(FileName.data(),hd->FileName);
// Calculate the size of optional data.
int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
@@ -367,14 +377,15 @@ size_t Archive::ReadHeader15()
{
// Here we read optional additional fields for subheaders.
// They are stored after the file name and before salt.
hd->SubData.Alloc(DataSize);
Raw.GetB(&hd->SubData[0],DataSize);
hd->SubData.resize(DataSize);
Raw.GetB(hd->SubData.data(),DataSize);
}
if (hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true;
}
if ((hd->Flags & LHD_SALT)!=0)
Raw.GetB(hd->Salt,SIZE_SALT30);
hd->mtime.SetDos(FileTime);
@@ -417,7 +428,7 @@ size_t Archive::ReadHeader15()
NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
bool CRCProcessedOnly=hd->CommentInHeader;
ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
uint HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
if (hd->HeadCRC!=HeaderCRC)
{
BrokenHeader=true;
@@ -434,7 +445,7 @@ size_t Archive::ReadHeader15()
}
break;
case HEAD_ENDARC:
*(BaseBlock *)&EndArcHead=ShortBlock;
EndArcHead.SetBaseBlock(ShortBlock);
EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
@@ -446,14 +457,14 @@ size_t Archive::ReadHeader15()
break;
#ifndef SFX_MODULE
case HEAD3_CMT:
*(BaseBlock *)&CommHead=ShortBlock;
CommHead.SetBaseBlock(ShortBlock);
CommHead.UnpSize=Raw.Get2();
CommHead.UnpVer=Raw.Get1();
CommHead.Method=Raw.Get1();
CommHead.CommCRC=Raw.Get2();
break;
case HEAD3_PROTECT:
*(BaseBlock *)&ProtectHead=ShortBlock;
ProtectHead.SetBaseBlock(ShortBlock);
ProtectHead.DataSize=Raw.Get4();
ProtectHead.Version=Raw.Get1();
ProtectHead.RecSectors=Raw.Get2();
@@ -462,26 +473,13 @@ size_t Archive::ReadHeader15()
NextBlockPos+=ProtectHead.DataSize;
break;
case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
*(BaseBlock *)&SubBlockHead=ShortBlock;
SubBlockHead.SetBaseBlock(ShortBlock);
SubBlockHead.DataSize=Raw.Get4();
NextBlockPos+=SubBlockHead.DataSize;
SubBlockHead.SubType=Raw.Get2();
SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
case UO_HEAD:
*(SubBlockHeader *)&UOHead=SubBlockHead;
UOHead.OwnerNameSize=Raw.Get2();
UOHead.GroupNameSize=Raw.Get2();
if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
UOHead.OwnerName[UOHead.OwnerNameSize]=0;
UOHead.GroupName[UOHead.GroupNameSize]=0;
break;
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
EAHead.UnpSize=Raw.Get4();
@@ -496,10 +494,13 @@ size_t Archive::ReadHeader15()
StreamHead.Method=Raw.Get1();
StreamHead.StreamCRC=Raw.Get4();
StreamHead.StreamNameSize=Raw.Get2();
if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
StreamHead.StreamName[StreamHead.StreamNameSize]=0;
const size_t MaxStreamName20=260; // Maximum allowed stream name in RAR 2.x format.
if (StreamHead.StreamNameSize>MaxStreamName20)
StreamHead.StreamNameSize=MaxStreamName20;
StreamHead.StreamName.resize(StreamHead.StreamNameSize);
Raw.GetB(&StreamHead.StreamName[0],StreamHead.StreamNameSize);
break;
}
break;
@@ -510,11 +511,15 @@ size_t Archive::ReadHeader15()
break;
}
ushort HeaderCRC=Raw.GetCRC15(false);
uint HeaderCRC=Raw.GetCRC15(false);
// Old AV header does not have header CRC properly set.
// Old Unix owners header didn't include string fields into header size,
// but included them into CRC, so it couldn't be verified with generic
// approach here.
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
ShortBlock.HeaderType!=HEAD3_AV)
ShortBlock.HeaderType!=HEAD3_AV &&
(ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD))
{
bool Recovered=false;
if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
@@ -544,6 +549,7 @@ size_t Archive::ReadHeader15()
return Raw.Size();
}
#endif // #ifndef SFX_MODULE
size_t Archive::ReadHeader50()
@@ -558,6 +564,13 @@ size_t Archive::ReadHeader50()
return 0;
#else
if (Cmd->SkipEncrypted)
{
uiMsg(UIMSG_SKIPENCARC,FileName);
FailedHeaderDecryption=true; // Suppress error messages and quit quietly.
return 0;
}
byte HeadersInitV[SIZE_INITV];
if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
{
@@ -570,14 +583,20 @@ size_t Archive::ReadHeader50()
// in -p<pwd> to not stop batch processing for encrypted archives.
bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
RarCheckPassword CheckPwd;
if (CryptHead.UsePswCheck && !BrokenHeader)
CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck);
while (true) // Repeat the password prompt for wrong passwords.
{
RequestArcPassword();
RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL);
byte PswCheck[SIZE_PSWCHECK];
HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
// Verify password validity.
if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
bool EncSet=HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
// Verify password validity. If header is damaged, we cannot rely on
// password check value, because it can be damaged too.
if (EncSet && CryptHead.UsePswCheck && !BrokenHeader &&
memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
{
if (GlobalPassword) // For -p<pwd> or Ctrl+P.
{
@@ -635,7 +654,7 @@ size_t Archive::ReadHeader50()
}
int SizeToRead=int(BlockSize);
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
SizeToRead-=int(FirstReadSize-SizeBytes-4); // Adjust overread size bytes if any.
uint HeaderSize=4+SizeBytes+(uint)BlockSize;
if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
@@ -700,13 +719,12 @@ size_t Archive::ReadHeader50()
{
case HEAD_CRYPT:
{
*(BaseBlock *)&CryptHead=ShortBlock;
CryptHead.SetBaseBlock(ShortBlock);
uint CryptVersion=(uint)Raw.GetV();
if (CryptVersion>CRYPT_VERSION)
{
wchar Info[20];
swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
UnkEncVerMsg(FileName,Info);
UnkEncVerMsg(FileName,L"h" + std::to_wstring(CryptVersion));
FailedHeaderDecryption=true;
return 0;
}
uint EncFlags=(uint)Raw.GetV();
@@ -714,9 +732,8 @@ size_t Archive::ReadHeader50()
CryptHead.Lg2Count=Raw.Get1();
if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
{
wchar Info[20];
swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
UnkEncVerMsg(FileName,Info);
UnkEncVerMsg(FileName,L"hc" + std::to_wstring(CryptHead.Lg2Count));
FailedHeaderDecryption=true;
return 0;
}
@@ -728,14 +745,15 @@ size_t Archive::ReadHeader50()
byte csum[SIZE_PSWCHECK_CSUM];
Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
sha256_context ctx;
sha256_init(&ctx);
sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
// Exclude this code for rarext.dll, Setup.SFX and unrar_nocrypt.dll linked
// without sha256. But still set Encrypted=true for rarext.dll here,
// so it can recognize encrypted header archives in archive properties.
#ifndef RAR_NOCRYPT
byte Digest[SHA256_DIGEST_SIZE];
sha256_done(&ctx, Digest);
sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest);
CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
#endif
}
Encrypted=true;
}
@@ -743,7 +761,7 @@ size_t Archive::ReadHeader50()
case HEAD_MAIN:
{
MainHead.Reset();
*(BaseBlock *)&MainHead=ShortBlock;
MainHead.SetBaseBlock(ShortBlock);
uint ArcFlags=(uint)Raw.GetV();
Volume=(ArcFlags & MHFL_VOLUME)!=0;
@@ -785,7 +803,7 @@ size_t Archive::ReadHeader50()
case HEAD_SERVICE:
{
FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
hd->Reset();
hd->Reset(); // Clear hash, time fields and other stuff like flags.
*(BaseBlock *)hd=ShortBlock;
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
@@ -821,9 +839,14 @@ size_t Archive::ReadHeader50()
// we may need to use the compression algorithm 15 in the future,
// but it was already used in RAR 1.5 and Unpack needs to distinguish
// them.
hd->UnpVer=(CompInfo & 0x3f) + 50;
if (hd->UnpVer!=50) // Only 5.0 compression is known now.
hd->UnpVer=VER_UNKNOWN;
uint UnpVer=(CompInfo & 0x3f);
if (UnpVer==0)
hd->UnpVer=VER_PACK5;
else
if (UnpVer==1)
hd->UnpVer=VER_PACK7;
else
hd->UnpVer=VER_UNKNOWN;
hd->HostOS=(byte)Raw.GetV();
size_t NameSize=(size_t)Raw.GetV();
@@ -841,16 +864,29 @@ size_t Archive::ReadHeader50()
hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
if (hd->Dir || UnpVer>1)
hd->WinSize=0;
else
{
hd->WinSize=0x20000ULL<<((CompInfo>>10)&(UnpVer==0 ? 0x0f:0x1f));
if (UnpVer==1)
{
hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f);
hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
// RAR7 header with RAR5 compression. Needed to append RAR7 files
// to RAR5 solid stream if new dictionary is larger than existing.
if ((CompInfo & FCI_RAR5_COMPAT)!=0)
hd->UnpVer=VER_PACK5;
if (hd->WinSize>UNPACK_MAX_DICT)
hd->UnpVer=VER_UNKNOWN;
}
}
char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
size_t ReadNameSize=Min(NameSize,MAXPATHSIZE);
std::string FileName(ReadNameSize,0);
Raw.GetB((byte *)&FileName[0],ReadNameSize);
UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
UtfToWide(FileName.data(),hd->FileName);
// Should do it before converting names, because extra fields can
// affect name processing, like in case of NTFS streams.
@@ -868,20 +904,16 @@ size_t Archive::ReadHeader50()
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true;
#if 0
// For RAR5 format we read the user specified recovery percent here.
// It would be useful to do it for shell extension too, so we display
// the correct recovery record size in archive properties. But then
// we would need to include the entire recovery record processing
// code to shell extension, which is not done now.
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.size()>0)
{
RecoveryPercent=hd->SubData[0];
RSBlockHeader Header;
GetRRInfo(this,&Header);
RecoverySize=Header.RecSectionSize*Header.RecCount;
// It is stored as a single byte up to RAR 6.02 and as vint since
// 6.10, where we extended the maximum RR size from 99% to 1000%.
RawRead RawPercent;
RawPercent.Read(hd->SubData.data(),hd->SubData.size());
RecoveryPercent=(int)RawPercent.GetV();
}
#endif
if (BadCRC) // Add the file name to broken header message displayed above.
uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
@@ -889,7 +921,7 @@ size_t Archive::ReadHeader50()
break;
case HEAD_ENDARC:
{
*(BaseBlock *)&EndArcHead=ShortBlock;
EndArcHead.SetBaseBlock(ShortBlock);
uint ArcFlags=(uint)Raw.GetV();
EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
EndArcHead.StoreVolNumber=false;
@@ -904,7 +936,7 @@ size_t Archive::ReadHeader50()
#if !defined(RAR_NOCRYPT)
void Archive::RequestArcPassword()
void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
{
if (!Cmd->Password.IsSet())
{
@@ -921,7 +953,7 @@ void Archive::RequestArcPassword()
*PasswordA=0;
if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
*PasswordA=0;
GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
CharToWide(PasswordA,PasswordW,ASIZE(PasswordW));
cleandata(PasswordA,sizeof(PasswordA));
}
Cmd->Password.Set(PasswordW);
@@ -934,7 +966,7 @@ void Archive::RequestArcPassword()
ErrHandler.Exit(RARX_USERBREAK);
}
#else
if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd))
{
Close();
uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
@@ -947,7 +979,7 @@ void Archive::RequestArcPassword()
#endif
void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
{
// Read extra data from the end of block skipping any fields before it.
size_t ExtraStart=Raw->Size()-ExtraSize;
@@ -970,22 +1002,52 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
if (bb->HeaderType==HEAD_MAIN)
{
MainHeader *hd=(MainHeader *)bb;
if (FieldType==MHEXTRA_LOCATOR)
switch(FieldType)
{
hd->Locator=true;
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->QOpenOffset=Offset+CurBlockPos;
}
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->RROffset=Offset+CurBlockPos;
}
case MHEXTRA_LOCATOR:
{
hd->Locator=true;
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->QOpenOffset=Offset+CurBlockPos;
}
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->RROffset=Offset+CurBlockPos;
}
}
break;
case MHEXTRA_METADATA:
{
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_METADATA_NAME)!=0)
{
uint64 NameSize=Raw->GetV();
if (NameSize>0 && NameSize<MAXPATHSIZE) // Prevent excessive allocation.
{
std::string NameU((size_t)NameSize,0); // 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)
UtfToWide(&NameU[0],hd->OrigName);
}
}
if ((Flags & MHEXTRA_METADATA_CTIME)!=0)
if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0)
if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0)
hd->OrigTime.SetUnixNS(Raw->Get8());
else
hd->OrigTime.SetUnix((time_t)Raw->Get4());
else
hd->OrigTime.SetWin(Raw->Get8());
}
break;
}
}
@@ -994,64 +1056,64 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
FileHeader *hd=(FileHeader *)bb;
switch(FieldType)
{
#ifndef RAR_NOCRYPT // Except rarext.dll, Setup.SFX and unrar_nocrypt.dll.
case FHEXTRA_CRYPT:
{
FileHeader *hd=(FileHeader *)bb;
uint EncVersion=(uint)Raw->GetV();
if (EncVersion>CRYPT_VERSION)
{
wchar Info[20];
swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
UnkEncVerMsg(hd->FileName,Info);
UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion));
hd->CryptMethod=CRYPT_UNKNOWN;
}
else
{
uint Flags=(uint)Raw->GetV();
hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
hd->Lg2Count=Raw->Get1();
if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
{
wchar Info[20];
swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
UnkEncVerMsg(hd->FileName,Info);
UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count));
hd->CryptMethod=CRYPT_UNKNOWN;
}
Raw->GetB(hd->Salt,SIZE_SALT50);
Raw->GetB(hd->InitV,SIZE_INITV);
if (hd->UsePswCheck)
else
{
Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
// It is important to know if password check data is valid.
// If it is damaged and header CRC32 fails to detect it,
// archiver would refuse to decompress a possibly valid file.
// Since we want to be sure distinguishing a wrong password
// or corrupt file data, we use 64-bit password check data
// and to control its validity we use 32 bits of password
// check data SHA-256 additionally to 32-bit header CRC32.
byte csum[SIZE_PSWCHECK_CSUM];
Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
Raw->GetB(hd->Salt,SIZE_SALT50);
Raw->GetB(hd->InitV,SIZE_INITV);
if (hd->UsePswCheck)
{
Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
sha256_context ctx;
sha256_init(&ctx);
sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
// It is important to know if password check data is valid.
// If it is damaged and header CRC32 fails to detect it,
// archiver would refuse to decompress a possibly valid file.
// Since we want to be sure distinguishing a wrong password
// or corrupt file data, we use 64-bit password check data
// and to control its validity we use 32 bits of password
// check data SHA-256 additionally to 32-bit header CRC32.
byte csum[SIZE_PSWCHECK_CSUM];
Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
byte Digest[SHA256_DIGEST_SIZE];
sha256_done(&ctx, Digest);
byte Digest[SHA256_DIGEST_SIZE];
sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest);
hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
// RAR 5.21 and earlier set PswCheck field in service records to 0
// even if UsePswCheck was present.
if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
hd->UsePswCheck=0;
// RAR 5.21 and earlier set PswCheck field in service records to 0
// even if UsePswCheck was present.
if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
hd->UsePswCheck=0;
}
hd->SaltSet=true;
hd->CryptMethod=CRYPT_RAR50;
hd->Encrypted=true;
}
hd->SaltSet=true;
hd->CryptMethod=CRYPT_RAR50;
hd->Encrypted=true;
}
}
break;
#endif
case FHEXTRA_HASH:
{
FileHeader *hd=(FileHeader *)bb;
@@ -1103,31 +1165,27 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
if (Version!=0)
{
hd->Version=true;
wchar VerText[20];
swprintf(VerText,ASIZE(VerText),L";%u",Version);
wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
hd->FileName += L';' + std::to_wstring(Version);
}
}
break;
case FHEXTRA_REDIR:
{
hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
FILE_SYSTEM_REDIRECT RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
uint Flags=(uint)Raw->GetV();
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
size_t NameSize=(size_t)Raw->GetV();
char UtfName[NM*4];
*UtfName=0;
if (NameSize<ASIZE(UtfName)-1)
if (NameSize>0 && NameSize<MAXPATHSIZE)
{
Raw->GetB(UtfName,NameSize);
UtfName[NameSize]=0;
}
std::string UtfName(NameSize,0);
hd->RedirType=RedirType;
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
Raw->GetB(&UtfName[0],NameSize);
UtfToWide(&UtfName[0],hd->RedirName);
#ifdef _WIN_ALL
UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
UnixSlashToDos(hd->RedirName,hd->RedirName);
#endif
UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
}
}
break;
case FHEXTRA_UOWNER:
@@ -1184,8 +1242,8 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
// We cannot allocate too much memory here, because above
// we check FieldSize againt Raw size and we control that Raw size
// is sensible when reading headers.
hd->SubData.Alloc((size_t)FieldSize);
Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
hd->SubData.resize((size_t)FieldSize);
Raw->GetB(hd->SubData.data(),(size_t)FieldSize);
}
break;
}
@@ -1208,7 +1266,7 @@ size_t Archive::ReadHeader14()
Raw.GetB(Mark,4);
uint HeadSize=Raw.Get2();
if (HeadSize<7)
return false;
return 0;
byte Flags=Raw.Get1();
NextBlockPos=CurBlockPos+HeadSize;
CurHeaderType=HEAD_MAIN;
@@ -1231,7 +1289,7 @@ size_t Archive::ReadHeader14()
FileHead.FileHash.CRC32=Raw.Get2();
FileHead.HeadSize=Raw.Get2();
if (FileHead.HeadSize<21)
return false;
return 0;
uint FileTime=Raw.Get4();
FileHead.FileAttr=Raw.Get1();
FileHead.Flags=Raw.Get1()|LONG_BLOCK;
@@ -1255,12 +1313,13 @@ size_t Archive::ReadHeader14()
Raw.Read(NameSize);
char FileName[NM];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
IntToExt(FileName,FileName,ASIZE(FileName));
CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
// RAR 1.4 name size is stored in a single byte field and it can't
// exceed 255, so additional checks are not needed.
std::string FileName(NameSize,0);
Raw.GetB((byte *)&FileName[0],NameSize);
std::string NameA;
OemToExt(FileName,NameA);
CharToWide(NameA,FileHead.FileName);
ConvertNameCase(FileHead.FileName);
ConvertFileHeader(&FileHead);
@@ -1274,7 +1333,7 @@ size_t Archive::ReadHeader14()
#ifndef SFX_MODULE
void Archive::ConvertNameCase(wchar *Name)
void Archive::ConvertNameCase(std::wstring &Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
wcsupper(Name);
@@ -1292,7 +1351,7 @@ bool Archive::IsArcDir()
void Archive::ConvertAttributes()
{
#if defined(_WIN_ALL) || defined(_EMX)
#ifdef _WIN_ALL
if (FileHead.HSType!=HSYS_WINDOWS)
FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
#endif
@@ -1357,19 +1416,23 @@ void Archive::ConvertAttributes()
void Archive::ConvertFileHeader(FileHeader *hd)
{
/*
if (hd->HSType==HSYS_UNKNOWN)
if (hd->Dir)
hd->FileAttr=0x10;
else
hd->FileAttr=0x20;
*/
#ifdef _WIN_ALL
if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
ConvertToPrecomposed(hd->FileName);
#endif
for (wchar *s=hd->FileName;*s!=0;s++)
for (uint I=0;I<hd->FileName.size();I++)
{
wchar *s=&hd->FileName[I];
#ifdef _UNIX
// Backslash is the invalid character for Windows file headers,
// but it can present in Unix file names extracted in Unix.
@@ -1377,7 +1440,7 @@ void Archive::ConvertFileHeader(FileHeader *hd)
*s='_';
#endif
#if defined(_WIN_ALL) || defined(_EMX)
#ifdef _WIN_ALL
// RAR 5.0 archives do not use '\' as path separator, so if we see it,
// it means that it is a part of Unix file name, which we cannot
// extract in Windows.
@@ -1402,6 +1465,9 @@ void Archive::ConvertFileHeader(FileHeader *hd)
if (*s=='/' || *s=='\\' && Format!=RARFMT50)
*s=CPATHDIVIDER;
}
// Zeroes inside might be possible in broken Unicode names decoded with EncodeFileName::Decode.
TruncateAtZero(hd->FileName); // Ensure there are no zeroes inside of string.
}
@@ -1416,7 +1482,7 @@ int64 Archive::GetStartPos()
}
bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode)
{
if (BrokenHeader)
{
@@ -1424,7 +1490,7 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
ErrHandler.SetErrorCode(RARX_CRC);
return false;
}
if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK7:VER_UNPACK))
{
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
@@ -1441,7 +1507,9 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
if (SubHead.UnpSize>0x1000000)
{
// So huge allocation must never happen in valid archives.
// Prevent the excessive allocation. When reading to memory, normally
// this function operates with reasonably small blocks, such as
// the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream.
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
}
@@ -1449,7 +1517,7 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
SubDataIO.SetTestMode(true);
else
{
UnpData->Alloc((size_t)SubHead.UnpSize);
UnpData->resize((size_t)SubHead.UnpSize);
SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
}
}
@@ -1478,7 +1546,7 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
ErrHandler.SetErrorCode(RARX_CRC);
if (UnpData!=NULL)
UnpData->Reset();
UnpData->clear();
return false;
}
return true;

View File

@@ -1,191 +0,0 @@
#ifndef _RAR_ARRAY_
#define _RAR_ARRAY_
extern ErrorHandler ErrHandler;
template <class T> class Array
{
private:
T *Buffer;
size_t BufSize;
size_t AllocSize;
size_t MaxSize;
bool Secure; // Clean memory if true.
public:
Array();
Array(size_t Size);
Array(const Array &Src); // Copy constructor.
~Array();
inline void CleanData();
inline T& operator [](size_t Item) const;
inline T* operator + (size_t Pos);
inline size_t Size(); // Returns the size in items, not in bytes.
void Add(size_t Items);
void Alloc(size_t Items);
void Reset();
void SoftReset();
void operator = (Array<T> &Src);
void Push(T Item);
void Append(T *Item,size_t Count);
T* Addr(size_t Item) {return Buffer+Item;}
void SetMaxSize(size_t Size) {MaxSize=Size;}
T* Begin() {return Buffer;}
T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
void SetSecure() {Secure=true;}
};
template <class T> void Array<T>::CleanData()
{
Buffer=NULL;
BufSize=0;
AllocSize=0;
MaxSize=0;
Secure=false;
}
template <class T> Array<T>::Array()
{
CleanData();
}
template <class T> Array<T>::Array(size_t Size)
{
CleanData();
Add(Size);
}
// Copy constructor in case we need to pass an object as value.
template <class T> Array<T>::Array(const Array &Src)
{
CleanData();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
}
template <class T> Array<T>::~Array()
{
if (Buffer!=NULL)
{
if (Secure)
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
template <class T> inline T& Array<T>::operator [](size_t Item) const
{
return Buffer[Item];
}
template <class T> inline T* Array<T>::operator +(size_t Pos)
{
return Buffer+Pos;
}
template <class T> inline size_t Array<T>::Size()
{
return BufSize;
}
template <class T> void Array<T>::Add(size_t Items)
{
BufSize+=Items;
if (BufSize>AllocSize)
{
if (MaxSize!=0 && BufSize>MaxSize)
{
ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
ErrHandler.MemoryError();
}
size_t Suggested=AllocSize+AllocSize/4+32;
size_t NewSize=Max(BufSize,Suggested);
T *NewBuffer;
if (Secure)
{
NewBuffer=(T *)malloc(NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
if (Buffer!=NULL)
{
memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
else
{
NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
}
Buffer=NewBuffer;
AllocSize=NewSize;
}
}
template <class T> void Array<T>::Alloc(size_t Items)
{
if (Items>AllocSize)
Add(Items-BufSize);
else
BufSize=Items;
}
template <class T> void Array<T>::Reset()
{
if (Buffer!=NULL)
{
free(Buffer);
Buffer=NULL;
}
BufSize=0;
AllocSize=0;
}
// Reset buffer size, but preserve already allocated memory if any,
// so we can reuse it without wasting time to allocation.
template <class T> void Array<T>::SoftReset()
{
BufSize=0;
}
template <class T> void Array<T>::operator =(Array<T> &Src)
{
Reset();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
}
template <class T> void Array<T>::Push(T Item)
{
Add(1);
(*this)[Size()-1]=Item;
}
template <class T> void Array<T>::Append(T *Items,size_t Count)
{
size_t CurSize=Size();
Add(Count);
memcpy(Buffer+CurSize,Items,Count*sizeof(T));
}
#endif

View File

@@ -2,6 +2,20 @@
#include "rar.hpp"
static const byte blake2s_sigma[10][16] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
};
#ifdef USE_SSE
#include "blake2s_sse.cpp"
#endif
@@ -18,20 +32,6 @@ static const uint32 blake2s_IV[8] =
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};
static const byte blake2s_sigma[10][16] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
};
static inline void blake2s_set_lastnode( blake2s_state *S )
{
S->f[1] = ~0U;
@@ -134,11 +134,7 @@ void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
#ifdef USE_SSE
#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
if (_SSE_Version>=SSE_SSE2)
#else
if (_SSE_Version>=SSE_SSSE3)
#endif
blake2s_compress_sse( S, S->buf );
else
blake2s_compress( S, S->buf ); // Compress

View File

@@ -5,12 +5,9 @@
#define BLAKE2_DIGEST_SIZE 32
#define BLAKE2_THREADS_NUMBER 8
enum blake2s_constant
{
BLAKE2S_BLOCKBYTES = 64,
BLAKE2S_OUTBYTES = 32
};
// Use constexpr instead of enums for -std=c++20 compatibility.
constexpr size_t BLAKE2S_BLOCKBYTES = 64;
constexpr size_t BLAKE2S_OUTBYTES = 32;
// Alignment to 64 improves performance of both SSE and non-SSE versions.
// Alignment to n*16 is required for SSE version, so we selected 64.
@@ -20,10 +17,15 @@ enum blake2s_constant
// 'new' operator.
struct blake2s_state
{
enum { BLAKE_ALIGNMENT = 64 };
// Use constexpr instead of enums, because otherwise clang -std=c++20
// issues a warning about "arithmetic between different enumeration types"
// in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration.
static constexpr size_t BLAKE_ALIGNMENT = 64;
// buffer and uint32 h[8], t[2], f[2];
enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
// 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip.
// It might differ in later versions.
static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES;
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];

View File

@@ -1,15 +1,14 @@
// Based on public domain code written in 2012 by Samuel Neves
extern const byte blake2s_sigma[10][16];
// Initialization vector.
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
#ifdef _WIN_64
// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
// Constants for cyclic rotation.
static __m128i crotr8, crotr16;
#endif
#ifdef __GNUC__
__attribute__((target("sse2")))
#endif
static void blake2s_init_sse()
{
// We cannot initialize these 128 bit variables in place when declaring
@@ -24,28 +23,18 @@ static void blake2s_init_sse()
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
#ifdef _WIN_64
crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
#endif
}
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
#ifdef _WIN_32
// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
// to not use _mm_shuffle_epi8 here.
#define mm_rotr_epi32(r, c) ( \
_mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
#else
#define mm_rotr_epi32(r, c) ( \
c==8 ? _mm_shuffle_epi8(r,crotr8) \
: c==16 ? _mm_shuffle_epi8(r,crotr16) \
: _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
#endif
#define G1(row1,row2,row3,row4,buf) \
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
@@ -73,14 +62,6 @@ static void blake2s_init_sse()
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
#ifdef _WIN_64
// MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
// from stack operations, which are slower than this code.
#define _mm_set_epi32(i3,i2,i1,i0) \
_mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
_mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
#endif
// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
// and about the same in x64 mode in our test. Perhaps depends on compiler.
// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
@@ -101,6 +82,9 @@ static void blake2s_init_sse()
}
#ifdef __GNUC__
__attribute__((target("ssse3")))
#endif
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
{
__m128i row[4];

View File

@@ -20,7 +20,7 @@ void blake2sp_init( blake2sp_state *S )
blake2s_init_param( &S->R, 0, 1 ); // Init root.
for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
for( uint32 i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
S->R.last_node = 1;
@@ -49,6 +49,8 @@ void Blake2ThreadData::Update()
if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
_mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
#endif
// We tried to _forceinline blake2s_update and blake2s_compress_sse,
// but it didn't improve performance.
blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;

View File

@@ -13,8 +13,18 @@ void CommandData::Init()
{
RAROptions::Init();
*Command=0;
*ArcName=0;
Command.clear();
ArcName.clear();
ExtrPath.clear();
TempPath.clear();
SFXModule.clear();
CommentFile.clear();
ArcPath.clear();
ExclArcPath.clear();
LogName.clear();
EmailTo.clear();
UseStdin.clear();
FileLists=false;
NoMoreSwitches=false;
@@ -26,61 +36,52 @@ void CommandData::Init()
FileArgs.Reset();
ExclArgs.Reset();
InclArgs.Reset();
StoreArgs.Reset();
ArcNames.Reset();
NextVolSizes.Reset();
}
// Return the pointer to next position in the string and store dynamically
// allocated command line parameter in Par.
static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
{
const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
if (NextCmd==NULL)
return NULL;
size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
*Par=(wchar *)malloc(ParSize*sizeof(wchar));
if (*Par==NULL)
return NULL;
return GetCmdParam(CmdLine,*Par,ParSize);
StoreArgs.Reset();
#ifdef PROPAGATE_MOTW
MotwList.Reset();
#endif
Password.Clean();
NextVolSizes.clear();
#ifdef RARDLL
DllDestName.clear();
#endif
}
#if !defined(SFX_MODULE)
void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
{
*Command=0;
Command.clear();
NoMoreSwitches=false;
#ifdef CUSTOM_CMDLINE_PARSER
// In Windows we may prefer to implement our own command line parser
// to avoid replacing \" by " in standard parser. Such replacing corrupts
// destination paths like "dest path\" in extraction commands.
// Also our own parser is Unicode compatible.
const wchar *CmdLine=GetCommandLine();
std::wstring CmdLine=GetCommandLine();
std::wstring Param;
std::wstring::size_type Pos=0;
wchar *Par;
for (bool FirstParam=true;;FirstParam=false)
{
if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
if (!GetCmdParam(CmdLine,Pos,Param))
break;
if (!FirstParam) // First parameter is the executable name.
if (Preprocess)
PreprocessArg(Par);
PreprocessArg(Param.c_str());
else
ParseArg(Par);
free(Par);
ParseArg(Param.c_str());
}
#else
Array<wchar> Arg;
for (int I=1;I<argc;I++)
{
Arg.Alloc(strlen(argv[I])+1);
CharToWide(argv[I],&Arg[0],Arg.Size());
std::wstring Arg;
CharToWide(argv[I],Arg);
if (Preprocess)
PreprocessArg(&Arg[0]);
PreprocessArg(Arg.c_str());
else
ParseArg(&Arg[0]);
ParseArg(Arg.c_str());
}
#endif
if (!Preprocess)
@@ -90,7 +91,7 @@ void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
#if !defined(SFX_MODULE)
void CommandData::ParseArg(wchar *Arg)
void CommandData::ParseArg(const wchar *Arg)
{
if (IsSwitch(*Arg) && !NoMoreSwitches)
if (Arg[1]=='-' && Arg[2]==0)
@@ -98,34 +99,52 @@ void CommandData::ParseArg(wchar *Arg)
else
ProcessSwitch(Arg+1);
else
if (*Command==0)
if (Command.empty())
{
wcsncpyz(Command,Arg,ASIZE(Command));
Command=Arg;
*Command=toupperw(*Command);
Command[0]=toupperw(Command[0]);
// 'I' and 'S' commands can contain case sensitive strings after
// the first character, so we must not modify their case.
// 'S' can contain SFX name, which case is important in Unix.
if (*Command!='I' && *Command!='S')
if (Command[0]!='I' && Command[0]!='S')
wcsupper(Command);
if (Command[0]=='P') // Enforce -idq for print command.
{
MsgStream=MSG_ERRONLY;
SetConsoleMsgStream(MSG_ERRONLY);
}
}
else
if (*ArcName==0)
wcsncpyz(ArcName,Arg,ASIZE(ArcName));
if (ArcName.empty())
ArcName=Arg;
else
{
// Check if last character is the path separator.
size_t Length=wcslen(Arg);
wchar EndChar=Length==0 ? 0:Arg[Length-1];
bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
// Check if trailing path separator like path\ is present.
bool FolderArg=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
wchar CmdChar=toupperw(*Command);
// 2024.01.05: We were asked to support exotic d:. and d:.. paths.
if (IsDriveLetter(Arg) && Arg[2]=='.' && (Arg[3]==0 || Arg[3]=='.' && Arg[4]==0))
FolderArg=true;
// 2024.01.06: FindFile::FastFind check below fails in Windows 10 if
// "." or ".." points at disk root. So we enforce it for "." and ".."
// optionally preceded with some path like "..\..".
size_t L=Length;
if (L>0 && Arg[L-1]=='.' && (L==1 || L>=2 && (IsPathDiv(Arg[L-2]) ||
Arg[L-2]=='.' && (L==2 || L>=3 && IsPathDiv(Arg[L-3])))))
FolderArg=true;
wchar CmdChar=toupperw(Command[0]);
bool Add=wcschr(L"AFUM",CmdChar)!=NULL;
bool Extract=CmdChar=='X' || CmdChar=='E';
bool Repair=CmdChar=='R' && Command[1]==0;
if (EndSeparator && !Add)
wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
if (FolderArg && !Add)
ExtrPath=Arg;
else
if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS))
FileArgs.AddString(Arg);
@@ -142,10 +161,10 @@ void CommandData::ParseArg(wchar *Arg)
}
else // We use 'destpath\' when extracting and reparing.
if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
if (Found && FileData.IsDir && (Extract || Repair) && ExtrPath.empty())
{
wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
AddEndSlash(ExtrPath,ASIZE(ExtrPath));
ExtrPath=Arg;
AddEndSlash(ExtrPath);
}
else
FileArgs.AddString(Arg);
@@ -173,12 +192,12 @@ void CommandData::ParseDone()
#if !defined(SFX_MODULE)
void CommandData::ParseEnvVar()
{
char *EnvStr=getenv("RAR");
if (EnvStr!=NULL)
char *EnvVar=getenv("RARINISWITCHES");
if (EnvVar!=NULL)
{
Array<wchar> EnvStrW(strlen(EnvStr)+1);
CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
ProcessSwitchesString(&EnvStrW[0]);
std::wstring EnvStr;
CharToWide(EnvVar,EnvStr);
ProcessSwitchesString(EnvStr);
}
}
#endif
@@ -187,7 +206,7 @@ void CommandData::ParseEnvVar()
#if !defined(SFX_MODULE)
// Preprocess those parameters, which must be processed before the rest of
// command line. Return 'false' to stop further processing.
// command line.
void CommandData::PreprocessArg(const wchar *Arg)
{
if (IsSwitch(Arg[0]) && !NoMoreSwitches)
@@ -196,7 +215,7 @@ void CommandData::PreprocessArg(const wchar *Arg)
if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
NoMoreSwitches=true;
if (wcsicomp(Arg,L"cfg-")==0)
ConfigDisabled=true;
ProcessSwitch(Arg);
if (wcsnicomp(Arg,L"ilog",4)==0)
{
// Ensure that correct log file name is already set
@@ -208,13 +227,13 @@ void CommandData::PreprocessArg(const wchar *Arg)
{
// Process -sc before reading any file lists.
ProcessSwitch(Arg);
if (*LogName!=0)
if (!LogName.empty())
InitLogOptions(LogName,ErrlogCharset);
}
}
else
if (*Command==0)
wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
if (Command.empty())
Command=Arg; // Need for rar.ini.
}
#endif
@@ -232,10 +251,10 @@ void CommandData::ReadConfig()
Str++;
if (wcsnicomp(Str,L"switches=",9)==0)
ProcessSwitchesString(Str+9);
if (*Command!=0)
if (!Command.empty())
{
wchar Cmd[16];
wcsncpyz(Cmd,Command,ASIZE(Cmd));
wcsncpyz(Cmd,Command.c_str(),ASIZE(Cmd));
wchar C0=toupperw(Cmd[0]);
wchar C1=toupperw(Cmd[1]);
if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
@@ -255,14 +274,19 @@ void CommandData::ReadConfig()
#if !defined(SFX_MODULE)
void CommandData::ProcessSwitchesString(const wchar *Str)
void CommandData::ProcessSwitchesString(const std::wstring &Str)
{
wchar *Par;
while ((Str=AllocCmdParam(Str,&Par))!=NULL)
std::wstring Par;
std::wstring::size_type Pos=0;
while (GetCmdParam(Str,Pos,Par))
{
if (IsSwitch(*Par))
ProcessSwitch(Par+1);
free(Par);
if (IsSwitch(Par[0]))
ProcessSwitch(&Par[1]);
else
{
mprintf(St(MSwSyntaxError),Par.c_str());
ErrHandler.Exit(RARX_USERERROR);
}
}
}
#endif
@@ -272,6 +296,9 @@ void CommandData::ProcessSwitchesString(const wchar *Str)
void CommandData::ProcessSwitch(const wchar *Switch)
{
if (LargePageAlloc::ProcessSwitch(this,Switch))
return;
switch(toupperw(Switch[0]))
{
case '@':
@@ -288,7 +315,10 @@ void CommandData::ProcessSwitch(const wchar *Switch)
AppendArcNameToPath=APPENDARCNAME_DESTPATH;
else
if (Switch[2]=='1')
AppendArcNameToPath=APPENDARCNAME_OWNDIR;
AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR;
else
if (Switch[2]=='2')
AppendArcNameToPath=APPENDARCNAME_OWNDIR;
break;
#ifndef SFX_MODULE
case 'G':
@@ -307,13 +337,27 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'I':
IgnoreGeneralAttr=true;
break;
case 'N': // Reserved for archive name.
case 'M':
switch(toupperw(Switch[2]))
{
case 0:
case 'S':
ArcMetadata=ARCMETA_SAVE;
break;
case 'R':
ArcMetadata=ARCMETA_RESTORE;
break;
default:
BadSwitch(Switch);
break;
}
break;
case 'O':
AddArcOnly=true;
break;
case 'P':
wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
// Convert slashes here than before every comparison.
SlashToNative(Switch+2,ArcPath);
break;
case 'S':
SyncFiles=true;
@@ -324,7 +368,14 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
break;
case 'C':
if (Switch[2]==0)
if (Switch[2]!=0)
{
if (wcsicomp(Switch+1,L"FG-")==0)
ConfigDisabled=true;
else
BadSwitch(Switch);
}
else
switch(toupperw(Switch[1]))
{
case '-':
@@ -336,10 +387,15 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'L':
ConvertNames=NAMES_LOWERCASE;
break;
default:
BadSwitch(Switch);
break;
}
break;
case 'D':
if (Switch[2]==0)
if (Switch[2]!=0)
BadSwitch(Switch);
else
switch(toupperw(Switch[1]))
{
case 'S':
@@ -351,6 +407,9 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'F':
DeleteFiles=true;
break;
default:
BadSwitch(Switch);
break;
}
break;
case 'E':
@@ -371,6 +430,13 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case '3':
ExclPath=EXCL_ABSPATH;
break;
case '4':
// Convert slashes here than before every comparison.
SlashToNative(Switch+3,ExclArcPath);
break;
default:
BadSwitch(Switch);
break;
}
break;
default:
@@ -397,13 +463,17 @@ void CommandData::ProcessSwitch(const wchar *Switch)
EncryptHeaders=true;
if (Switch[2]!=0)
{
// We use this code for other archive formats too, so MAXPASSWORD
// instead of MAXPASSWORD_RAR.
if (wcslen(Switch+2)>=MAXPASSWORD)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
Password.Set(Switch+2);
cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
else
if (!Password.IsSet())
{
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL);
eprintf(L"\n");
}
break;
@@ -415,7 +485,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'I':
if (wcsnicomp(Switch+1,L"LOG",3)==0)
{
wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
LogName=Switch[4]!=0 ? Switch+4:DefLogName;
break;
}
if (wcsnicomp(Switch+1,L"SND",3)==0)
@@ -433,12 +503,12 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
if (wcsnicomp(Switch+1,L"EML",3)==0)
{
wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
EmailTo=Switch[4]!=0 ? Switch+4:L"@";
break;
}
if (wcsicomp(Switch+1,L"M")==0)
if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv.
{
MoreInfo=true;
VerboseOutput=true;
break;
}
if (wcsicomp(Switch+1,L"NUL")==0)
@@ -465,6 +535,12 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'P':
DisablePercentage=true;
break;
case 'N':
DisableNames=true;
break;
case 'V':
VerboseOutput=true;
break;
}
break;
}
@@ -534,13 +610,14 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
switch(toupperw(*(Str++)))
{
case 'T': Type=FILTER_PPM; break;
// case 'T': Type=FILTER_TEXT; break;
case 'E': Type=FILTER_E8; break;
case 'D': Type=FILTER_DELTA; break;
case 'A': Type=FILTER_AUDIO; break;
case 'C': Type=FILTER_RGB; break;
case 'I': Type=FILTER_ITANIUM; break;
case 'R': Type=FILTER_ARM; break;
// case 'A': Type=FILTER_AUDIO; break;
// case 'C': Type=FILTER_RGB; break;
// case 'R': Type=FILTER_ARM; break;
case 'L': Type=FILTER_LONGRANGE; break;
case 'X': Type=FILTER_EXHAUSTIVE; break;
}
if (*Str=='+' || *Str=='-')
State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE;
@@ -550,42 +627,74 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
}
break;
case 'M':
break;
case 'D':
break;
case 'S':
{
wchar StoreNames[1024];
wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames));
wchar *Names=StoreNames;
while (*Names!=0)
bool SetDictLimit=toupperw(Switch[2])=='X';
uint64 Size=atoiw(Switch+(SetDictLimit ? 3 : 2));
wchar LastChar=toupperw(Switch[wcslen(Switch)-1]);
if (IsDigit(LastChar))
LastChar=SetDictLimit ? 'G':'M'; // Treat -md128 as -md128m and -mdx32 as -mdx32g.
switch(LastChar)
{
wchar *End=wcschr(Names,';');
if (End!=NULL)
*End=0;
if (*Names=='.')
Names++;
wchar Mask[NM];
if (wcspbrk(Names,L"*?.")==NULL)
swprintf(Mask,ASIZE(Mask),L"*.%ls",Names);
else
wcsncpyz(Mask,Names,ASIZE(Mask));
StoreArgs.AddString(Mask);
if (End==NULL)
case 'K':
Size*=1024;
break;
Names=End+1;
case 'M':
Size*=1024*1024;
break;
case 'G':
Size*=1024*1024*1024;
break;
default:
BadSwitch(Switch);
}
// 2023.07.22: For 4 GB and less we also check that it is power of 2,
// so archives are compatible with RAR 5.0+.
// We allow Size>PACK_MAX_DICT here, so we can use -md[x] to unpack
// archives created by future versions with higher PACK_MAX_DICT.
uint Flags;
if ((Size=Archive::GetWinSize(Size,Flags))==0 ||
Size<=0x100000000ULL && !IsPow2(Size))
BadSwitch(Switch);
else
if (SetDictLimit)
WinSizeLimit=Size;
else
{
WinSize=Size;
}
}
break;
case 'E':
if (toupperw(Switch[2])=='S' && Switch[3]==0)
SkipEncrypted=true;
break;
case 'L':
if (toupperw(Switch[2])=='P')
{
UseLargePages=true;
if (!LargePageAlloc::IsPrivilegeAssigned() && LargePageAlloc::AssignConfirmation())
{
LargePageAlloc::AssignPrivilege();
// Quit immediately. We do not want to interrupt the current copy
// archive processing with reboot after assigning privilege.
SetupComplete=true;
}
}
break;
case 'M':
break;
case 'S':
GetBriefMaskList(Switch[2]==0 ? DefaultStoreList:Switch+2,StoreArgs);
break;
#ifdef RAR_SMP
case 'T':
Threads=atoiw(Switch+2);
if (Threads>MaxPoolThreads || Threads<1)
BadSwitch(Switch);
else
{
}
break;
#endif
default:
@@ -631,8 +740,31 @@ void CommandData::ProcessSwitch(const wchar *Switch)
#ifdef SAVE_LINKS
case 'L':
SaveSymLinks=true;
if (toupperw(Switch[2])=='A')
AbsoluteLinks=true;
for (uint I=2;Switch[I]!=0;I++)
switch(toupperw(Switch[I]))
{
case 'A':
AbsoluteLinks=true;
break;
case '-':
SkipSymLinks=true;
break;
default:
BadSwitch(Switch);
break;
}
break;
#endif
#ifdef PROPAGATE_MOTW
case 'M':
{
MotwAllFields=Switch[2]=='1';
const wchar *Sep=wcschr(Switch+2,'=');
if (Switch[2]=='-')
MotwList.Reset();
else
GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,MotwList);
}
break;
#endif
#ifdef _WIN_ALL
@@ -641,6 +773,10 @@ void CommandData::ProcessSwitch(const wchar *Switch)
AllowIncompatNames=true;
break;
#endif
case 'P':
ExtrPath=Switch+2;
AddEndSlash(ExtrPath);
break;
case 'R':
Overwrite=OVERWRITE_AUTORENAME;
break;
@@ -660,11 +796,13 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'P':
if (Switch[1]==0)
{
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL);
eprintf(L"\n");
}
else
{
if (wcslen(Switch+1)>=MAXPASSWORD)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
Password.Set(Switch+1);
cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
@@ -731,7 +869,52 @@ void CommandData::ProcessSwitch(const wchar *Switch)
switch(toupperw(Switch[1]))
{
case 0:
case '=':
Solid|=SOLID_NORMAL;
if (Switch[1]=='=')
{
uint Par=0;
for (const wchar *S=Switch+2;*S!=0;S++)
{
if (IsDigit(*S))
Par=Par*10+*S-'0';
switch(toupperw(*S))
{
case '-':
Solid=SOLID_NONE;
break;
case 'D':
Solid|=SOLID_VOLUME_DEPENDENT;
break;
case 'E':
Solid|=SOLID_FILEEXT;
break;
case 'F':
Solid|=SOLID_COUNT;
SolidCount=Par;
break;
case 'K':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL;
break;
case 'M':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL*1024LL;
break;
case 'G':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL*1024LL*1024LL;
break;
case 'R':
Solid=SOLID_RESET;
break;
case 'V':
Solid|=SOLID_VOLUME_INDEPENDENT;
break;
}
}
}
break;
case '-':
Solid=SOLID_NONE;
@@ -745,13 +928,22 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'D':
Solid|=SOLID_VOLUME_DEPENDENT;
break;
case 'I':
ProhibitConsoleInput();
// We do not assign the archive name automatically for -si
// if archive name is omitted and require the archive name to
// present always. Otherwise for"type arc.rar|rar x -si arc2.rar"
// if arc2.rar is a dummy archive name or file inside of arc.rar,
// which needs to be extracted.
UseStdin=Switch[2] ? Switch+2:L"stdin";
break;
case 'L':
if (IsDigit(Switch[2]))
FileSizeLess=atoilw(Switch+2);
FileSizeLess=GetModSize(Switch+2,1);
break;
case 'M':
if (IsDigit(Switch[2]))
FileSizeMore=atoilw(Switch+2);
FileSizeMore=GetModSize(Switch+2,1);
break;
case 'C':
{
@@ -809,12 +1001,6 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'T':
switch(toupperw(Switch[1]))
{
case 'K':
ArcTime=ARCTIME_KEEP;
break;
case 'L':
ArcTime=ARCTIME_LATEST;
break;
case 'O':
SetTimeFilters(Switch+2,true,true);
break;
@@ -866,8 +1052,8 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
break;
case 'W':
wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
AddEndSlash(TempPath,ASIZE(TempPath));
TempPath=Switch+1;
AddEndSlash(TempPath);
break;
case 'Y':
AllYes=true;
@@ -876,10 +1062,10 @@ void CommandData::ProcessSwitch(const wchar *Switch)
if (Switch[1]==0)
{
// If comment file is not specified, we read data from stdin.
wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
CommentFile=L"stdin";
}
else
wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
CommentFile=Switch+1;
break;
case '?' :
OutHelp(RARX_SUCCESS);
@@ -906,34 +1092,41 @@ void CommandData::ProcessCommand()
#ifndef SFX_MODULE
const wchar *SingleCharCommands=L"FUADPXETK";
if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
const wchar *ArcExt=GetExt(ArcName);
// RAR -mlp command is the legitimate way to assign the required privilege.
if (Command.empty() && UseLargePages || SetupComplete)
return;
if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || ArcName.empty())
OutHelp(Command.empty() ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
size_t ExtPos=GetExtPos(ArcName);
#ifdef _UNIX
if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
wcsncatz(ArcName,L".rar",ASIZE(ArcName));
// If we want to update an archive without extension, in Windows we can use
// "arcname." and it will be treated as "arcname". In Unix "arcname"
// and "arcname." are two different names, so we check if "arcname" exists
// and do not append ".rar", allowing user to update such archive.
if (ExtPos==std::wstring::npos && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
ArcName+=L".rar";
#else
if (ArcExt==NULL)
wcsncatz(ArcName,L".rar",ASIZE(ArcName));
if (ExtPos==std::wstring::npos)
ArcName+=L".rar";
#endif
// Treat arcname.part1 as arcname.part1.rar.
if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) &&
!FileExist(ArcName))
if (ExtPos!=std::wstring::npos && wcsnicomp(&ArcName[ExtPos],L".part",5)==0 &&
IsDigit(ArcName[ExtPos+5]) && !FileExist(ArcName))
{
wchar Name[NM];
wcsncpyz(Name,ArcName,ASIZE(Name));
wcsncatz(Name,L".rar",ASIZE(Name));
std::wstring Name=ArcName+L".rar";
if (FileExist(Name))
wcsncpyz(ArcName,Name,ASIZE(ArcName));
ArcName=Name;
}
if (wcschr(L"AFUMD",*Command)==NULL)
if (wcschr(L"AFUMD",Command[0])==NULL && UseStdin.empty())
{
if (GenerateArcName)
{
const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask;
GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false);
GenerateArchiveName(ArcName,Mask,false);
}
StringList ArcMasks;
@@ -967,12 +1160,22 @@ void CommandData::ProcessCommand()
OutHelp(RARX_USERERROR);
#endif
}
// Since messages usually include '\n' in the beginning, we also issue
// the final '\n'. It is especially important in Unix, where otherwise
// the shell can display the prompt on the same line as the last message.
// mprintf is blocked with -idq and if error messages had been displayed
// in this mode, we use eprintf to separate them from shell prompt.
// If nothing was displayed with -idq, we avoid the excessive empty line.
if (!BareOutput)
mprintf(L"\n");
if (MsgStream==MSG_ERRONLY && IsConsoleOutputPresent())
eprintf(L"\n");
else
mprintf(L"\n");
}
void CommandData::AddArcName(const wchar *Name)
void CommandData::AddArcName(const std::wstring &Name)
{
ArcNames.AddString(Name);
}
@@ -984,9 +1187,15 @@ bool CommandData::GetArcName(wchar *Name,int MaxSize)
}
bool CommandData::GetArcName(std::wstring &Name)
{
return ArcNames.GetString(Name);
}
bool CommandData::IsSwitch(int Ch)
{
#if defined(_WIN_ALL) || defined(_EMX)
#ifdef _WIN_ALL
return Ch=='-' || Ch=='/';
#else
return Ch=='-';
@@ -1012,7 +1221,7 @@ uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
case 'V':
Attr|=S_IFCHR;
break;
#elif defined(_WIN_ALL) || defined(_EMX)
#elif defined(_WIN_ALL)
case 'R':
Attr|=0x1;
break;
@@ -1036,21 +1245,6 @@ uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
#ifndef SFX_MODULE
bool CommandData::CheckWinSize()
{
// Define 0x100000000 as macro to avoid troubles with older compilers.
const uint64 MaxDictSize=INT32TO64(1,0);
// Limit the dictionary size to 4 GB.
for (uint64 I=0x10000;I<=MaxDictSize;I*=2)
if (WinSize==I)
return true;
WinSize=0x400000;
return false;
}
#endif
#ifndef SFX_MODULE
void CommandData::ReportWrongSwitches(RARFORMAT Format)
{
@@ -1080,3 +1274,57 @@ void CommandData::ReportWrongSwitches(RARFORMAT Format)
}
}
#endif
// Get size for string with optional trailing modifiers like "100m".
int64 CommandData::GetModSize(const wchar *S,uint DefMultiplier)
{
int64 Size=0,FloatingDivider=0;
for (uint I=0;S[I]!=0;I++)
if (IsDigit(S[I]))
{
Size=Size*10+S[I]-'0';
FloatingDivider*=10;
}
else
if (S[I]=='.')
FloatingDivider=1;
if (*S!=0)
{
const wchar *ModList=L"bBkKmMgGtT";
const wchar *Mod=wcschr(ModList,S[wcslen(S)-1]);
if (Mod==nullptr)
Size*=DefMultiplier;
else
for (ptrdiff_t I=2;I<=Mod-ModList;I+=2)
Size*=((Mod-ModList)&1)!=0 ? 1000:1024;
}
if (FloatingDivider!=0)
Size/=FloatingDivider;
return Size;
}
// Treat the list like rar;zip as *.rar;*.zip for -ms and similar switches.
void CommandData::GetBriefMaskList(const std::wstring &Masks,StringList &Args)
{
size_t Pos=0;
while (Pos<Masks.size())
{
if (Masks[Pos]=='.')
Pos++;
size_t EndPos=Masks.find(';',Pos);
std::wstring Mask=Masks.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos);
if (Mask.find_first_of(L"*?.")==std::wstring::npos)
Mask.insert(0,L"*.");
Args.AddString(Mask);
if (EndPos==std::wstring::npos)
break;
Pos=EndPos+1;
}
}

View File

@@ -1,8 +1,14 @@
#ifndef _RAR_CMDDATA_
#define _RAR_CMDDATA_
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
// In Windows we implement our own command line parser to avoid replacing
// \" by " in standard parser. Such replacing corrupts destination paths
// like "dest path\" in extraction commands.
#define CUSTOM_CMDLINE_PARSER
#endif
#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx"
#define DefaultStoreList L"7z;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst"
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
@@ -11,7 +17,6 @@ enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1};
class CommandData:public RAROptions
{
private:
void ProcessSwitchesString(const wchar *Str);
void ProcessSwitch(const wchar *Switch);
void BadSwitch(const wchar *Switch);
uint GetExclAttr(const wchar *Str,bool &Dir);
@@ -19,6 +24,7 @@ class CommandData:public RAROptions
void SetTimeFilters(const wchar *Mod,bool Before,bool Age);
void SetStoreTimeMode(const wchar *S);
#endif
int64 GetModSize(const wchar *S,uint DefMultiplier);
bool FileLists;
bool NoMoreSwitches;
@@ -29,42 +35,69 @@ class CommandData:public RAROptions
void Init();
void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
void ParseArg(wchar *ArgW);
void ParseArg(const wchar *ArgW);
void ParseDone();
void ParseEnvVar();
void ReadConfig();
void PreprocessArg(const wchar *Arg);
void ProcessSwitchesString(const std::wstring &Str);
void OutTitle();
void OutHelp(RAR_EXIT ExitCode);
bool IsSwitch(int Ch);
bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode);
bool ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
static bool CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode);
bool ExclDirByAttr(uint FileAttr);
bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta);
bool SizeCheck(int64 Size);
bool AnyFiltersActive();
int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
bool Flags,wchar *MatchedArg,uint MatchedArgSize);
bool Flags,std::wstring *MatchedArg);
void ProcessCommand();
void AddArcName(const wchar *Name);
void AddArcName(const std::wstring &Name);
bool GetArcName(wchar *Name,int MaxSize);
bool CheckWinSize();
bool GetArcName(std::wstring &Name);
int GetRecoverySize(const wchar *Str,int DefSize);
#ifndef SFX_MODULE
void ReportWrongSwitches(RARFORMAT Format);
#endif
wchar Command[NM+16];
void GetBriefMaskList(const std::wstring &Masks,StringList &Args);
wchar ArcName[NM];
std::wstring Command;
std::wstring ArcName;
std::wstring ExtrPath;
std::wstring TempPath;
std::wstring SFXModule;
std::wstring CommentFile;
std::wstring ArcPath; // For -ap<path>.
std::wstring ExclArcPath; // For -ep4<path> switch.
std::wstring LogName;
std::wstring EmailTo;
// Read data from stdin and store in archive under a name specified here
// when archiving. Read an archive from stdin if any non-empty string
// is specified here when extracting.
std::wstring UseStdin;
StringList FileArgs;
StringList ExclArgs;
StringList InclArgs;
StringList ArcNames;
StringList StoreArgs;
#ifdef PROPAGATE_MOTW
StringList MotwList; // Extensions to assign the mark of the web.
#endif
SecPassword Password;
std::vector<int64> NextVolSizes;
#ifdef RARDLL
std::wstring DllDestName;
#endif
};
#endif

View File

@@ -1,7 +1,7 @@
// Return 'true' if we need to exclude the file from processing as result
// of -x switch. If CheckInclList is true, we also check the file against
// the include list created with -n switch.
bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
bool CommandData::ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
{
if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
return true;
@@ -13,17 +13,21 @@ bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,b
}
bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode)
bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode)
{
wchar *Name=ConvertPath(CheckName,NULL,0);
wchar FullName[NM];
wchar CurMask[NM];
*FullName=0;
std::wstring Name,FullName,CurMask;
ConvertPath(&CheckName,&Name);
Args->Rewind();
while (Args->GetString(CurMask,ASIZE(CurMask)))
while (Args->GetString(CurMask))
{
wchar *LastMaskChar=PointToLastChar(CurMask);
bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
#ifdef _WIN_ALL
// 2025.09.11: Unix allows DOS slashes as a part of file name, so we do not
// convert it for Unix. In Windows we wish -xdir\file and -xdir/file both
// to exclude the file.
UnixSlashToDos(CurMask,CurMask);
#endif
wchar LastMaskChar=GetLastChar(CurMask);
bool DirMask=IsPathDiv(LastMaskChar); // Mask for directories only.
if (Dir)
{
@@ -33,16 +37,33 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo
// We process the directory and have the directory exclusion mask.
// So let's convert "mask\" to "mask" and process it normally.
*LastMaskChar=0;
CurMask.pop_back();
}
else
{
// REMOVED, we want -npath\* to match empty folders too.
// If mask has wildcards in name part and does not have the trailing
// '\' character, we cannot use it for directories.
// if (IsWildcard(PointToName(CurMask)))
// continue;
// This code doesn't allow to apply -n and -x wildcard masks without
// trailing slash to folders unless these masks are * and *.*.
// See the changes history below.
// 2023.03.26: Previously we removed this code completely to let
// 'rar a arc dir -ndir\path\*' include empty folders in 'path' too.
// But then we received an email from user not willing -x*.avi to
// exclude folders like dir.avi with non-avi files. Also rar.txt
// mentions that masks like *.avi exclude only files. Initially
// we wanted masks like -npath\* or -xpath\* to match the entire
// contents of path including empty folders and added the special
// check for "*" and "*.*". But this is not very straightforward,
// when *.* and *.avi are processed differently, especially taking
// into account that we can specify the exact folder name without
// wildcards to process it and masks like 'dir*\' can be used to
// exclude folders. So we decided to skip all usual wildcard masks
// for folders.
// 2023.11.22: We returned the special check for "*" and "*.*",
// because users expected 'rar a arc dir -xdir\*' to exclude
// everything including subfolders in 'dir'. For now we returned it
// both for -n and -x, but we can limit it to -x only if needed.
std::wstring Name=PointToName(CurMask);
if (IsWildcard(Name) && Name!=L"*" && Name!=L"*.*")
continue;
}
}
else
@@ -54,7 +75,7 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo
// is excluded from further scanning.
if (DirMask)
wcsncatz(CurMask,L"*",ASIZE(CurMask));
CurMask+=L"*";
}
#ifndef SFX_MODULE
@@ -66,19 +87,20 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo
// correctly. Moreover, removing "*\" from mask would break
// the comparison, because now all names have the path.
if (*FullName==0)
ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
if (FullName.empty())
ConvertNameToFull(CheckName,FullName);
if (CmpName(CurMask,FullName,MatchMode))
return true;
}
else
#endif
{
wchar NewName[NM+2],*CurName=Name;
std::wstring CurName=Name;
// Important to convert before "*\" check below, so masks like
// d:*\something are processed properly.
wchar *CmpMask=ConvertPath(CurMask,NULL,0);
size_t MaskOffset=ConvertPath(&CurMask,nullptr);
std::wstring CmpMask=CurMask.substr(MaskOffset);
if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
{
@@ -86,10 +108,9 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo
// but also in the current directory. We convert the name
// from 'name' to '.\name' to be matched by "*\" part even if it is
// in current directory.
NewName[0]='.';
NewName[1]=CPATHDIVIDER;
wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
CurName=NewName;
CurName=L'.';
CurName+=CPATHDIVIDER;
CurName+=Name;
}
if (CmpName(CmpMask,CurName,MatchMode))
@@ -262,6 +283,8 @@ bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
// Return 'true' if we need to exclude the file from processing.
bool CommandData::SizeCheck(int64 Size)
{
if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size.
return false;
if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
return true;
if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
@@ -275,10 +298,10 @@ bool CommandData::SizeCheck(int64 Size)
// Return 0 if file must not be processed or a number of matched parameter otherwise.
int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
bool Flags,wchar *MatchedArg,uint MatchedArgSize)
bool Flags,std::wstring *MatchedArg)
{
if (MatchedArg!=NULL && MatchedArgSize>0)
*MatchedArg=0;
if (MatchedArg!=NULL)
MatchedArg->clear();
bool Dir=FileHead.Dir;
if (ExclCheck(FileHead.FileName,Dir,false,true))
return 0;
@@ -287,23 +310,28 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy
return 0;
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
return 0;
if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 ||
FileHead.Dir && !InclDir))
if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
(!FileHead.Dir || !InclDir))
return 0;
if (!Dir && SizeCheck(FileHead.UnpSize))
return 0;
#endif
wchar *ArgName;
std::wstring ArgName;
FileArgs.Rewind();
for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
for (int StringCount=1;FileArgs.GetString(ArgName);StringCount++)
{
// Ensure that both parameters of CmpName are either C++ strings or
// pointers, so we avoid time consuming string construction for one of
// parameters in this expensive loop.
if (CmpName(ArgName,FileHead.FileName,MatchType))
{
if (ExactMatch!=NULL)
*ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
if (MatchedArg!=NULL)
wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
*MatchedArg=ArgName;
return StringCount;
}
}
return 0;
}

View File

@@ -2,7 +2,7 @@ void CommandData::OutTitle()
{
if (BareOutput || DisableCopyright)
return;
#if defined(__GNUC__) && defined(SFX_MODULE)
#ifdef SFX_MODULE
mprintf(St(MCopyrightS));
#else
#ifndef SILENT
@@ -61,26 +61,24 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,
MCHelpSwY
MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP,
MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwMLP,
MCHelpSwN,MCHelpSwNa,MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,
MCHelpSwOM,MCHelpSwOP,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,MCHelpSwR,
MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwTA,
MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,
MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY
#endif
};
for (uint I=0;I<ASIZE(Help);I++)
{
#ifndef SFX_MODULE
if (CmpMSGID(Help[I],MCHelpSwV))
continue;
#ifndef _WIN_ALL
static MSGID Win32Only[]={
MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
MCHelpSwEP2,MCHelpSwMLP,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
};
bool Found=false;
for (uint J=0;J<ASIZE(Win32Only);J++)
@@ -92,11 +90,18 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
if (Found)
continue;
#endif
#ifdef _UNIX
if (CmpMSGID(Help[I],MRARTitle2))
{
mprintf(St(MFwrSlTitle2));
continue;
}
#endif
#if !defined(_UNIX) && !defined(_WIN_ALL)
if (CmpMSGID(Help[I],MCHelpSwOW))
continue;
#endif
#if !defined(_WIN_ALL) && !defined(_EMX)
#ifndef _WIN_ALL
if (CmpMSGID(Help[I],MCHelpSwAC))
continue;
#endif

View File

@@ -1,8 +1,8 @@
inline unsigned int RangeCoder::GetChar()
inline byte RangeCoder::GetChar()
{
return(UnpackRead->GetChar());
return UnpackRead->GetChar();
}
@@ -11,8 +11,8 @@ void RangeCoder::InitDecoder(Unpack *UnpackRead)
RangeCoder::UnpackRead=UnpackRead;
low=code=0;
range=uint(-1);
for (int i=0;i < 4;i++)
range=0xffffffff;
for (uint i = 0; i < 4; i++)
code=(code << 8) | GetChar();
}

View File

@@ -11,7 +11,7 @@ class RangeCoder
inline uint GetCurrentShiftCount(uint SHIFT);
inline void Decode();
inline void PutChar(unsigned int c);
inline unsigned int GetChar();
inline byte GetChar();
uint low, code, range;
struct SUBRANGE

View File

@@ -17,13 +17,16 @@ class PackDef
static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3;
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3;
static const uint LOW_DIST_REP_COUNT = 16;
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
static const uint DC = 64;
static const uint DCB = 64; // Base distance codes up to 4 GB.
static const uint DCX = 80; // Extended distance codes up to 1 TB.
static const uint LDC = 16;
static const uint RC = 44;
static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC;
static const uint HUFF_TABLE_SIZEB = NC + DCB + RC + LDC;
static const uint HUFF_TABLE_SIZEX = NC + DCX + RC + LDC;
static const uint BC = 20;
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
@@ -42,10 +45,6 @@ class PackDef
// Largest alphabet size among all values listed above.
static const uint LARGEST_TABLE_SIZE = 306;
enum {
CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE,
CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA
};
};
@@ -53,7 +52,10 @@ enum FilterType {
// These values must not be changed, because we use them directly
// in RAR5 compression and decompression code.
FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_TEXT,
// These values can be changed.
FILTER_LONGRANGE,FILTER_EXHAUSTIVE,FILTER_NONE
};
#endif

View File

@@ -3,8 +3,8 @@
static MESSAGE_TYPE MsgStream=MSG_STDOUT;
static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
const int MaxMsgSize=2*NM+2048;
static bool ProhibitInput=false;
static bool ConsoleOutputPresent=false;
static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
@@ -61,47 +61,53 @@ void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)
}
void ProhibitConsoleInput()
{
ProhibitInput=true;
}
#ifndef SILENT
static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
{
// This buffer is for format string only, not for entire output,
// so it can be short enough.
wchar fmtw[1024];
PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
ConsoleOutputPresent=true;
// No need for PrintfPrepareFmt here, vwstrprintf calls it.
std::wstring s=vwstrprintf(fmt,arglist);
ReplaceEsc(s);
#ifdef _WIN_ALL
safebuf wchar Msg[MaxMsgSize];
if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
{
HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
DWORD Written;
if (RedirectCharset==RCH_UNICODE)
WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL);
else
{
// Avoid Unicode for redirect in Windows, it does not work with pipes.
safebuf char MsgA[MaxMsgSize];
std::string MsgA;
if (RedirectCharset==RCH_UTF8)
WideToUtf(Msg,MsgA,ASIZE(MsgA));
WideToUtf(s,MsgA);
else
WideToChar(Msg,MsgA,ASIZE(MsgA));
WideToChar(s,MsgA);
if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding.
CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding.
// We already converted \n to \r\n above, so we use WriteFile instead
// of C library to avoid unnecessary additional conversion.
WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
WriteFile(hOut,MsgA.data(),(DWORD)MsgA.size(),&Written,NULL);
}
return;
}
// MSVC2008 vfwprintf writes every character to console separately
// and it is too slow. We use direct WriteConsole call instead.
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
DWORD Written;
WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL);
#else
vfwprintf(dest,fmtw,arglist);
fputws(s.c_str(),dest);
// We do not use setbuf(NULL) in Unix (see comments in InitConsole).
fflush(dest);
#endif
@@ -141,81 +147,109 @@ void eprintf(const wchar *fmt,...)
#ifndef SILENT
static void GetPasswordText(wchar *Str,uint MaxLength)
static void QuitIfInputProhibited()
{
if (MaxLength==0)
return;
// We cannot handle user prompts if -si is used to read file or archive data
// from stdin.
if (ProhibitInput)
{
mprintf(St(MStdinNoInput));
ErrHandler.Exit(RARX_FATAL);
}
}
static void GetPasswordText(std::wstring &Str)
{
QuitIfInputProhibited();
if (StdinRedirected)
getwstr(Str,MaxLength); // Read from pipe or redirected file.
getwstr(Str); // Read from pipe or redirected file.
else
{
#ifdef _WIN_ALL
HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
DWORD ConInMode,ConOutMode;
DWORD Read=0;
DWORD ConInMode;
GetConsoleMode(hConIn,&ConInMode);
GetConsoleMode(hConOut,&ConOutMode);
SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT.
std::vector<wchar> Buf(MAXPASSWORD);
// We prefer ReadConsole to ReadFile, so we can read Unicode input.
DWORD Read=0;
ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL);
Buf[Read]=0;
Str=Buf.data();
cleandata(Buf.data(),Buf.size()*sizeof(Buf[0]));
ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
Str[Read]=0;
SetConsoleMode(hConIn,ConInMode);
SetConsoleMode(hConOut,ConOutMode);
// 2023.03.12: Previously we checked for presence of "\n" in entered
// passwords, supposing that truncated strings do not include it.
// We did it to read the rest of excessively long string, so it is not
// read later as the second password for -p switch. But this "\n" check
// doesn't seem to work in Windows 10 anymore and "\r" is present even
// in truncated strings. Also we increased MAXPASSWORD, so it is larger
// than MAXPASSWORD_RAR. Thus we removed this check as not working
// and not that necessary. Low level FlushConsoleInputBuffer doesn't help
// for high level ReadConsole, which in line input mode seems to store
// the rest of string in its own internal buffer.
#else
char StrA[MAXPASSWORD];
#if defined(_EMX) || defined (__VMS)
fgets(StrA,ASIZE(StrA)-1,stdin);
std::vector<char> StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters.
#ifdef __VMS
fgets(StrA.data(),StrA.size()-1,stdin);
#elif defined(__sun)
strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
strncpyz(StrA.data(),getpassphrase(""),StrA.size());
#else
strncpyz(StrA,getpass(""),ASIZE(StrA));
strncpyz(StrA.data(),getpass(""),StrA.size());
#endif
CharToWide(StrA,Str,MaxLength);
cleandata(StrA,sizeof(StrA));
CharToWide(StrA.data(),Str);
cleandata(StrA.data(),StrA.size()*sizeof(StrA[0]));
#endif
}
Str[MaxLength-1]=0;
RemoveLF(Str);
}
#endif
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password)
{
if (!StdinRedirected)
uiAlarm(UIALARM_QUESTION);
while (true)
{
if (!StdinRedirected)
// if (!StdinRedirected)
if (Type==UIPASSWORD_GLOBAL)
eprintf(L"\n%s: ",St(MAskPsw));
else
eprintf(St(MAskPswFor),FileName);
eprintf(St(MAskPswFor),FileName.c_str());
wchar PlainPsw[MAXPASSWORD];
GetPasswordText(PlainPsw,ASIZE(PlainPsw));
if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL)
std::wstring PlainPsw;
GetPasswordText(PlainPsw);
if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL)
return false;
if (PlainPsw.size()>=MAXPASSWORD)
{
PlainPsw.erase(MAXPASSWORD-1);
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
}
if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
{
eprintf(St(MReAskPsw));
wchar CmpStr[MAXPASSWORD];
GetPasswordText(CmpStr,ASIZE(CmpStr));
if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
std::wstring CmpStr;
GetPasswordText(CmpStr);
if (CmpStr.empty() || PlainPsw!=CmpStr)
{
eprintf(St(MNotMatchPsw));
cleandata(PlainPsw,sizeof(PlainPsw));
cleandata(CmpStr,sizeof(CmpStr));
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
continue;
}
cleandata(CmpStr,sizeof(CmpStr));
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
}
Password->Set(PlainPsw);
cleandata(PlainPsw,sizeof(PlainPsw));
Password->Set(PlainPsw.c_str());
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
break;
}
return true;
@@ -224,12 +258,17 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *
#ifndef SILENT
bool getwstr(wchar *str,size_t n)
void getwstr(std::wstring &str)
{
// Print buffered prompt title function before waiting for input.
fflush(stderr);
*str=0;
QuitIfInputProhibited();
str.clear();
const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name.
#if defined(_WIN_ALL)
// fgetws does not work well with non-English text in Windows,
// so we do not use it.
@@ -237,33 +276,50 @@ bool getwstr(wchar *str,size_t n)
{
// fgets does not work well with pipes in Windows in our test.
// Let's use files.
Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
std::vector<char> StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t.
File SrcFile;
SrcFile.SetHandleType(FILE_HANDLESTD);
int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
SrcFile.SetLineInputMode(true);
int ReadSize=SrcFile.Read(&StrA[0],StrA.size()-1);
if (ReadSize<=0)
{
// Looks like stdin is a null device. We can enter to infinite loop
// calling Ask(), so let's better exit.
ErrHandler.Exit(RARX_USERBREAK);
// calling Ask() or set an empty password, so let's better exit.
ErrHandler.ReadError(L"stdin");
}
StrA[ReadSize]=0;
CharToWide(&StrA[0],str,n);
cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords.
// We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR,
// such as send passwords, we get OEM encoding by default, unless we
// use "chcp" in console. But we avoid OEM to ANSI conversion,
// because we also want to handle ANSI files redirection correctly,
// like "rar ... < ansifile.txt".
CharToWide(&StrA[0],str);
cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords.
}
else
{
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
DWORD SizeToRead=(DWORD)Buf.size()-1;
// ReadConsole fails in Windows 7 for requested input exceeding 30 KB.
// Not certain about Windows 8, so check for Windows 10 here.
if (WinNT()<=WNT_W10)
SizeToRead=Min(SizeToRead,0x4000);
DWORD ReadSize=0;
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
return false;
str[ReadSize]=0;
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0)
ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort.
Buf[ReadSize]=0;
str=Buf.data();
}
#else
if (fgetws(str,n,stdin)==NULL)
ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
if (fgetws(&Buf[0],Buf.size(),stdin)==NULL)
ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop.
str=Buf.data();
#endif
RemoveLF(str);
return true;
}
#endif
@@ -277,22 +333,22 @@ int Ask(const wchar *AskStr)
{
uiAlarm(UIALARM_QUESTION);
const int MaxItems=10;
const uint MaxItems=10;
wchar Item[MaxItems][40];
int ItemKeyPos[MaxItems],NumItems=0;
uint ItemKeyPos[MaxItems],NumItems=0;
for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_'))
{
wchar *CurItem=Item[NumItems];
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
wchar *EndItem=wcschr(CurItem,'_');
if (EndItem!=NULL)
if (EndItem!=nullptr)
*EndItem=0;
int KeyPos=0,CurKey;
uint KeyPos=0,CurKey;
while ((CurKey=CurItem[KeyPos])!=0)
{
bool Found=false;
for (int I=0;I<NumItems && !Found;I++)
for (uint I=0;I<NumItems && !Found;I++)
if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
Found=true;
if (!Found && CurKey!=' ')
@@ -303,19 +359,19 @@ int Ask(const wchar *AskStr)
NumItems++;
}
for (int I=0;I<NumItems;I++)
for (uint I=0;I<NumItems;I++)
{
eprintf(I==0 ? (NumItems>4 ? L"\n":L" "):L", ");
int KeyPos=ItemKeyPos[I];
for (int J=0;J<KeyPos;J++)
eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", ");
uint KeyPos=ItemKeyPos[I];
for (uint J=0;J<KeyPos;J++)
eprintf(L"%c",Item[I][J]);
eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
}
eprintf(L" ");
wchar Str[50];
getwstr(Str,ASIZE(Str));
std::wstring Str;
getwstr(Str);
wchar Ch=toupperw(Str[0]);
for (int I=0;I<NumItems;I++)
for (uint I=0;I<NumItems;I++)
if (Ch==Item[I][ItemKeyPos[I]])
return I+1;
return 0;
@@ -323,11 +379,11 @@ int Ask(const wchar *AskStr)
#endif
static bool IsCommentUnsafe(const wchar *Data,size_t Size)
static bool IsCommentUnsafe(const std::wstring &Data)
{
for (size_t I=0;I<Size;I++)
for (size_t I=0;I<Data.size();I++)
if (Data[I]==27 && Data[I+1]=='[')
for (size_t J=I+2;J<Size;J++)
for (size_t J=I+2;J<Data.size();J++)
{
// Return true for <ESC>[{key};"{string}"p used to redefine
// a keyboard key on some terminals.
@@ -340,18 +396,21 @@ static bool IsCommentUnsafe(const wchar *Data,size_t Size)
}
void OutComment(const wchar *Comment,size_t Size)
void OutComment(const std::wstring &Comment)
{
if (IsCommentUnsafe(Comment,Size))
if (IsCommentUnsafe(Comment))
return;
const size_t MaxOutSize=0x400;
for (size_t I=0;I<Size;I+=MaxOutSize)
for (size_t I=0;I<Comment.size();I+=MaxOutSize)
{
wchar Msg[MaxOutSize+1];
size_t CopySize=Min(MaxOutSize,Size-I);
wcsncpy(Msg,Comment+I,CopySize);
Msg[CopySize]=0;
mprintf(L"%s",Msg);
size_t CopySize=Min(MaxOutSize,Comment.size()-I);
mprintf(L"%s",Comment.substr(I,CopySize).c_str());
}
mprintf(L"\n");
}
bool IsConsoleOutputPresent()
{
return ConsoleOutputPresent;
}

View File

@@ -4,10 +4,12 @@
void InitConsole();
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream);
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset);
void OutComment(const wchar *Comment,size_t Size);
void ProhibitConsoleInput();
void OutComment(const std::wstring &Comment);
bool IsConsoleOutputPresent();
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password);
#endif
#ifdef SILENT
@@ -15,13 +17,13 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *
inline void eprintf(const wchar *fmt,...) {}
inline void Alarm() {}
inline int Ask(const wchar *AskStr) {return 0;}
inline bool getwstr(wchar *str,size_t n) {return false;}
inline void getwstr(std::wstring &str) {}
#else
void mprintf(const wchar *fmt,...);
void eprintf(const wchar *fmt,...);
void Alarm();
int Ask(const wchar *AskStr);
bool getwstr(wchar *str,size_t n);
void getwstr(std::wstring &str);
#endif
#endif

View File

@@ -14,7 +14,17 @@
#include "rar.hpp"
static uint crc_tables[8][256]; // Tables for Slicing-by-8.
#ifndef SFX_MODULE
// User suggested to avoid BSD license in SFX module, so they do not need
// to include the license to SFX archive.
#define USE_SLICING
#endif
static uint crc_tables[16][256]; // Tables for Slicing-by-16.
#ifdef USE_NEON_CRC32
static bool CRC_Neon;
#endif
// Build the classic CRC32 lookup table.
@@ -30,6 +40,19 @@ void InitCRC32(uint *CRCTab)
C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
CRCTab[I]=C;
}
#ifdef USE_NEON_CRC32
#ifdef _APPLE
// getauxval isn't available in OS X
uint Value=0;
size_t Size=sizeof(Value);
int RetCode=sysctlbyname("hw.optional.armv8_crc32",&Value,&Size,NULL,0);
CRC_Neon=RetCode==0 && Value!=0;
#else
CRC_Neon=(getauxval(AT_HWCAP) & HWCAP_CRC32)!=0;
#endif
#endif
}
@@ -37,15 +60,17 @@ static void InitTables()
{
InitCRC32(crc_tables[0]);
#ifdef USE_SLICING
for (uint I=0;I<256;I++) // Build additional lookup tables.
{
uint C=crc_tables[0][I];
for (uint J=1;J<8;J++)
for (uint J=1;J<16;J++)
{
C=crc_tables[0][(byte)C]^(C>>8);
crc_tables[J][I]=C;
}
}
#endif
}
@@ -55,28 +80,68 @@ uint CRC32(uint StartCRC,const void *Addr,size_t Size)
{
byte *Data=(byte *)Addr;
// Align Data to 8 for better performance.
for (;Size>0 && ((size_t)Data & 7);Size--,Data++)
#ifdef USE_NEON_CRC32
if (CRC_Neon)
{
for (;Size>=8;Size-=8,Data+=8)
#ifdef __clang__
StartCRC = __builtin_arm_crc32d(StartCRC, RawGet8(Data));
#else
StartCRC = __builtin_aarch64_crc32x(StartCRC, RawGet8(Data));
#endif
for (;Size>0;Size--,Data++) // Process left data.
#ifdef __clang__
StartCRC = __builtin_arm_crc32b(StartCRC, *Data);
#else
StartCRC = __builtin_aarch64_crc32b(StartCRC, *Data);
#endif
return StartCRC;
}
#endif
#ifdef USE_SLICING
// Align Data to 16 for better performance and to avoid ALLOW_MISALIGNED
// check below.
for (;Size>0 && ((size_t)Data & 15)!=0;Size--,Data++)
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
for (;Size>=8;Size-=8,Data+=8)
// 2023.12.06: We switched to slicing-by-16, which seems to be faster than
// slicing-by-8 on modern CPUs. Slicing-by-32 would require 32 KB for tables
// and could be limited by L1 cache size on some CPUs.
for (;Size>=16;Size-=16,Data+=16)
{
#ifdef BIG_ENDIAN
StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
StartCRC ^= RawGet4(Data);
uint D1 = RawGet4(Data+4);
uint D2 = RawGet4(Data+8);
uint D3 = RawGet4(Data+12);
#else
// We avoid RawGet4 here for performance reason, to access uint32
// directly even if ALLOW_MISALIGNED isn't defined. We can do it,
// because we aligned 'Data' above.
StartCRC ^= *(uint32 *) Data;
uint NextData = *(uint32 *) (Data+4);
uint D1 = *(uint32 *) (Data+4);
uint D2 = *(uint32 *) (Data+8);
uint D3 = *(uint32 *) (Data+12);
#endif
StartCRC = crc_tables[7][(byte) StartCRC ] ^
crc_tables[6][(byte)(StartCRC >> 8) ] ^
crc_tables[5][(byte)(StartCRC >> 16)] ^
crc_tables[4][(byte)(StartCRC >> 24)] ^
crc_tables[3][(byte) NextData ] ^
crc_tables[2][(byte)(NextData >> 8) ] ^
crc_tables[1][(byte)(NextData >> 16)] ^
crc_tables[0][(byte)(NextData >> 24)];
StartCRC = crc_tables[15][(byte) StartCRC ] ^
crc_tables[14][(byte)(StartCRC >> 8) ] ^
crc_tables[13][(byte)(StartCRC >> 16)] ^
crc_tables[12][(byte)(StartCRC >> 24)] ^
crc_tables[11][(byte) D1 ] ^
crc_tables[10][(byte)(D1 >> 8) ] ^
crc_tables[ 9][(byte)(D1 >> 16)] ^
crc_tables[ 8][(byte)(D1 >> 24)] ^
crc_tables[ 7][(byte) D2 ] ^
crc_tables[ 6][(byte)(D2 >> 8)] ^
crc_tables[ 5][(byte)(D2 >> 16)] ^
crc_tables[ 4][(byte)(D2 >> 24)] ^
crc_tables[ 3][(byte) D3 ] ^
crc_tables[ 2][(byte)(D3 >> 8)] ^
crc_tables[ 1][(byte)(D3 >> 16)] ^
crc_tables[ 0][(byte)(D3 >> 24)];
}
#endif
for (;Size>0;Size--,Data++) // Process left data.
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
@@ -100,3 +165,116 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
#endif
#if 0
static void TestCRC();
struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC;
void TestCRC()
{
// This function is invoked from global object and _SSE_Version is global
// and can be initialized after this function. So we explicitly initialize
// it here to enable SSE support in Blake2sp.
_SSE_Version=GetSSEVersion();
const uint FirstSize=300;
byte b[FirstSize];
if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84)
mprintf(L"\nCRC32 test1 OK");
else
mprintf(L"\nCRC32 test1 FAILED");
if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE)
mprintf(L"\nCRC32 test2 OK");
else
mprintf(L"\nCRC32 test2 FAILED");
for (uint I=0;I<14;I++) // Check for possible int sign extension.
b[I]=(byte)0x7f+I;
if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA)
mprintf(L"\nCRC32 test3 OK");
else
mprintf(L"\nCRC32 test3 FAILED");
for (uint I=0;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];
GetRnd(Buf,BufSize);
clock_t StartTime=clock();
r32=0xffffffff;
const uint64 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: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
StartTime=clock();
DataHash Hash;
Hash.Init(HASH_CRC32,MaxPoolThreads);
const uint64 BufCountMT=20000;
for (uint I=0;I<BufCountMT;I++)
Hash.Update(Buf,BufSize);
HashValue Result;
Hash.Result(&Result);
mprintf(L"\nCRC32 MT speed: %llu MB/s",BufCountMT*CLOCKS_PER_SEC/(clock()-StartTime));
StartTime=clock();
Hash.Init(HASH_BLAKE2,MaxPoolThreads);
for (uint I=0;I<BufCount;I++)
Hash.Update(Buf,BufSize);
Hash.Result(&Result);
mprintf(L"\nBlake2sp speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(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: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
}
#endif

View File

@@ -11,21 +11,12 @@
CryptData::CryptData()
{
Method=CRYPT_NONE;
memset(KDF3Cache,0,sizeof(KDF3Cache));
memset(KDF5Cache,0,sizeof(KDF5Cache));
KDF3CachePos=0;
KDF5CachePos=0;
memset(CRCTab,0,sizeof(CRCTab));
}
CryptData::~CryptData()
{
cleandata(KDF3Cache,sizeof(KDF3Cache));
cleandata(KDF5Cache,sizeof(KDF5Cache));
}
void CryptData::DecryptBlock(byte *Buf,size_t Size)
@@ -56,16 +47,28 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
if (!Password->IsSet() || Method==CRYPT_NONE)
if (Method==CRYPT_NONE || !Password->IsSet())
return false;
CryptData::Method=Method;
wchar PwdW[MAXPASSWORD];
Password->Get(PwdW,ASIZE(PwdW));
// Display this warning only when encrypting. Users complained that
// it is distracting when decrypting. It still can be useful when encrypting,
// so users do not waste time to excessively long passwords.
if (Encrypt && wcslen(PwdW)>=MAXPASSWORD_RAR)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD_RAR-1);
PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
char PwdA[MAXPASSWORD];
WideToChar(PwdW,PwdA,ASIZE(PwdA));
PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
bool Success=true;
switch(Method)
{
#ifndef SFX_MODULE
@@ -83,12 +86,12 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
SetKey30(Encrypt,Password,PwdW,Salt);
break;
case CRYPT_RAR50:
SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
break;
}
cleandata(PwdA,sizeof(PwdA));
cleandata(PwdW,sizeof(PwdW));
return true;
return Success;
}
@@ -117,7 +120,7 @@ void GetRnd(byte *RndBuf,size_t BufSize)
HCRYPTPROV hProvider = 0;
if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) != FALSE;
CryptReleaseContext(hProvider, 0);
}
#elif defined(_UNIX)

View File

@@ -3,7 +3,8 @@
enum CRYPT_METHOD {
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50,
CRYPT_UNKNOWN
};
#define SIZE_SALT50 16
@@ -30,6 +31,18 @@ class CryptData
uint Lg2Count; // Log2 of PBKDF2 repetition count.
byte PswCheckValue[SHA256_DIGEST_SIZE];
byte HashKeyValue[SHA256_DIGEST_SIZE];
KDF5CacheItem() {Clean();}
~KDF5CacheItem() {Clean();}
void Clean()
{
cleandata(Salt,sizeof(Salt));
cleandata(Key,sizeof(Key));
cleandata(&Lg2Count,sizeof(Lg2Count));
cleandata(PswCheckValue,sizeof(PswCheckValue));
cleandata(HashKeyValue,sizeof(HashKeyValue));
}
};
struct KDF3CacheItem
@@ -39,6 +52,17 @@ class CryptData
byte Key[16];
byte Init[16];
bool SaltPresent;
KDF3CacheItem() {Clean();}
~KDF3CacheItem() {Clean();}
void Clean()
{
cleandata(Salt,sizeof(Salt));
cleandata(Key,sizeof(Key));
cleandata(Init,sizeof(Init));
cleandata(&SaltPresent,sizeof(SaltPresent));
}
};
@@ -56,7 +80,7 @@ class CryptData
void DecryptBlock20(byte *Buf);
void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
bool SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
KDF3CacheItem KDF3Cache[4];
uint KDF3CachePos;
@@ -77,17 +101,63 @@ class CryptData
ushort Key15[4];
public:
CryptData();
~CryptData();
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,
byte *HashKey,byte *PswCheck);
void SetAV15Encryption();
void SetCmt13Encryption();
void EncryptBlock(byte *Buf,size_t Size);
void DecryptBlock(byte *Buf,size_t Size);
static void SetSalt(byte *Salt,size_t SaltSize);
};
class CheckPassword
{
public:
enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
virtual CONFIDENCE GetConfidence()=0;
virtual bool Check(SecPassword *Password)=0;
};
class RarCheckPassword:public CheckPassword
{
private:
CryptData *Crypt;
uint Lg2Count;
byte Salt[SIZE_SALT50];
byte InitV[SIZE_INITV];
byte PswCheck[SIZE_PSWCHECK];
public:
RarCheckPassword()
{
Crypt=NULL;
}
~RarCheckPassword()
{
delete Crypt;
}
void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
{
if (Crypt==NULL)
Crypt=new CryptData;
memcpy(this->Salt,Salt,sizeof(this->Salt));
memcpy(this->InitV,InitV,sizeof(this->InitV));
this->Lg2Count=Lg2Count;
memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
}
bool IsSet() {return Crypt!=NULL;}
// RAR5 provides the higly reliable 64 bit password verification value.
CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
bool Check(SecPassword *Password)
{
byte PswCheck[SIZE_PSWCHECK];
Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
}
};
void GetRnd(byte *RndBuf,size_t BufSize);
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,

View File

@@ -1,5 +1,3 @@
extern uint CRCTab[256];
void CryptData::SetKey13(const char *Password)
{
Key13[0]=Key13[1]=Key13[2]=0;
@@ -25,22 +23,11 @@ void CryptData::SetKey15(const char *Password)
{
byte P=Password[I];
Key15[2]^=P^CRCTab[P];
Key15[3]+=P+(CRCTab[P]>>16);
Key15[3]+=ushort(P+(CRCTab[P]>>16));
}
}
void CryptData::SetAV15Encryption()
{
InitCRC32(CRCTab);
Method=CRYPT_RAR15;
Key15[0]=0x4765;
Key15[1]=0x9021;
Key15[2]=0x7382;
Key15[3]=0x5215;
}
void CryptData::SetCmt13Encryption()
{
Method=CRYPT_RAR13;
@@ -68,7 +55,7 @@ void CryptData::Crypt15(byte *Data,size_t Count)
{
Key15[0]+=0x1234;
Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16;
Key15[2]-=ushort(CRCTab[(Key15[0] & 0x1fe)>>1]>>16);
Key15[0]^=Key15[2];
Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
Key15[3]=rotrs(Key15[3]&0xffff,1,16);

View File

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

View File

@@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
sha256_context ICtx;
if (ICtxOpt!=NULL && *SetIOpt)
ICtx=*ICtxOpt; // Use already calculated first block context.
ICtx=*ICtxOpt; // Use already calculated the first block context.
else
{
// This calculation is the same for all iterations with same password.
@@ -90,10 +90,10 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
byte SaltData[MaxSalt+4];
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
SaltData[SaltLength + 1] = 0;
SaltData[SaltLength + 2] = 0;
SaltData[SaltLength + 3] = 1;
SaltData[SaltLength + 0] = 0; // Block index appened to salt.
SaltData[SaltLength + 1] = 0; //
SaltData[SaltLength + 2] = 0; // Since we do not request the key width
SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
// First iteration: HMAC of password, salt and block index (1).
byte U1[SHA256_DIGEST_SIZE];
@@ -128,19 +128,19 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
}
void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
byte *PswCheck)
{
if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
return;
return false;
byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
bool Found=false;
for (uint I=0;I<ASIZE(KDF5Cache);I++)
{
KDF5CacheItem *Item=KDF5Cache+I;
if (Item->Lg2Count==Lg2Cnt && Item->Pwd==*Password &&
if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{
memcpy(Key,Item->Key,sizeof(Key));
@@ -186,6 +186,7 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
rin.Init(Encrypt, Key, 256, InitV);
cleandata(Key,sizeof(Key));
return true;
}
@@ -200,6 +201,7 @@ void ConvertHashToMAC(HashValue *Value,byte *Key)
Value->CRC32=0;
for (uint I=0;I<ASIZE(Digest);I++)
Value->CRC32^=Digest[I] << ((I & 3) * 8);
Value->CRC32&=0xffffffff; // In case the variable size is larger than 32-bit.
}
if (Value->Type==HASH_BLAKE2)
{

View File

@@ -16,8 +16,7 @@ struct DataSet
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
{
RAROpenArchiveDataEx rx;
memset(&rx,0,sizeof(rx));
RAROpenArchiveDataEx rx{};
rx.ArcName=r->ArcName;
rx.OpenMode=r->OpenMode;
rx.CmtBuf=r->CmtBuf;
@@ -32,7 +31,7 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
DataSet *Data=NULL;
DataSet *Data=nullptr;
try
{
ErrHandler.Clean();
@@ -44,22 +43,21 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
Data->Cmd.FileArgs.AddString(L"*");
Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
char AnsiArcName[NM];
*AnsiArcName=0;
if (r->ArcName!=NULL)
std::string AnsiArcName;
if (r->ArcName!=nullptr)
{
strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
AnsiArcName=r->ArcName;
#ifdef _WIN_ALL
if (!AreFileApisANSI())
{
OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
AnsiArcName[ASIZE(AnsiArcName)-1]=0;
}
OemToExt(r->ArcName,AnsiArcName);
#endif
}
wchar ArcName[NM];
GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
std::wstring ArcName;
if (r->ArcNameW!=nullptr && *r->ArcNameW!=0)
ArcName=r->ArcNameW;
else
CharToWide(AnsiArcName,ArcName);
Data->Cmd.AddArcName(ArcName);
Data->Cmd.Overwrite=OVERWRITE_ALL;
@@ -75,7 +73,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
r->OpenResult=ERAR_EOPEN;
delete Data;
return NULL;
return nullptr;
}
if (!Data->Arc.IsArchive(true))
{
@@ -90,7 +88,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
r->OpenResult=ERAR_BAD_ARCHIVE;
}
delete Data;
return NULL;
return nullptr;
}
r->Flags=0;
@@ -113,35 +111,47 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
if (Data->Arc.FirstVolume)
r->Flags|=ROADF_FIRSTVOLUME;
Array<wchar> CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
std::wstring CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW))
{
if (r->CmtBufW!=NULL)
if (r->CmtBufW!=nullptr)
{
CmtDataW.Push(0);
size_t Size=wcslen(&CmtDataW[0])+1;
// CmtDataW.push_back(0);
size_t Size=wcslen(CmtDataW.data())+1;
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW));
memcpy(r->CmtBufW,CmtDataW.data(),(r->CmtSize-1)*sizeof(*r->CmtBufW));
r->CmtBufW[r->CmtSize-1]=0;
}
else
if (r->CmtBuf!=NULL)
{
Array<char> CmtData(CmtDataW.Size()*4+1);
memset(&CmtData[0],0,CmtData.Size());
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
size_t Size=strlen(&CmtData[0])+1;
std::vector<char> CmtData(CmtDataW.size()*4+1);
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.size()-1);
size_t Size=strlen(CmtData.data())+1;
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
memcpy(r->CmtBuf,CmtData.data(),r->CmtSize-1);
r->CmtBuf[r->CmtSize-1]=0;
}
}
else
r->CmtState=r->CmtSize=0;
#ifdef PROPAGATE_MOTW
if (r->MarkOfTheWeb!=nullptr)
{
Data->Cmd.MotwAllFields=r->MarkOfTheWeb[0]=='1';
const wchar *Sep=wcschr(r->MarkOfTheWeb,'=');
if (r->MarkOfTheWeb[0]=='-')
Data->Cmd.MotwList.Reset();
else
Data->Cmd.GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,Data->Cmd.MotwList);
}
#endif
Data->Extract.ExtractArchiveInit(Data->Arc);
return (HANDLE)Data;
}
@@ -183,8 +193,7 @@ int PASCAL RARCloseArchive(HANDLE hArcData)
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
{
struct RARHeaderDataEx X;
memset(&X,0,sizeof(X));
struct RARHeaderDataEx X{};
int Code=RARReadHeaderEx(hArcData,&X);
@@ -242,14 +251,18 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
else
return Code;
}
wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW));
wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW));
WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
if (D->ArcNameEx!=nullptr)
wcsncpyz(D->ArcNameEx,Data->Arc.FileName.c_str(),D->ArcNameExSize);
wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW));
wcsncpyz(D->FileNameW,hd->FileName.c_str(),ASIZE(D->FileNameW));
WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
#ifdef _WIN_ALL
CharToOemA(D->FileName,D->FileName);
#endif
if (D->FileNameEx!=nullptr)
wcsncpyz(D->FileNameEx,hd->FileName.c_str(),D->FileNameExSize);
D->Flags=0;
if (hd->SplitBefore)
@@ -311,14 +324,14 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
// this RedirNameSize check sometimes later.
if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
D->RedirNameSize>0 && D->RedirNameSize<100000)
wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize);
wcsncpyz(D->RedirName,hd->RedirName.c_str(),D->RedirNameSize);
D->DirTarget=hd->DirTarget;
}
catch (RAR_EXIT ErrCode)
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
return ERAR_SUCCESS;
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
}
@@ -346,50 +359,52 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
{
Data->Cmd.DllOpMode=Operation;
*Data->Cmd.ExtrPath=0;
*Data->Cmd.DllDestName=0;
Data->Cmd.ExtrPath.clear();
Data->Cmd.DllDestName.clear();
if (DestPath!=NULL)
{
char ExtrPathA[NM];
strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2);
std::string ExtrPathA=DestPath;
#ifdef _WIN_ALL
// We must not apply OemToCharBuffA directly to DestPath,
// because we do not know DestPath length and OemToCharBuffA
// does not stop at 0.
OemToCharA(ExtrPathA,ExtrPathA);
OemToExt(ExtrPathA,ExtrPathA);
#endif
CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
CharToWide(ExtrPathA,Data->Cmd.ExtrPath);
AddEndSlash(Data->Cmd.ExtrPath);
}
if (DestName!=NULL)
{
char DestNameA[NM];
strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2);
std::string DestNameA=DestName;
#ifdef _WIN_ALL
// We must not apply OemToCharBuffA directly to DestName,
// because we do not know DestName length and OemToCharBuffA
// does not stop at 0.
OemToCharA(DestNameA,DestNameA);
OemToExt(DestNameA,DestNameA);
#endif
CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
CharToWide(DestNameA,Data->Cmd.DllDestName);
}
if (DestPathW!=NULL)
{
wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
Data->Cmd.ExtrPath=DestPathW;
AddEndSlash(Data->Cmd.ExtrPath);
}
if (DestNameW!=NULL)
wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
Data->Cmd.DllDestName=DestNameW;
wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command));
Data->Cmd.Command=Operation==RAR_EXTRACT ? L"X":L"T";
Data->Cmd.Test=Operation!=RAR_EXTRACT;
bool Repeat=false;
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
// Now we process extra file information if any.
// It is important to do it in the same ProcessFile(), because caller
// app can rely on this behavior, for example, to overwrite
// the extracted Mark of the Web with propagated from archive
// immediately after ProcessFile() call.
//
// Archive can be closed if we process volumes, next volume is missing
// and current one is already removed or deleted. So we need to check
@@ -413,7 +428,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
return Data->Cmd.DllError;
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
}
@@ -456,7 +471,7 @@ void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
#ifndef RAR_NOCRYPT
DataSet *Data=(DataSet *)hArcData;
wchar PasswordW[MAXPASSWORD];
GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
CharToWide(Password,PasswordW,ASIZE(PasswordW));
Data->Cmd.Password.Set(PasswordW);
cleandata(PasswordW,sizeof(PasswordW));
#endif
@@ -474,6 +489,7 @@ static int RarErrorToDll(RAR_EXIT ErrCode)
switch(ErrCode)
{
case RARX_FATAL:
case RARX_READ:
return ERAR_EREAD;
case RARX_CRC:
return ERAR_BAD_DATA;
@@ -489,6 +505,8 @@ static int RarErrorToDll(RAR_EXIT ErrCode)
return ERAR_BAD_PASSWORD;
case RARX_SUCCESS:
return ERAR_SUCCESS; // 0.
case RARX_BADARC:
return ERAR_BAD_ARCHIVE;
default:
return ERAR_UNKNOWN;
}

View File

@@ -19,6 +19,7 @@
#define ERAR_MISSING_PASSWORD 22
#define ERAR_EREFERENCE 23
#define ERAR_BAD_PASSWORD 24
#define ERAR_LARGE_DICT 25
#define RAR_OM_LIST 0
#define RAR_OM_EXTRACT 1
@@ -31,7 +32,7 @@
#define RAR_VOL_ASK 0
#define RAR_VOL_NOTIFY 1
#define RAR_DLL_VERSION 8
#define RAR_DLL_VERSION 9
#define RAR_HASH_NONE 0
#define RAR_HASH_CRC32 1
@@ -108,7 +109,11 @@ struct RARHeaderDataEx
unsigned int CtimeHigh;
unsigned int AtimeLow;
unsigned int AtimeHigh;
unsigned int Reserved[988];
wchar_t *ArcNameEx;
unsigned int ArcNameExSize;
wchar_t *FileNameEx;
unsigned int FileNameExSize;
unsigned int Reserved[982];
};
@@ -152,12 +157,13 @@ struct RAROpenArchiveDataEx
LPARAM UserData;
unsigned int OpFlags;
wchar_t *CmtBufW;
unsigned int Reserved[25];
wchar_t *MarkOfTheWeb;
unsigned int Reserved[23];
};
enum UNRARCALLBACK_MESSAGES {
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
UCM_NEEDPASSWORDW
UCM_NEEDPASSWORDW,UCM_LARGEDICT
};
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);

View File

@@ -2,8 +2,8 @@
#include <commctrl.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5, 91, 100, 3470
PRODUCTVERSION 5, 91, 100, 3470
FILEVERSION 7, 20, 100, 1861
PRODUCTVERSION 7, 20, 100, 1861
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
@@ -14,9 +14,9 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
VALUE "FileVersion", "5.91.0\0"
VALUE "ProductVersion", "5.91.0\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0"
VALUE "FileVersion", "7.20.0\0"
VALUE "ProductVersion", "7.20.0\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2026\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
}

View File

@@ -11,17 +11,16 @@ EncodeFileName::EncodeFileName()
void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,
wchar *NameW,size_t MaxDecSize)
void EncodeFileName::Decode(const char *Name,size_t NameSize,
const byte *EncName,size_t EncSize,
std::wstring &NameW)
{
size_t EncPos=0,DecPos=0;
byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
while (EncPos<EncSize && DecPos<MaxDecSize)
while (EncPos<EncSize)
{
if (FlagBits==0)
{
if (EncPos>=EncSize)
break;
Flags=EncName[EncPos++];
FlagBits=8;
}
@@ -30,16 +29,20 @@ void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncS
case 0:
if (EncPos>=EncSize)
break;
// We need DecPos also for ASCII "Name", so resize() instead of push_back().
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos++];
break;
case 1:
if (EncPos>=EncSize)
break;
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
break;
case 2:
if (EncPos+1>=EncSize)
break;
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
EncPos+=2;
break;
@@ -53,17 +56,22 @@ void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncS
if (EncPos>=EncSize)
break;
byte Correction=EncName[EncPos++];
for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
for (Length=(Length&0x7f)+2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
}
}
else
for (Length+=2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
for (Length+=2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
NameW[DecPos]=Name[DecPos];
}
}
break;
}
Flags<<=2;
FlagBits-=2;
}
NameW[DecPos<MaxDecSize ? DecPos:MaxDecSize-1]=0;
}

View File

@@ -4,17 +4,16 @@
class EncodeFileName
{
private:
void AddFlags(int Value);
void AddFlags(byte Value,std::vector<byte> &EncName);
byte *EncName;
byte Flags;
uint FlagBits;
size_t FlagsPos;
size_t DestSize;
public:
EncodeFileName();
size_t Encode(char *Name,wchar *NameW,byte *EncName);
void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize);
void Encode(const std::string &Name,const std::wstring &NameW,std::vector<byte> &EncName);
void Decode(const char *Name,size_t NameSize,const byte *EncName,size_t EncSize,std::wstring &NameW);
};
#endif

View File

@@ -15,6 +15,7 @@ void ErrorHandler::Clean()
UserBreak=false;
MainExit=false;
DisableShutdown=false;
ReadErrIgnoreAll=false;
}
@@ -25,7 +26,7 @@ void ErrorHandler::MemoryError()
}
void ErrorHandler::OpenError(const wchar *FileName)
void ErrorHandler::OpenError(const std::wstring &FileName)
{
#ifndef SILENT
OpenErrorMsg(FileName);
@@ -34,7 +35,7 @@ void ErrorHandler::OpenError(const wchar *FileName)
}
void ErrorHandler::CloseError(const wchar *FileName)
void ErrorHandler::CloseError(const std::wstring &FileName)
{
if (!UserBreak)
{
@@ -50,34 +51,44 @@ void ErrorHandler::CloseError(const wchar *FileName)
}
void ErrorHandler::ReadError(const wchar *FileName)
void ErrorHandler::ReadError(const std::wstring &FileName)
{
#ifndef SILENT
ReadErrorMsg(FileName);
#endif
#if !defined(SILENT) || defined(RARDLL)
Exit(RARX_FATAL);
Exit(RARX_READ);
#endif
}
bool ErrorHandler::AskRepeatRead(const wchar *FileName)
void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit)
{
SetErrorCode(RARX_READ);
#if !defined(SILENT) && !defined(SFX_MODULE)
if (!Silent)
{
uiMsg(UIERROR_FILEREAD,L"",FileName);
SysErrMsg();
bool Repeat=uiAskRepeatRead(FileName);
if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
DisableShutdown=true;
return Repeat;
if (ReadErrIgnoreAll)
Ignore=true;
else
{
bool All=false;
uiAskRepeatRead(FileName,Ignore,All,Retry,Quit);
if (All)
ReadErrIgnoreAll=Ignore=true;
if (Quit) // Disable shutdown if user select Quit in read error prompt.
DisableShutdown=true;
}
return;
}
#endif
return false;
Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice.
}
void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &FileName)
{
#ifndef SILENT
WriteErrorMsg(ArcName,FileName);
@@ -89,7 +100,7 @@ void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName)
#ifdef _WIN_ALL
void ErrorHandler::WriteErrorFAT(const wchar *FileName)
void ErrorHandler::WriteErrorFAT(const std::wstring &FileName)
{
SysErrMsg();
uiMsg(UIERROR_NTFSREQUIRED,FileName);
@@ -100,7 +111,7 @@ void ErrorHandler::WriteErrorFAT(const wchar *FileName)
#endif
bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull)
bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull)
{
#ifndef SILENT
if (!Silent)
@@ -118,7 +129,7 @@ bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull)
}
void ErrorHandler::SeekError(const wchar *FileName)
void ErrorHandler::SeekError(const std::wstring &FileName)
{
if (!UserBreak)
{
@@ -133,13 +144,16 @@ void ErrorHandler::SeekError(const wchar *FileName)
void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
{
#ifndef RARDLL
va_list arglist;
va_start(arglist,fmt);
wchar Msg[1024];
vswprintf(Msg,ASIZE(Msg),fmt,arglist);
std::wstring Msg=vwstrprintf(fmt,arglist);
uiMsg(UIERROR_GENERALERRMSG,Msg);
SysErrMsg();
va_end(arglist);
#endif
}
@@ -150,28 +164,31 @@ void ErrorHandler::MemoryErrorMsg()
}
void ErrorHandler::OpenErrorMsg(const wchar *FileName)
void ErrorHandler::OpenErrorMsg(const std::wstring &FileName)
{
OpenErrorMsg(NULL,FileName);
OpenErrorMsg(L"",FileName);
}
void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
Wait(); // Keep GUI responsive if many files cannot be opened when archiving.
uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_OPEN);
// Keep GUI responsive if many files cannot be opened when archiving.
// Call after SysErrMsg to avoid modifying the error code and SysErrMsg text.
Wait();
}
void ErrorHandler::CreateErrorMsg(const wchar *FileName)
void ErrorHandler::CreateErrorMsg(const std::wstring &FileName)
{
CreateErrorMsg(NULL,FileName);
CreateErrorMsg(L"",FileName);
}
void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILECREATE,ArcName,FileName);
SysErrMsg();
@@ -179,21 +196,21 @@ void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName)
}
void ErrorHandler::ReadErrorMsg(const wchar *FileName)
void ErrorHandler::ReadErrorMsg(const std::wstring &FileName)
{
ReadErrorMsg(NULL,FileName);
ReadErrorMsg(L"",FileName);
}
void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILEREAD,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_FATAL);
SetErrorCode(RARX_READ);
}
void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
SysErrMsg();
@@ -201,21 +218,21 @@ void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
}
void ErrorHandler::ArcBrokenMsg(const wchar *ArcName)
void ErrorHandler::ArcBrokenMsg(const std::wstring &ArcName)
{
uiMsg(UIERROR_ARCBROKEN,ArcName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName)
void ErrorHandler::UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
ErrHandler.SetErrorCode(RARX_FATAL);
@@ -239,7 +256,8 @@ void ErrorHandler::SetErrorCode(RAR_EXIT Code)
ExitCode=Code;
break;
case RARX_CRC:
if (ExitCode!=RARX_BADPWD)
// 2025.10.25: RARX_OPEN is set if next volume is missing.
if (ExitCode!=RARX_BADPWD && ExitCode!=RARX_OPEN)
ExitCode=Code;
break;
case RARX_FATAL:
@@ -318,33 +336,44 @@ void ErrorHandler::Throw(RAR_EXIT Code)
if (Code==RARX_USERBREAK && !EnableBreak)
return;
#if !defined(SILENT)
// Do not write "aborted" when just displaying online help.
if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
mprintf(L"\n%s\n",St(MProgAborted));
if (Code!=RARX_SUCCESS)
if (Code==RARX_USERERROR) // Do not write "aborted" when just displaying the online help.
mprintf(L"\n"); // For consistency with other errors, which print the final "\n".
else
mprintf(L"\n%s\n",St(MProgAborted));
#endif
SetErrorCode(Code);
throw Code;
}
bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size)
bool ErrorHandler::GetSysErrMsg(std::wstring &Msg)
{
#ifndef SILENT
#ifdef _WIN_ALL
int ErrType=GetLastError();
if (ErrType!=0)
return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
Msg,(DWORD)Size,NULL)!=0;
{
wchar *Buf=nullptr;
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)&Buf,0,NULL)!=0)
{
Msg=Buf;
LocalFree(Buf);
return true;
}
}
#endif
#if defined(_UNIX) || defined(_EMX)
#ifdef _UNIX
if (errno!=0)
{
char *err=strerror(errno);
if (err!=NULL)
{
CharToWide(err,Msg,Size);
CharToWide(err,Msg);
return true;
}
}
@@ -356,32 +385,27 @@ bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size)
void ErrorHandler::SysErrMsg()
{
#if !defined(SFX_MODULE) && !defined(SILENT)
wchar Msg[1024];
if (!GetSysErrMsg(Msg,ASIZE(Msg)))
#ifndef SILENT
std::wstring Msg;
if (!GetSysErrMsg(Msg))
return;
#ifdef _WIN_ALL
wchar *CurMsg=Msg;
while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines.
// Print string with \r\n as several strings to multiple lines.
size_t Pos=0;
while (Pos!=std::wstring::npos)
{
while (*CurMsg=='\r' || *CurMsg=='\n')
CurMsg++;
if (*CurMsg==0)
while (Msg[Pos]=='\r' || Msg[Pos]=='\n')
Pos++;
if (Pos==Msg.size())
break;
wchar *EndMsg=wcschr(CurMsg,'\r');
if (EndMsg==NULL)
EndMsg=wcschr(CurMsg,'\n');
if (EndMsg!=NULL)
{
*EndMsg=0;
EndMsg++;
}
size_t EndPos=Msg.find_first_of(L"\r\n",Pos);
std::wstring CurMsg=Msg.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos);
uiMsg(UIERROR_SYSERRMSG,CurMsg);
CurMsg=EndMsg;
Pos=EndPos;
}
#endif
#if defined(_UNIX) || defined(_EMX)
#ifdef _UNIX
uiMsg(UIERROR_SYSERRMSG,Msg);
#endif

View File

@@ -15,9 +15,12 @@ enum RAR_EXIT // RAR exit code.
RARX_CREATE = 9,
RARX_NOFILES = 10,
RARX_BADPWD = 11,
RARX_READ = 12,
RARX_BADARC = 13,
RARX_USERBREAK = 255
};
class ErrorHandler
{
private:
@@ -26,30 +29,31 @@ class ErrorHandler
bool EnableBreak;
bool Silent;
bool DisableShutdown; // Shutdown is not suitable after last error.
bool ReadErrIgnoreAll;
public:
ErrorHandler();
void Clean();
void MemoryError();
void OpenError(const wchar *FileName);
void CloseError(const wchar *FileName);
void ReadError(const wchar *FileName);
bool AskRepeatRead(const wchar *FileName);
void WriteError(const wchar *ArcName,const wchar *FileName);
void WriteErrorFAT(const wchar *FileName);
bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
void SeekError(const wchar *FileName);
void OpenError(const std::wstring &FileName);
void CloseError(const std::wstring &FileName);
void ReadError(const std::wstring &FileName);
void AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit);
void WriteError(const std::wstring &ArcName,const std::wstring &FileName);
void WriteErrorFAT(const std::wstring &FileName);
bool AskRepeatWrite(const std::wstring &FileName,bool DiskFull);
void SeekError(const std::wstring &FileName);
void GeneralErrMsg(const wchar *fmt,...);
void MemoryErrorMsg();
void OpenErrorMsg(const wchar *FileName);
void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
void CreateErrorMsg(const wchar *FileName);
void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
void ReadErrorMsg(const wchar *FileName);
void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
void ArcBrokenMsg(const wchar *ArcName);
void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
void OpenErrorMsg(const std::wstring &FileName);
void OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void CreateErrorMsg(const std::wstring &FileName);
void CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void ReadErrorMsg(const std::wstring &FileName);
void ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void ArcBrokenMsg(const std::wstring &ArcName);
void ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName);
void UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName);
void Exit(RAR_EXIT ExitCode);
void SetErrorCode(RAR_EXIT Code);
RAR_EXIT GetErrorCode() {return ExitCode;}
@@ -57,7 +61,7 @@ class ErrorHandler
void SetSignalHandlers(bool Enable);
void Throw(RAR_EXIT Code);
void SetSilent(bool Mode) {Silent=Mode;}
bool GetSysErrMsg(wchar *Msg,size_t Size);
bool GetSysErrMsg(std::wstring &Msg);
void SysErrMsg();
int GetSystemErrorCode();
void SetSystemErrorCode(int Code);

View File

@@ -19,19 +19,13 @@
// RAR2 service header extra records.
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
#ifdef _UNIX
case UO_HEAD:
if (Cmd->ProcessOwners)
ExtractUnixOwner20(Arc,Name);
break;
#endif
#ifdef _WIN_ALL
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
@@ -39,19 +33,19 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
case STREAM_HEAD:
ExtractStreams20(Arc,Name);
break;
#endif
}
#endif
}
#endif
// RAR3 and RAR5 service header extra records.
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
ExtractUnixOwner30(Arc,Name);
ExtractUnixOwner30(Arc,Name.c_str());
#endif
#ifdef _WIN_ALL
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
@@ -63,7 +57,7 @@ void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
// Extra data stored directly in file header.
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
@@ -74,36 +68,34 @@ void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
// Calculate a number of path components except \. and \..
static int CalcAllowedDepth(const wchar *Name)
// Calculate the number of path components except \. and \..
static int CalcAllowedDepth(const std::wstring &Name)
{
int AllowedDepth=0;
while (*Name!=0)
{
if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
for (size_t I=0;I<Name.size();I++)
if (IsPathDiv(Name[I]))
{
bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
bool Dot=Name[I+1]=='.' && (IsPathDiv(Name[I+2]) || Name[I+2]==0);
bool Dot2=Name[I+1]=='.' && Name[I+2]=='.' && (IsPathDiv(Name[I+3]) || Name[I+3]==0);
if (!Dot && !Dot2)
AllowedDepth++;
else
if (Dot2)
AllowedDepth--;
}
Name++;
}
return AllowedDepth;
return AllowedDepth < 0 ? 0 : AllowedDepth;
}
// Check if all existing path components are directories and not links.
static bool LinkInPath(const wchar *Name)
static bool LinkInPath(std::wstring Path)
{
wchar Path[NM];
if (wcslen(Name)>=ASIZE(Path))
return true; // It should not be that long, skip.
wcsncpyz(Path,Name,ASIZE(Path));
for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
if (IsPathDiv(*s))
if (Path.empty()) // So we can safely use Path.size()-1 below.
return false;
for (size_t I=Path.size()-1;I>0;I--)
if (IsPathDiv(Path[I]))
{
*s=0;
Path.erase(I);
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
return true;
@@ -112,7 +104,7 @@ static bool LinkInPath(const wchar *Name)
}
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
// Do not check PrepSrcName here, it can be root based if destination path
@@ -122,19 +114,22 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
// Number of ".." in link target.
int UpLevels=0;
for (int Pos=0;*TargetName!=0;Pos++)
for (uint Pos=0;Pos<TargetName.size();Pos++)
{
bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
(IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
(Pos==0 || IsPathDiv(*(TargetName-1)));
bool Dot2=TargetName[Pos]=='.' && TargetName[Pos+1]=='.' &&
(IsPathDiv(TargetName[Pos+2]) || TargetName[Pos+2]==0) &&
(Pos==0 || IsPathDiv(TargetName[Pos-1]));
if (Dot2)
UpLevels++;
TargetName++;
}
// If link target includes "..", it must not have another links
// in the path, because they can bypass our safety check. For example,
// If link target includes "..", it must not have another links in its
// source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
// or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
// or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
// file "dir/lnk1/lnk2/poc.txt" last.
// Do not confuse with link chains in target, this is in link source path.
// It is important for Windows too, though this check can be omitted
// if LinksToDirs is invoked in Windows as well.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;
@@ -147,12 +142,12 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
// Remove the destination path from prepared name if any. We should not
// count the destination path depth, because the link target must point
// inside of this path, not outside of it.
size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
size_t ExtrPathLength=Cmd->ExtrPath.size();
if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0)
{
PrepSrcName+=ExtrPathLength;
while (IsPathDiv(*PrepSrcName))
PrepSrcName++;
while (IsPathDiv(PrepSrcName[ExtrPathLength]))
ExtrPathLength++;
PrepSrcName.erase(0,ExtrPathLength);
}
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
@@ -160,19 +155,30 @@ 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 std::wstring &LinkName,bool &UpLink)
{
// Returning true in Uplink indicates that link target might include ".."
// and enables additional checks. It is ok to falsely return true here,
// as it implies only the minor performance penalty. But we shall always
// return true for links with ".." in target for security reason.
UpLink=true; // Assume the target might include potentially unsafe "..".
#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
UpLink=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos;
#endif
#if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15)
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName.c_str(),UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
#elif defined _WIN_ALL
return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead);
#elif defined(_WIN_ALL)
// RAR 5.0 archives store link information in file header, so there is
// no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50)
return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
return CreateReparsePoint(Cmd,LinkName.c_str(),&Arc.FileHead);
#endif
return false;
}

View File

@@ -1,23 +1,19 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const wchar *FileName);
void SetUnixOwner(Archive &Arc,const std::wstring &FileName);
#endif
bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting);
void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
std::wstring GetStreamNameNTFS(Archive &Arc);
#ifdef _WIN_ALL
bool SetPrivilege(LPCTSTR PrivName);
#endif
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name);
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -6,22 +6,46 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
struct ExtractRef
{
std::wstring RefName;
std::wstring TmpName;
uint64 RefCount;
};
std::vector<ExtractRef> RefList;
struct AnalyzeData
{
std::wstring StartName;
uint64 StartPos;
std::wstring EndName;
uint64 EndPos;
} Analyze;
bool ArcAnalyzed;
void FreeAnalyzeData();
EXTRACT_ARC_CODE ExtractArchive();
bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
bool ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize);
void ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName);
#ifdef RARDLL
bool ExtrDllGetPassword();
#else
bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
#endif
void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
bool ExtrCreateFile(Archive &Arc,File &CurFile);
bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName);
bool ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly);
bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName);
#ifndef SFX_MODULE
void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering);
void GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName);
#endif
bool CheckWinLimit(Archive &Arc,std::wstring &ArcFileName);
RarTime StartTime; // time when extraction started
RarTime StartTime; // Time when extraction started.
CommandData *Cmd;
@@ -34,6 +58,7 @@ class CmdExtract
bool FirstFile;
bool AllMatchesExact;
bool ReconstructDone;
bool UseExactVolName;
// If any non-zero solid file was successfully unpacked before current.
// If true and if current encrypted file is broken, obviously
@@ -41,12 +66,21 @@ class CmdExtract
// any wrong password hints.
bool AnySolidDataUnpackedWell;
wchar ArcName[NM];
std::wstring ArcName;
bool GlobalPassword;
bool PrevProcessed; // If previous file was successfully extracted or tested.
wchar DestFileName[NM];
bool PasswordCancelled;
std::wstring DestFileName;
bool SuppressNoFilesMessage;
// In Windows it is set to true if at least one symlink with ".."
// in target was extracted.
bool ConvertSymlinkPaths;
// Last path checked for symlinks. We use it to improve the performance,
// so we do not check recently checked folders again.
std::wstring LastCheckedSymlink;
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif

View File

@@ -1,9 +1,9 @@
#include "rar.hpp"
// If NewFile==NULL, we delete created file after user confirmation.
// It is useful we we need to overwrite an existing folder or file,
// It is useful if we need to overwrite an existing folder or file,
// but need user confirmation for that.
bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
@@ -29,7 +29,7 @@ bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
// autorename below can change the name, so we need to check it again.
ShortNameChanged=false;
#endif
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
if (Choice==UIASKREP_R_REPLACE)
break;
@@ -44,95 +44,82 @@ bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
}
// Try to truncate the existing file first instead of delete,
// so we preserve existing file permissions such as NTFS permissions.
// so we preserve existing file permissions, such as NTFS permissions,
// also as "Compressed" attribute and hard links. In GUI version we avoid
// deleting an existing file for non-.rar archive formats as well.
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
if (NewFile!=NULL && NewFile->Create(Name,FileMode))
return true;
CreatePath(Name,true);
CreatePath(Name,true,Cmd->DisableNames);
return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
}
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
{
wchar NewName[NM];
size_t NameLength=wcslen(Name);
wchar *Ext=GetExt(Name);
if (Ext==NULL)
Ext=Name+NameLength;
for (uint FileVer=1;;FileVer++)
{
swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
if (!FileExist(NewName))
{
wcsncpyz(Name,NewName,MaxNameSize);
break;
}
if (FileVer>=1000000)
return false;
}
return true;
}
#if defined(_WIN_ALL)
// If we find a file, which short name is equal to 'Name', we try to change
// its short name, while preserving the long name. It helps when unpacking
// an archived file, which long name is equal to short name of already
// existing file. Otherwise we would overwrite the already existing file,
// even though its long name does not match the name of unpacking file.
bool UpdateExistingShortName(const wchar *Name)
bool UpdateExistingShortName(const std::wstring &Name)
{
wchar LongPathName[NM];
DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
if (Res==0 || Res>=ASIZE(LongPathName))
DWORD Res=GetLongPathName(Name.c_str(),NULL,0);
if (Res==0)
return false;
wchar ShortPathName[NM];
Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
if (Res==0 || Res>=ASIZE(ShortPathName))
std::vector<wchar> LongPathBuf(Res);
Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size());
if (Res==0 || Res>=LongPathBuf.size())
return false;
wchar *LongName=PointToName(LongPathName);
wchar *ShortName=PointToName(ShortPathName);
Res=GetShortPathName(Name.c_str(),NULL,0);
if (Res==0)
return false;
std::vector<wchar> ShortPathBuf(Res);
Res=GetShortPathName(Name.c_str(),ShortPathBuf.data(),(DWORD)ShortPathBuf.size());
if (Res==0 || Res>=ShortPathBuf.size())
return false;
std::wstring LongPathName=LongPathBuf.data();
std::wstring ShortPathName=ShortPathBuf.data();
std::wstring LongName=PointToName(LongPathName);
std::wstring ShortName=PointToName(ShortPathName);
// We continue only if file has a short name, which does not match its
// long name, and this short name is equal to name of file which we need
// to create.
if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
if (ShortName.empty() || wcsicomp(LongName,ShortName)==0 ||
wcsicomp(PointToName(Name),ShortName)!=0)
return false;
// Generate the temporary new name for existing file.
wchar NewName[NM];
*NewName=0;
for (int I=0;I<10000 && *NewName==0;I+=123)
std::wstring NewName;
for (uint I=0;I<10000 && NewName.empty();I+=123)
{
// Here we copy the path part of file to create. We'll make the temporary
// file in the same folder.
wcsncpyz(NewName,Name,ASIZE(NewName));
NewName=Name;
// Here we set the random name part.
swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I));
// If such file is already exist, try next random name.
if (FileExist(NewName))
*NewName=0;
NewName.clear();
}
// If we could not generate the name not used by any other file, we return.
if (*NewName==0)
if (NewName.empty())
return false;
// FastFind returns the name without path, but we need the fully qualified
// name for renaming, so we use the path from file to create and long name
// from existing file.
wchar FullName[NM];
wcsncpyz(FullName,Name,ASIZE(FullName));
SetName(FullName,LongName,ASIZE(FullName));
std::wstring FullName=Name;
SetName(FullName,LongName);
// Rename the existing file to randomly generated name. Normally it changes
// the short name too.
if (!MoveFile(FullName,NewName))
if (!MoveFile(FullName.c_str(),NewName.c_str()))
return false;
// Now we need to create the temporary empty file with same name as
@@ -147,7 +134,7 @@ bool UpdateExistingShortName(const wchar *Name)
// Now we rename the existing file from temporary name to original long name.
// Since its previous short name is occupied by another file, it should
// get another short name.
MoveFile(NewName,FullName);
MoveFile(NewName.c_str(),FullName.c_str());
if (Created)
{
@@ -155,9 +142,9 @@ bool UpdateExistingShortName(const wchar *Name)
KeepShortFile.Close();
KeepShortFile.Delete();
}
// We successfully changed the short name. Maybe sometimes we'll simplify
// this function by use of SetFileShortName Windows API call.
// But SetFileShortName is not available in older Windows.
// We successfully changed the short name. We do not use the simpler
// SetFileShortName Windows API call, because it requires SE_RESTORE_NAME
// privilege.
return true;
}
#endif

View File

@@ -1,14 +1,12 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
bool *UserReject,int64 FileSize=INT64NDF,
RarTime *FileTime=NULL,bool WriteOnly=false);
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
#if defined(_WIN_ALL)
bool UpdateExistingShortName(const wchar *Name);
bool UpdateExistingShortName(const std::wstring &Name);
#endif
#endif

View File

@@ -3,21 +3,22 @@
File::File()
{
hFile=FILE_BAD_HANDLE;
*FileName=0;
NewFile=false;
LastWrite=false;
HandleType=FILE_HANDLENORMAL;
LineInput=false;
SkipClose=false;
IgnoreReadErrors=false;
ErrorType=FILE_SUCCESS;
OpenShared=false;
AllowDelete=true;
AllowExceptions=true;
PreserveAtime=false;
#ifdef _WIN_ALL
NoSequentialRead=false;
CreateMode=FMF_UNDEFINED;
// CreateMode=FMF_UNDEFINED;
#endif
ReadErrorMode=FREM_ASK;
TruncatedAfterReadError=false;
CurFilePos=0;
}
@@ -37,12 +38,13 @@ void File::operator = (File &SrcFile)
NewFile=SrcFile.NewFile;
LastWrite=SrcFile.LastWrite;
HandleType=SrcFile.HandleType;
wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
FileName=SrcFile.FileName;
SrcFile.SkipClose=true;
}
bool File::Open(const wchar *Name,uint Mode)
bool File::Open(const std::wstring &Name,uint Mode)
{
ErrorType=FILE_SUCCESS;
FileHandle hNewFile;
@@ -56,21 +58,21 @@ bool File::Open(const wchar *Name,uint Mode)
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
if (OpenShared)
ShareMode|=FILE_SHARE_WRITE;
uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
FindData FD;
if (PreserveAtime)
Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
hNewFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
DWORD LastError;
if (hNewFile==FILE_BAD_HANDLE)
{
LastError=GetLastError();
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
{
hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
hNewFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
// For archive names longer than 260 characters first CreateFile
// (without \\?\) fails and sets LastError to 3 (access denied).
@@ -98,6 +100,12 @@ bool File::Open(const wchar *Name,uint Mode)
#else
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
// 2025.06.09: We can't just set O_DIRECT for Unix like we set
// FILE_FLAG_SEQUENTIAL_SCAN for Windows to minimize disk caching.
// O_DIRECT might impose alignment requirements for data size, data address
// and file offset. Also it might not be supported by some file systems
// and fail with an error.
#ifdef O_BINARY
flags|=O_BINARY;
#if defined(_AIX) && defined(_LARGE_FILE_API)
@@ -109,21 +117,21 @@ bool File::Open(const wchar *Name,uint Mode)
if (PreserveAtime)
flags|=O_NOATIME;
#endif
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
std::string NameA;
WideToChar(Name,NameA);
int handle=open(NameA,flags);
int handle=open(NameA.c_str(),flags);
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
extern "C" int flock(int, int);
#endif
if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
{
close(handle);
return false;
}
#endif
if (handle==-1)
hNewFile=FILE_BAD_HANDLE;
@@ -145,14 +153,15 @@ bool File::Open(const wchar *Name,uint Mode)
if (Success)
{
hFile=hNewFile;
wcsncpyz(FileName,Name,ASIZE(FileName));
FileName=Name;
TruncatedAfterReadError=false;
}
return Success;
}
#if !defined(SFX_MODULE)
void File::TOpen(const wchar *Name)
void File::TOpen(const std::wstring &Name)
{
if (!WOpen(Name))
ErrHandler.Exit(RARX_OPEN);
@@ -160,7 +169,7 @@ void File::TOpen(const wchar *Name)
#endif
bool File::WOpen(const wchar *Name)
bool File::WOpen(const std::wstring &Name)
{
if (Open(Name))
return true;
@@ -169,8 +178,9 @@ bool File::WOpen(const wchar *Name)
}
bool File::Create(const wchar *Name,uint Mode)
bool File::Create(const std::wstring &Name,uint Mode)
{
// 2025.09.03: Likely outdated info, see https://www.illumos.org/issues/2000
// OpenIndiana based NAS and CIFS shares fail to set the file time if file
// was created in read+write mode and some data was written and not flushed
// before SetFileTime call. So we should use the write only mode if we plan
@@ -178,46 +188,46 @@ bool File::Create(const wchar *Name,uint Mode)
bool WriteMode=(Mode & FMF_WRITE)!=0;
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
#ifdef _WIN_ALL
CreateMode=Mode;
// CreateMode=Mode;
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
// Windows automatically removes dots and spaces in the end of file name,
// So we detect such names and process them with \\?\ prefix.
wchar *LastChar=PointToLastChar(Name);
bool Special=*LastChar=='.' || *LastChar==' ';
wchar LastChar=GetLastChar(Name);
bool Special=LastChar=='.' || LastChar==' ';
if (Special && (Mode & FMF_STANDARDNAMES)==0)
hFile=FILE_BAD_HANDLE;
else
hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
hFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
if (hFile==FILE_BAD_HANDLE)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
}
#else
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
#ifdef FILE_USE_OPEN
hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
std::string NameA;
WideToChar(Name,NameA);
hFile=open(NameA.c_str(),(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
#else
hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY);
#endif
#endif
NewFile=true;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
wcsncpyz(FileName,Name,ASIZE(FileName));
FileName=Name;
return hFile!=FILE_BAD_HANDLE;
}
#if !defined(SFX_MODULE)
void File::TCreate(const wchar *Name,uint Mode)
void File::TCreate(const std::wstring &Name,uint Mode)
{
if (!WCreate(Name,Mode))
ErrHandler.Exit(RARX_FATAL);
@@ -225,7 +235,7 @@ void File::TCreate(const wchar *Name,uint Mode)
#endif
bool File::WCreate(const wchar *Name,uint Mode)
bool File::WCreate(const std::wstring &Name,uint Mode)
{
if (Create(Name,Mode))
return true;
@@ -246,7 +256,7 @@ bool File::Close()
// We use the standard system handle for stdout in Windows
// and it must not be closed here.
if (HandleType==FILE_HANDLENORMAL)
Success=CloseHandle(hFile)==TRUE;
Success=CloseHandle(hFile)!=FALSE;
#else
#ifdef FILE_USE_OPEN
Success=close(hFile)!=-1;
@@ -276,16 +286,16 @@ bool File::Delete()
}
bool File::Rename(const wchar *NewName)
bool File::Rename(const std::wstring &NewName)
{
// No need to rename if names are already same.
bool Success=wcscmp(FileName,NewName)==0;
bool Success=(NewName==FileName);
if (!Success)
Success=RenameFile(FileName,NewName);
if (Success)
wcsncpyz(FileName,NewName,ASIZE(FileName));
FileName=NewName;
return Success;
}
@@ -323,13 +333,13 @@ bool File::Write(const void *Data,size_t Size)
const size_t MaxSize=0x4000;
for (size_t I=0;I<Size;I+=MaxSize)
{
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)!=FALSE;
if (!Success)
break;
}
}
else
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)!=FALSE;
#else
#ifdef FILE_USE_OPEN
ssize_t Written=write(hFile,Data,Size);
@@ -358,7 +368,7 @@ bool File::Write(const void *Data,size_t Size)
Seek(Tell()-Written,SEEK_SET);
continue;
}
ErrHandler.WriteError(NULL,FileName);
ErrHandler.WriteError(L"",FileName);
}
break;
}
@@ -369,19 +379,23 @@ bool File::Write(const void *Data,size_t Size)
int File::Read(void *Data,size_t Size)
{
if (TruncatedAfterReadError)
return 0;
int64 FilePos=0; // Initialized only to suppress some compilers warning.
if (IgnoreReadErrors)
if (ReadErrorMode==FREM_IGNORE)
FilePos=Tell();
int ReadSize;
int TotalRead=0;
while (true)
{
ReadSize=DirectRead(Data,Size);
int ReadSize=DirectRead(Data,Size);
if (ReadSize==-1)
{
ErrorType=FILE_READERROR;
if (AllowExceptions)
if (IgnoreReadErrors)
if (ReadErrorMode==FREM_IGNORE)
{
ReadSize=0;
for (size_t I=0;I<Size;I+=512)
@@ -390,18 +404,49 @@ int File::Read(void *Data,size_t Size)
size_t SizeToRead=Min(Size-I,512);
int ReadCode=DirectRead(Data,SizeToRead);
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
if (ReadSize!=-1)
TotalRead+=ReadSize;
}
}
else
{
if (HandleType==FILE_HANDLENORMAL && ErrHandler.AskRepeatRead(FileName))
continue;
bool Ignore=false,Retry=false,Quit=false;
if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL && IsOpened())
{
ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
if (Retry)
continue;
}
if (Ignore || ReadErrorMode==FREM_TRUNCATE)
{
TruncatedAfterReadError=true;
return 0;
}
ErrHandler.ReadError(FileName);
}
}
TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here.
if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size)
{
// Unlike regular files, for pipe we can read only as much as was
// written at the other end of pipe. We had seen data coming in small
// ~80 byte chunks when piping from 'type arc.rar'. Extraction code
// would fail if we read an incomplete archive header from stdin.
// So here we ensure that requested size is completely read.
// But we return the available data immediately in "line input" mode,
// when processing user's input in console prompts. Otherwise apps
// piping user responses to multiple Ask() prompts can hang if no more
// data is available yet and pipe isn't closed.
Data=(byte*)Data+ReadSize;
Size-=ReadSize;
continue;
}
break;
}
return ReadSize; // It can return -1 only if AllowExceptions is disabled.
if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled.
CurFilePos+=TotalRead;
return TotalRead; // It can return -1 only if AllowExceptions is disabled.
}
@@ -483,6 +528,36 @@ bool File::RawSeek(int64 Offset,int Method)
{
if (hFile==FILE_BAD_HANDLE)
return true;
if (!IsSeekable()) // To extract archives from stdin with -si.
{
// We tried to dynamically allocate 32 KB buffer here, but it improved
// speed in Windows 10 by mere ~1.5%.
byte Buf[4096];
if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
{
uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
while (SkipSize>0) // Reading to emulate seek forward.
{
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
if (ReadSize<=0)
return false;
SkipSize-=ReadSize;
CurFilePos+=ReadSize;
}
return true;
}
// May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking
// RAR 4.x archives without the end of archive block created with -en.
if (Method==SEEK_END)
{
int ReadSize;
while ((ReadSize=Read(Buf,ASIZE(Buf)))>0)
CurFilePos+=ReadSize;
return true;
}
return false; // Backward seek on unseekable file.
}
if (Offset<0 && Method!=SEEK_SET)
{
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
@@ -517,6 +592,8 @@ int64 File::Tell()
ErrHandler.SeekError(FileName);
else
return -1;
if (!IsSeekable())
return CurFilePos;
#ifdef _WIN_ALL
LONG HighDist=0;
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
@@ -575,7 +652,7 @@ void File::PutByte(byte Byte)
bool File::Truncate()
{
#ifdef _WIN_ALL
return SetEndOfFile(hFile)==TRUE;
return SetEndOfFile(hFile)!=FALSE;
#else
return ftruncate(GetFD(),(off_t)Tell())==0;
#endif
@@ -601,8 +678,10 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
// Workaround for OpenIndiana NAS time bug. If we cannot create a file
// in write only mode, we need to flush the write buffer before calling
// SetFileTime or file time will not be changed.
if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
FlushFileBuffers(hFile);
// 2025.09.03: Removed this code as likely redundant now,
// see https://www.illumos.org/issues/2000
// if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
// FlushFileBuffers(hFile);
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
@@ -633,15 +712,15 @@ void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
}
void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta)
{
#ifdef _UNIX
bool setm=ftm!=NULL && ftm->IsSet();
bool seta=fta!=NULL && fta->IsSet();
if (setm || seta)
{
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
std::string NameA;
WideToChar(Name,NameA);
#ifdef UNIX_TIME_NS
timespec times[2];
@@ -649,7 +728,7 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
times[1].tv_sec=setm ? ftm->GetUnix() : 0;
times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
utimensat(AT_FDCWD,NameA,times,0);
utimensat(AT_FDCWD,NameA.c_str(),times,0);
#else
utimbuf ut;
if (setm)
@@ -660,24 +739,47 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
ut.actime=fta->GetUnix();
else
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
utime(NameA,&ut);
utime(NameA.c_str(),&ut);
#endif
}
#endif
}
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
FILETIME FileTime;
GetFileTime(hFile,NULL,NULL,&FileTime);
ft->SetWinFT(&FileTime);
#endif
#if defined(_UNIX) || defined(_EMX)
FILETIME ctime,atime,mtime;
GetFileTime(hFile,&ctime,&atime,&mtime);
if (ftm!=NULL) ftm->SetWinFT(&mtime);
if (ftc!=NULL) ftc->SetWinFT(&ctime);
if (fta!=NULL) fta->SetWinFT(&atime);
#elif defined(_UNIX)
struct stat st;
fstat(GetFD(),&st);
ft->SetUnix(st.st_mtime);
StatToRarTime(st,ftm,ftc,fta);
#endif
}
@@ -708,15 +810,23 @@ bool File::IsDevice()
#ifndef SFX_MODULE
int64 File::Copy(File &Dest,int64 Length)
{
Array<byte> Buffer(File::CopyBufferSize());
int64 CopySize=0;
bool CopyAll=(Length==INT64NDF);
// Adjust the buffer size to data size. So we do not waste too much time
// to vector initialization when copying many small data blocks like
// when updating an archive with many small files.
size_t BufSize=File::CopyBufferSize();
if (!CopyAll && Length<(int64)BufSize)
BufSize=(size_t)Length;
std::vector<byte> Buffer(BufSize);
int64 CopySize=0;
while (CopyAll || Length>0)
{
Wait();
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
byte *Buf=&Buffer[0];
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size();
byte *Buf=Buffer.data();
int ReadSize=Read(Buf,SizeToRead);
if (ReadSize==0)
break;

View File

@@ -14,8 +14,6 @@
#define FILE_BAD_HANDLE NULL
#endif
class RAROptions;
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
@@ -43,7 +41,13 @@ enum FILE_MODE_FLAGS {
FMF_STANDARDNAMES=32,
// Mode flags are not defined yet.
FMF_UNDEFINED=256
// FMF_UNDEFINED=256
};
enum FILE_READ_ERROR_MODE {
FREM_ASK, // Propose to use the already read part, retry or abort.
FREM_TRUNCATE, // Use the already read part without additional prompt.
FREM_IGNORE // Try to skip unreadable block and read further.
};
@@ -53,20 +57,32 @@ class File
FileHandle hFile;
bool LastWrite;
FILE_HANDLETYPE HandleType;
// If we read the user input in console prompts from stdin, we shall
// process the available line immediately, not waiting for rest of data.
// Otherwise apps piping user responses to multiple Ask() prompts can
// hang if no more data is available yet and pipe isn't closed.
// If we read RAR archive or other file data from stdin, we shall collect
// the entire requested block as long as pipe isn't closed, so we get
// complete archive headers, not split between different reads.
bool LineInput;
bool SkipClose;
bool IgnoreReadErrors;
FILE_READ_ERROR_MODE ReadErrorMode;
bool NewFile;
bool AllowDelete;
bool AllowExceptions;
#ifdef _WIN_ALL
bool NoSequentialRead;
uint CreateMode;
// uint CreateMode;
#endif
bool PreserveAtime;
bool TruncatedAfterReadError;
int64 CurFilePos; // Used for forward seeks in stdin files.
protected:
bool OpenShared; // Set by 'Archive' class.
public:
wchar FileName[NM];
std::wstring FileName;
FILE_ERRORTYPE ErrorType;
public:
@@ -76,15 +92,15 @@ class File
// Several functions below are 'virtual', because they are redefined
// by Archive for QOpen and by MultiFile for split files in WinRAR.
virtual bool Open(const wchar *Name,uint Mode=FMF_READ);
void TOpen(const wchar *Name);
bool WOpen(const wchar *Name);
bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
virtual bool Open(const std::wstring &Name,uint Mode=FMF_READ);
void TOpen(const std::wstring &Name);
bool WOpen(const std::wstring &Name);
bool Create(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
void TCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
bool WCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
virtual bool Close(); // 'virtual' for MultiFile class.
bool Delete();
bool Rename(const wchar *NewName);
bool Rename(const std::wstring &NewName);
bool Write(const void *Data,size_t Size);
virtual int Read(void *Data,size_t Size);
int DirectRead(void *Data,size_t Size);
@@ -98,24 +114,27 @@ class File
void Flush();
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
void GetOpenFileTime(RarTime *ft);
static void SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta);
#ifdef _UNIX
static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta);
#endif
void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
int64 FileLength();
virtual int64 FileLength(); // 'virtual' for MultiFile class.
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
void SetLineInputMode(bool Mode) {LineInput=Mode;}
FILE_HANDLETYPE GetHandleType() {return HandleType;}
bool IsSeekable() {return HandleType!=FILE_HANDLESTD;}
bool IsDevice();
static bool RemoveCreated();
FileHandle GetHandle() {return hFile;}
void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}
void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;}
int64 Copy(File &Dest,int64 Length=INT64NDF);
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
#ifdef _WIN_ALL
void RemoveSequentialFlag() {NoSequentialRead=true;}
#endif
void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;}
#ifdef _UNIX
int GetFD()
{
@@ -128,14 +147,9 @@ class File
#endif
static size_t CopyBufferSize()
{
#ifdef _WIN_ALL
// USB flash performance is poor with 64 KB buffer, 256+ KB resolved it.
// For copying from HDD to same HDD the best performance was with 256 KB
// buffer in XP and with 1 MB buffer in Win10.
return WinNT()==WNT_WXP ? 0x40000:0x100000;
#else
return 0x100000;
#endif
// Values in 0x100000 - 0x400000 range are ok, but multithreaded CRC32
// seems to benefit from 0x400000, especially on ARM CPUs.
return 0x400000;
}
};

View File

@@ -1,18 +1,18 @@
#include "rar.hpp"
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
{
#ifdef _WIN_ALL
// Windows automatically removes dots and spaces in the end of directory
// name. So we detect such names and process them with \\?\ prefix.
wchar *LastChar=PointToLastChar(Name);
bool Special=*LastChar=='.' || *LastChar==' ';
BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
wchar LastChar=GetLastChar(Name);
bool Special=LastChar=='.' || LastChar==' ';
BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL);
if (RetCode==0 && !FileExist(Name))
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
RetCode=CreateDirectory(LongName,NULL);
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
RetCode=CreateDirectory(LongName.c_str(),NULL);
}
if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
{
@@ -25,10 +25,10 @@ MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
return MKDIR_BADPATH;
return MKDIR_ERROR;
#elif defined(_UNIX)
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
std::string NameA;
WideToChar(Name,NameA);
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
int ErrCode=mkdir(NameA,uattr);
int ErrCode=mkdir(NameA.c_str(),uattr);
if (ErrCode==-1)
return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
return MKDIR_SUCCESS;
@@ -38,12 +38,19 @@ MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
}
bool CreatePath(const wchar *Path,bool SkipLastName)
// Simplified version of MakeDir().
bool CreateDir(const std::wstring &Name)
{
if (Path==NULL || *Path==0)
return MakeDir(Name,false,0)==MKDIR_SUCCESS;
}
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent)
{
if (Path.empty())
return false;
#if defined(_WIN_ALL) || defined(_EMX)
#ifdef _WIN_ALL
uint DirAttr=0;
#else
uint DirAttr=0777;
@@ -51,42 +58,36 @@ bool CreatePath(const wchar *Path,bool SkipLastName)
bool Success=true;
for (const wchar *s=Path;*s!=0;s++)
for (size_t I=0;I<Path.size();I++)
{
wchar DirName[NM];
if (s-Path>=ASIZE(DirName))
break;
// Process all kinds of path separators, so user can enter Unix style
// path in Windows or Windows in Unix. s>Path check avoids attempting
// path in Windows or Windows in Unix. I>0 check avoids attempting
// creating an empty directory for paths starting from path separator.
if (IsPathDiv(*s) && s>Path)
if (IsPathDiv(Path[I]) && I>0)
{
#ifdef _WIN_ALL
// We must not attempt to create "D:" directory, because first
// CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
// to create "D:" directory.
if (s==Path+2 && Path[1]==':')
if (I==2 && Path[1]==':')
continue;
#endif
wcsncpy(DirName,Path,s-Path);
DirName[s-Path]=0;
std::wstring DirName=Path.substr(0,I);
Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
if (Success)
if (Success && !Silent)
{
mprintf(St(MCreatDir),DirName);
mprintf(St(MCreatDir),DirName.c_str());
mprintf(L" %s",St(MOk));
}
}
}
if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
if (!SkipLastName && !IsPathDiv(GetLastChar(Path)))
Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
return Success;
}
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#if defined(_WIN_ALL)
bool sm=ftm!=NULL && ftm->IsSet();
@@ -98,13 +99,13 @@ void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
if (ResetAttr)
SetFileAttr(Name,0);
HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
}
@@ -122,18 +123,20 @@ void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
if (ResetAttr)
SetFileAttr(Name,DirAttr);
#endif
#if defined(_UNIX) || defined(_EMX)
#ifdef _UNIX
File::SetCloseFileTimeByName(Name,ftm,fta);
#endif
}
bool IsRemovable(const wchar *Name)
bool IsRemovable(const std::wstring &Name)
{
#if defined(_WIN_ALL)
wchar Root[NM];
GetPathRoot(Name,Root,ASIZE(Root));
int Type=GetDriveType(*Root!=0 ? Root:NULL);
std::wstring Root;
GetPathRoot(Name,Root);
int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str());
return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
#else
return false;
@@ -142,25 +145,25 @@ bool IsRemovable(const wchar *Name)
#ifndef SFX_MODULE
int64 GetFreeDisk(const wchar *Name)
int64 GetFreeDisk(const std::wstring &Name)
{
#ifdef _WIN_ALL
wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
std::wstring Root;
GetPathWithSep(Name,Root);
ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) &&
uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
return 0;
#elif defined(_UNIX)
wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
char RootA[NM];
WideToChar(Root,RootA,ASIZE(RootA));
std::wstring Root;
GetPathWithSep(Name,Root);
std::string RootA;
WideToChar(Root,RootA);
struct statvfs sfs;
if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0)
return 0;
int64 FreeSize=sfs.f_bsize;
FreeSize=FreeSize*sfs.f_bavail;
@@ -175,26 +178,27 @@ int64 GetFreeDisk(const wchar *Name)
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
// file size to 4 GB for these file systems.
bool IsFAT(const wchar *Name)
bool IsFAT(const std::wstring &Name)
{
wchar Root[NM];
GetPathRoot(Name,Root,ASIZE(Root));
std::wstring Root;
GetPathRoot(Name,Root);
wchar FileSystem[MAX_PATH+1];
if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
// Root can be empty, when we create volumes with -v in the current folder.
if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
return false;
}
#endif
bool FileExist(const wchar *Name)
bool FileExist(const std::wstring &Name)
{
#ifdef _WIN_ALL
return GetFileAttr(Name)!=0xffffffff;
#elif defined(ENABLE_ACCESS)
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
return access(NameA,0)==0;
std::string NameA;
WideToChar(Name,NameA);
return access(NameA.c_str(),0)==0;
#else
FindData FD;
return FindFile::FastFind(Name,&FD);
@@ -202,7 +206,7 @@ bool FileExist(const wchar *Name)
}
bool WildFileExist(const wchar *Name)
bool WildFileExist(const std::wstring &Name)
{
if (IsWildcard(Name))
{
@@ -230,8 +234,9 @@ bool IsUnreadable(uint Attr)
{
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
#endif
#else
return false;
#endif
}
@@ -261,70 +266,66 @@ bool IsDeleteAllowed(uint FileAttr)
}
void PrepareToDelete(const wchar *Name)
void PrepareToDelete(const std::wstring &Name)
{
#if defined(_WIN_ALL) || defined(_EMX)
#ifdef _WIN_ALL
SetFileAttr(Name,0);
#endif
#ifdef _UNIX
if (Name!=NULL)
{
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
}
std::string NameA;
WideToChar(Name,NameA);
chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR);
#endif
}
uint GetFileAttr(const wchar *Name)
uint GetFileAttr(const std::wstring &Name)
{
#ifdef _WIN_ALL
DWORD Attr=GetFileAttributes(Name);
DWORD Attr=GetFileAttributes(Name.c_str());
if (Attr==0xffffffff)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Attr=GetFileAttributes(LongName);
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Attr=GetFileAttributes(LongName.c_str());
}
return Attr;
#else
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
std::string NameA;
WideToChar(Name,NameA);
struct stat st;
if (stat(NameA,&st)!=0)
if (stat(NameA.c_str(),&st)!=0)
return 0;
return st.st_mode;
#endif
}
bool SetFileAttr(const wchar *Name,uint Attr)
bool SetFileAttr(const std::wstring &Name,uint Attr)
{
#ifdef _WIN_ALL
bool Success=SetFileAttributes(Name,Attr)!=0;
bool Success=SetFileAttributes(Name.c_str(),Attr)!=0;
if (!Success)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Success=SetFileAttributes(LongName,Attr)!=0;
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=SetFileAttributes(LongName.c_str(),Attr)!=0;
}
return Success;
#elif defined(_UNIX)
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
return chmod(NameA,(mode_t)Attr)==0;
std::string NameA;
WideToChar(Name,NameA);
return chmod(NameA.c_str(),(mode_t)Attr)==0;
#else
return false;
#endif
}
#if 0
wchar *MkTemp(wchar *Name,size_t MaxSize)
// Ext is the extension with the leading dot, like L".bat", or nullptr to use
// the default extension.
bool MkTemp(std::wstring &Name,const wchar *Ext)
{
size_t Length=wcslen(Name);
RarTime CurTime;
CurTime.SetCurrentTime();
@@ -343,18 +344,28 @@ wchar *MkTemp(wchar *Name,size_t MaxSize)
for (uint Attempt=0;;Attempt++)
{
uint Ext=Random%50000+Attempt;
wchar RndText[50];
swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
return NULL;
wcsncpyz(Name+Length,RndText,MaxSize-Length);
if (!FileExist(Name))
uint RandomExt=Random%50000+Attempt;
if (Attempt==1000)
return false;
// User asked to specify the single extension for all temporary files,
// so it can be added to server ransomware protection exceptions.
// He wrote, this protection blocks temporary files when adding
// a file to RAR archive with drag and drop. So unless a calling code
// requires a specific extension, like .bat file when uninstalling,
// we set the uniform extension here.
if (Ext==nullptr)
Ext=L".rartemp";
std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext;
if (!FileExist(NewName))
{
Name=NewName;
break;
}
}
return Name;
return true;
}
#endif
#if !defined(SFX_MODULE)
@@ -372,8 +383,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
SrcFile->Seek(0,SEEK_SET);
const size_t BufSize=0x100000;
Array<byte> Data(BufSize);
std::vector<byte> Data(BufSize);
DataHash HashCRC,HashBlake2;
HashCRC.Init(HASH_CRC32,Threads);
@@ -388,7 +398,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
SizeToRead=BufSize; // Then always attempt to read the entire buffer.
else
SizeToRead=(size_t)Min((int64)BufSize,Size);
int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
int ReadSize=SrcFile->Read(Data.data(),SizeToRead);
if (ReadSize==0)
break;
TotalRead+=ReadSize;
@@ -397,7 +407,11 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
{
#ifndef SILENT
if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength);
{
// Update only the current file progress in WinRAR, set the total to 0
// to keep it as is. It looks better for WinRAR.
uiExtractProgress(TotalRead,FileLength,0,0);
}
else
{
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
@@ -408,9 +422,9 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
}
if (CRC32!=NULL)
HashCRC.Update(&Data[0],ReadSize);
HashCRC.Update(Data.data(),ReadSize);
if (Blake2!=NULL)
HashBlake2.Update(&Data[0],ReadSize);
HashBlake2.Update(Data.data(),ReadSize);
if (Size!=INT64NDF)
Size-=ReadSize;
@@ -432,73 +446,109 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
#endif
bool RenameFile(const wchar *SrcName,const wchar *DestName)
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName)
{
#ifdef _WIN_ALL
bool Success=MoveFile(SrcName,DestName)!=0;
bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0;
if (!Success)
{
wchar LongName1[NM],LongName2[NM];
if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
Success=MoveFile(LongName1,LongName2)!=0;
std::wstring LongName1,LongName2;
if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2))
Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0;
}
return Success;
#else
char SrcNameA[NM],DestNameA[NM];
WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
WideToChar(DestName,DestNameA,ASIZE(DestNameA));
bool Success=rename(SrcNameA,DestNameA)==0;
std::string SrcNameA,DestNameA;
WideToChar(SrcName,SrcNameA);
WideToChar(DestName,DestNameA);
bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0;
return Success;
#endif
}
bool DelFile(const wchar *Name)
bool DelFile(const std::wstring &Name)
{
#ifdef _WIN_ALL
bool Success=DeleteFile(Name)!=0;
bool Success=DeleteFile(Name.c_str())!=0;
if (!Success)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Success=DeleteFile(LongName)!=0;
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=DeleteFile(LongName.c_str())!=0;
}
return Success;
#else
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
bool Success=remove(NameA)==0;
std::string NameA;
WideToChar(Name,NameA);
bool Success=remove(NameA.c_str())==0;
return Success;
#endif
}
bool DelDir(const std::wstring &Name)
{
#ifdef _WIN_ALL
bool Success=RemoveDirectory(Name.c_str())!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=RemoveDirectory(LongName.c_str())!=0;
}
return Success;
#else
std::string NameA;
WideToChar(Name,NameA);
bool Success=rmdir(NameA.c_str())==0;
return Success;
#endif
}
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool SetFileCompression(const wchar *Name,bool State)
bool SetFileCompression(const std::wstring &Name,bool State)
{
HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
return false;
}
if (hFile==INVALID_HANDLE_VALUE)
return false;
bool Success=SetFileCompression(hFile,State);
CloseHandle(hFile);
return Success;
}
bool SetFileCompression(HANDLE hFile,bool State)
{
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
DWORD Result;
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
sizeof(NewState),NULL,0,&Result,NULL);
CloseHandle(hFile);
return RetCode!=0;
}
void ResetFileCache(const std::wstring &Name)
{
// To reset file cache in Windows it is enough to open it with
// FILE_FLAG_NO_BUFFERING and then close it.
HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL);
if (hSrc!=INVALID_HANDLE_VALUE)
CloseHandle(hSrc);
}
#endif
@@ -510,3 +560,71 @@ bool SetFileCompression(const wchar *Name,bool State)
// Delete symbolic links in file path, if any, and replace them by directories.
// Prevents extracting files outside of destination folder with symlink chains.
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked)
{
// Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
// "lnk1/../dir", but converts the path to "dir". In Unix we need to call
// this function to prevent placing unpacked files outside of destination
// folder if previously we unpacked "dir/lnk1" -> "..",
// "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
// We may still need this function to prevent abusing symlink chains
// in link source path if we remove detection of such chains
// in IsRelativeSymlinkSafe. This function seems to make other symlink
// related safety checks redundant, but for now we prefer to keep them too.
//
// 2022.12.01: the performance impact is minimized after adding the check
// against the previous path and enabling this verification only after
// extracting a symlink with ".." in target. So we enabled it for Windows
// as well for extra safety.
//#ifdef _UNIX
std::wstring Path=SrcName;
size_t SkipLength=SkipPart.size();
if (SkipLength>0 && !starts_with(Path,SkipPart))
SkipLength=0; // Parameter validation, not really needed now.
// Do not check parts already checked in previous path to improve performance.
for (size_t I=0;I<Path.size() && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
if (IsPathDiv(Path[I]) && I>SkipLength)
SkipLength=I;
// Avoid converting symlinks in destination path part specified by user.
while (SkipLength<Path.size() && IsPathDiv(Path[SkipLength]))
SkipLength++;
if (Path.size()>0)
for (size_t I=Path.size()-1;I>SkipLength;I--)
if (IsPathDiv(Path[I]))
{
Path.erase(I);
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
{
#ifdef _WIN_ALL
// Normally Windows symlinks to directory look like a directory
// and are deleted with DelDir(). It is possible to create
// a file-like symlink pointing at directory, which can be deleted
// only with && DelFile, but such symlink isn't really functional.
// Here we prefer to fail deleting such symlink and skip extracting
// a file.
if (!DelDir(Path))
#else
if (!DelFile(Path))
#endif
{
ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory.
return false; // Couldn't delete the symlink to replace it with directory.
}
}
}
LastChecked=SrcName;
//#endif
return true;
}

View File

@@ -3,48 +3,55 @@
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
bool CreatePath(const wchar *Path,bool SkipLastName);
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
bool IsRemovable(const wchar *Name);
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr);
bool CreateDir(const std::wstring &Name);
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent);
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
bool IsRemovable(const std::wstring &Name);
#ifndef SFX_MODULE
int64 GetFreeDisk(const wchar *Name);
int64 GetFreeDisk(const std::wstring &Name);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool IsFAT(const wchar *Root);
bool IsFAT(const std::wstring &Root);
#endif
bool FileExist(const wchar *Name);
bool WildFileExist(const wchar *Name);
bool FileExist(const std::wstring &Name);
bool WildFileExist(const std::wstring &Name);
bool IsDir(uint Attr);
bool IsUnreadable(uint Attr);
bool IsLink(uint Attr);
void SetSFXMode(const wchar *FileName);
void EraseDiskContents(const wchar *FileName);
void SetSFXMode(const std::wstring &FileName);
void EraseDiskContents(const std::wstring &FileName);
bool IsDeleteAllowed(uint FileAttr);
void PrepareToDelete(const wchar *Name);
uint GetFileAttr(const wchar *Name);
bool SetFileAttr(const wchar *Name,uint Attr);
#if 0
wchar* MkTemp(wchar *Name,size_t MaxSize);
#endif
void PrepareToDelete(const std::wstring &Name);
uint GetFileAttr(const std::wstring &Name);
bool SetFileAttr(const std::wstring &Name,uint Attr);
bool MkTemp(std::wstring &Name,const wchar *Ext);
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
bool RenameFile(const wchar *SrcName,const wchar *DestName);
bool DelFile(const wchar *Name);
bool DelDir(const wchar *Name);
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName);
bool DelFile(const std::wstring &Name);
bool DelDir(const std::wstring &Name);
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool SetFileCompression(const wchar *Name,bool State);
bool SetFileCompression(const std::wstring &Name,bool State);
bool SetFileCompression(HANDLE hFile,bool State);
void ResetFileCache(const std::wstring &Name);
#endif
// Keep it here and not in extinfo.cpp, because it is invoked from Zip.SFX too.
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked);
#endif

View File

@@ -1,7 +1,7 @@
#include "rar.hpp"
bool ReadTextFile(
const wchar *Name,
const std::wstring &Name,
StringList *List,
bool Config,
bool AbortOnError,
@@ -10,17 +10,15 @@ bool ReadTextFile(
bool SkipComments,
bool ExpandEnvStr)
{
wchar FileName[NM];
*FileName=0;
std::wstring FileName;
if (Name!=NULL)
if (Config)
GetConfigName(Name,FileName,ASIZE(FileName),true,false);
else
wcsncpyz(FileName,Name,ASIZE(FileName));
if (Config)
GetConfigName(Name,FileName,true,false);
else
FileName=Name;
File SrcFile;
if (*FileName!=0)
if (!FileName.empty())
{
bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
@@ -34,36 +32,36 @@ bool ReadTextFile(
else
SrcFile.SetHandleType(FILE_HANDLESTD);
uint DataSize=0,ReadSize;
size_t DataSize=0,ReadSize;
const int ReadBlock=4096;
Array<byte> Data(ReadBlock);
std::vector<byte> Data(ReadBlock);
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
{
DataSize+=ReadSize;
Data.Add(ReadSize); // Always have ReadBlock available for next data.
Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data.
}
// Set to really read size, so we can zero terminate it correctly.
Data.Alloc(DataSize);
Data.resize(DataSize);
int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
if (SrcCharset==RCH_DEFAULT)
SrcCharset=DetectTextEncoding(&Data[0],DataSize);
SrcCharset=DetectTextEncoding(Data.data(),DataSize);
Array<wchar> DataW;
std::vector<wchar> DataW(ReadBlock);
if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
{
Data.Push(0); // Zero terminate.
Data.push_back(0); // Zero terminate.
#if defined(_WIN_ALL)
if (SrcCharset==RCH_OEM)
OemToCharA((char *)&Data[0],(char *)&Data[0]);
OemToCharA((char *)Data.data(),(char *)Data.data());
#endif
DataW.Alloc(Data.Size());
CharToWide((char *)&Data[0],&DataW[0],DataW.Size());
DataW.resize(Data.size());
CharToWide((char *)Data.data(),DataW.data(),DataW.size());
}
if (SrcCharset==RCH_UNICODE)
@@ -75,8 +73,8 @@ bool ReadTextFile(
LittleEndian=1;
}
DataW.Alloc(Data.Size()/2+1);
size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16.
DataW.resize(Data.size()/2+1);
size_t End=Data.size() & ~1; // We need even bytes number for UTF-16.
for (size_t I=Start;I<End;I+=2)
DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256;
DataW[(End-Start)/2]=0;
@@ -84,12 +82,12 @@ bool ReadTextFile(
if (SrcCharset==RCH_UTF8)
{
Data.Push(0); // Zero terminate data.
DataW.Alloc(Data.Size());
UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size());
Data.push_back(0); // Zero terminate data.
DataW.resize(Data.size());
UtfToWide((const char *)(Data.data()+(Utf8 ? 3:0)),DataW.data(),DataW.size());
}
wchar *CurStr=&DataW[0];
wchar *CurStr=DataW.data();
while (*CurStr!=0)
{
@@ -127,12 +125,11 @@ bool ReadTextFile(
#if defined(_WIN_ALL)
if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
{
wchar ExpName[NM];
*ExpName=0;
DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
Expanded=Result!=0 && Result<ASIZE(ExpName);
if (Expanded && *ExpName!=0)
std::wstring ExpName=CurStr;
ExpandEnvironmentStr(ExpName);
if (!ExpName.empty())
List->AddString(ExpName);
Expanded=true;
}
#endif
if (!Expanded && *CurStr!=0)

View File

@@ -2,7 +2,7 @@
#define _RAR_FILESTR_
bool ReadTextFile(
const wchar *Name,
const std::wstring &Name,
StringList *List,
bool Config,
bool AbortOnError=false,

View File

@@ -2,7 +2,6 @@
FindFile::FindFile()
{
*FindMask=0;
FirstCall=true;
#ifdef _WIN_ALL
hFind=INVALID_HANDLE_VALUE;
@@ -24,9 +23,9 @@ FindFile::~FindFile()
}
void FindFile::SetMask(const wchar *Mask)
void FindFile::SetMask(const std::wstring &Mask)
{
wcsncpyz(FindMask,Mask,ASIZE(FindMask));
FindMask=Mask;
FirstCall=true;
}
@@ -34,7 +33,7 @@ void FindFile::SetMask(const wchar *Mask)
bool FindFile::Next(FindData *fd,bool GetSymLink)
{
fd->Error=false;
if (*FindMask==0)
if (FindMask.empty())
return false;
#ifdef _WIN_ALL
if (FirstCall)
@@ -48,14 +47,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
#else
if (FirstCall)
{
wchar DirName[NM];
wcsncpyz(DirName,FindMask,ASIZE(DirName));
std::wstring DirName;
DirName=FindMask;
RemoveNameFromPath(DirName);
if (*DirName==0)
wcsncpyz(DirName,L".",ASIZE(DirName));
char DirNameA[NM];
WideToChar(DirName,DirNameA,ASIZE(DirNameA));
if ((dirp=opendir(DirNameA))==NULL)
if (DirName.empty())
DirName=L".";
std::string DirNameA;
WideToChar(DirName,DirNameA);
if ((dirp=opendir(DirNameA.c_str()))==NULL)
{
fd->Error=(errno!=ENOENT);
return false;
@@ -63,32 +62,31 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
}
while (1)
{
wchar Name[NM];
std::wstring Name;
struct dirent *ent=readdir(dirp);
if (ent==NULL)
return false;
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
if (!CharToWide(ent->d_name,Name,ASIZE(Name)))
uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
if (!CharToWide(std::string(ent->d_name),Name))
uiMsg(UIERROR_INVALIDNAME,L"",Name);
if (CmpName(FindMask,Name,MATCH_NAMES))
{
wchar FullName[NM];
wcsncpyz(FullName,FindMask,ASIZE(FullName));
*PointToName(FullName)=0;
if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
std::wstring FullName=FindMask;
FullName.erase(GetNamePos(FullName));
if (FullName.size()+Name.size()>=MAXPATHSIZE)
{
uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
return false;
}
wcsncatz(FullName,Name,ASIZE(FullName));
FullName+=Name;
if (!FastFind(FullName,fd,GetSymLink))
{
ErrHandler.OpenErrorMsg(FullName);
continue;
}
wcsncpyz(fd->Name,FullName,ASIZE(fd->Name));
fd->Name=FullName;
break;
}
}
@@ -98,14 +96,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
fd->IsLink=IsLink(fd->FileAttr);
FirstCall=false;
wchar *NameOnly=PointToName(fd->Name);
if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
std::wstring NameOnly=PointToName(fd->Name);
if (NameOnly==L"." || NameOnly==L"..")
return Next(fd);
return true;
}
bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink)
{
fd->Error=false;
#ifndef _UNIX
@@ -117,17 +115,17 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
if (hFind==INVALID_HANDLE_VALUE)
return false;
FindClose(hFind);
#else
char FindMaskA[NM];
WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
#elif defined(_UNIX)
std::string FindMaskA;
WideToChar(FindMask,FindMaskA);
struct stat st;
if (GetSymLink)
{
#ifdef SAVE_LINKS
if (lstat(FindMaskA,&st)!=0)
if (lstat(FindMaskA.c_str(),&st)!=0)
#else
if (stat(FindMaskA,&st)!=0)
if (stat(FindMaskA.c_str(),&st)!=0)
#endif
{
fd->Error=(errno!=ENOENT);
@@ -135,7 +133,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
}
}
else
if (stat(FindMaskA,&st)!=0)
if (stat(FindMaskA.c_str(),&st)!=0)
{
fd->Error=(errno!=ENOENT);
return false;
@@ -143,17 +141,9 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
fd->FileAttr=st.st_mode;
fd->Size=st.st_size;
#ifdef UNIX_TIME_NS
fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
#else
fd->mtime.SetUnix(st.st_mtime);
fd->atime.SetUnix(st.st_atime);
fd->ctime.SetUnix(st.st_ctime);
#endif
File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
fd->Name=FindMask;
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
@@ -164,17 +154,17 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
#ifdef _WIN_ALL
HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd)
{
WIN32_FIND_DATA FindData;
if (hFind==INVALID_HANDLE_VALUE)
{
hFind=FindFirstFile(Mask,&FindData);
hFind=FindFirstFile(Mask.c_str(),&FindData);
if (hFind==INVALID_HANDLE_VALUE)
{
wchar LongMask[NM];
if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
hFind=FindFirstFile(LongMask,&FindData);
std::wstring LongMask;
if (GetWinLongPath(Mask,LongMask))
hFind=FindFirstFile(LongMask.c_str(),&FindData);
}
if (hFind==INVALID_HANDLE_VALUE)
{
@@ -198,8 +188,8 @@ HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
if (hFind!=INVALID_HANDLE_VALUE)
{
wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
fd->Name=Mask;
SetName(fd->Name,FindData.cFileName);
fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
fd->FileAttr=FindData.dwFileAttributes;
fd->ftCreationTime=FindData.ftCreationTime;

View File

@@ -7,7 +7,7 @@ enum FINDDATA_FLAGS {
struct FindData
{
wchar Name[NM];
std::wstring Name;
uint64 Size;
uint FileAttr;
bool IsDir;
@@ -28,10 +28,10 @@ class FindFile
{
private:
#ifdef _WIN_ALL
static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
static HANDLE Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd);
#endif
wchar FindMask[NM];
std::wstring FindMask;
bool FirstCall;
#ifdef _WIN_ALL
HANDLE hFind;
@@ -41,9 +41,9 @@ class FindFile
public:
FindFile();
~FindFile();
void SetMask(const wchar *Mask);
void SetMask(const std::wstring &Mask);
bool Next(FindData *fd,bool GetSymLink=false);
static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false);
};
#endif

View File

@@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer)
ExternalBuffer=false;
if (AllocBuffer)
{
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
// So let's allocate 3 additional bytes for situation, when we need to
// getbits*() attempt to read data from InAddr, ... InAddr+8 positions.
// So let's allocate 8 additional bytes for situation, when we need to
// read only 1 byte from the last position of buffer and avoid a crash
// from access to next 3 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+3;
// from access to next 8 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+8;
InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area
@@ -17,7 +17,7 @@ BitInput::BitInput(bool AllocBuffer)
memset(InBuf,0,BufSize);
}
else
InBuf=NULL;
InBuf=nullptr;
}
@@ -30,21 +30,21 @@ BitInput::~BitInput()
void BitInput::faddbits(uint Bits)
{
// Function wrapped version of inline addbits to save code size.
// Function wrapped version of inline addbits to reduce the code size.
addbits(Bits);
}
uint BitInput::fgetbits()
{
// Function wrapped version of inline getbits to save code size.
// Function wrapped version of inline getbits to reduce the code size.
return getbits();
}
void BitInput::SetExternalBuffer(byte *Buf)
{
if (InBuf!=NULL && !ExternalBuffer)
if (InBuf!=nullptr && !ExternalBuffer)
delete[] InBuf;
InBuf=Buf;
ExternalBuffer=true;

View File

@@ -28,30 +28,43 @@ class BitInput
InAddr+=Bits>>3;
InBit=Bits&7;
}
// Return 16 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits()
{
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
uint32 BitField=RawGetBE4(InBuf+InAddr);
BitField >>= (16-InBit);
#else
uint BitField=(uint)InBuf[InAddr] << 16;
BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit);
#endif
return BitField & 0xffff;
}
// Return 32 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits32()
{
uint BitField=(uint)InBuf[InAddr] << 24;
BitField|=(uint)InBuf[InAddr+1] << 16;
BitField|=(uint)InBuf[InAddr+2] << 8;
BitField|=(uint)InBuf[InAddr+3];
uint BitField=RawGetBE4(InBuf+InAddr);
BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
return BitField & 0xffffffff;
}
// Return 64 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint64 getbits64()
{
uint64 BitField=RawGetBE8(InBuf+InAddr);
BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+8] >> (8-InBit);
return BitField;
}
void faddbits(uint Bits);
uint fgetbits();

View File

@@ -1,6 +1,6 @@
#define INCLUDEGLOBAL
#if defined(__BORLANDC__) || defined(_MSC_VER)
#ifdef _MSC_VER
#pragma hdrstop
#endif

View File

@@ -1,7 +1,5 @@
bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting)
{
SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
if (!FileExist(NameExisting))
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
@@ -9,10 +7,10 @@ bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
ErrHandler.SetErrorCode(RARX_CREATE);
return false;
}
CreatePath(NameNew,true);
CreatePath(NameNew,true,Cmd->DisableNames);
#ifdef _WIN_ALL
bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0;
bool Success=CreateHardLink(NameNew.c_str(),NameExisting.c_str(),NULL)!=0;
if (!Success)
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
@@ -21,10 +19,10 @@ bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
}
return Success;
#elif defined(_UNIX)
char NameExistingA[NM],NameNewA[NM];
WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA));
WideToChar(NameNew,NameNewA,ASIZE(NameNewA));
bool Success=link(NameExistingA,NameNewA)==0;
std::string NameExistingA,NameNewA;
WideToChar(NameExisting,NameExistingA);
WideToChar(NameNew,NameNewA);
bool Success=link(NameExistingA.c_str(),NameNewA.c_str())==0;
if (!Success)
{
uiMsg(UIERROR_HLINKCREATE,NameNew);

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)
return true;
@@ -76,7 +76,7 @@ void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
if (Type==HASH_BLAKE2)
blake2sp_init(blake2ctx);
#ifdef RAR_SMP
DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads);
DataHash::MaxThreads=Min(MaxThreads,HASH_POOL_THREADS);
#endif
}
@@ -88,13 +88,19 @@ void DataHash::Update(const void *Data,size_t DataSize)
CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
#endif
if (HashType==HASH_CRC32)
{
#ifdef RAR_SMP
UpdateCRC32MT(Data,DataSize);
#else
CurCRC32=CRC32(CurCRC32,Data,DataSize);
#endif
}
if (HashType==HASH_BLAKE2)
{
#ifdef RAR_SMP
if (MaxThreads>1 && ThPool==NULL)
ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER);
if (MaxThreads>1 && ThPool==nullptr)
ThPool=new ThreadPool(HASH_POOL_THREADS);
blake2ctx->ThPool=ThPool;
blake2ctx->MaxThreads=MaxThreads;
#endif
@@ -103,6 +109,146 @@ void DataHash::Update(const void *Data,size_t DataSize)
}
#ifdef RAR_SMP
THREAD_PROC(BuildCRC32Thread)
{
DataHash::CRC32ThreadData *td=(DataHash::CRC32ThreadData *)Data;
// Use 0 initial value to simplify combining the result with existing CRC32.
// It doesn't affect the first initial 0xffffffff in the data beginning.
// If we used 0xffffffff here, we would need to shift 0xffffffff left to
// block width and XOR it with block CRC32 to reset its initial value to 0.
td->DataCRC=CRC32(0,td->Data,td->DataSize);
}
// CRC is linear and distributive over addition, so CRC(a+b)=CRC(a)+CRC(b).
// Since addition in finite field is XOR, we have CRC(a^b)=CRC(a)^CRC(b).
// So CRC(aaabbb) = CRC(aaa000) ^ CRC(000bbb) = CRC(aaa000) ^ CRC(bbb),
// because CRC ignores leading zeroes. Thus to split CRC calculations
// to "aaa" and "bbb" blocks and then to threads we need to be able to
// find CRC(aaa000) knowing "aaa" quickly. We use Galois finite field to
// calculate the power of 2 to get "1000" and multiply it by "aaa".
void DataHash::UpdateCRC32MT(const void *Data,size_t DataSize)
{
const size_t MinBlock=0x4000;
if (DataSize<2*MinBlock || MaxThreads<2)
{
CurCRC32=CRC32(CurCRC32,Data,DataSize);
return;
}
if (ThPool==nullptr)
ThPool=new ThreadPool(HASH_POOL_THREADS);
size_t Threads=MaxThreads;
size_t BlockSize=DataSize/Threads;
if (BlockSize<MinBlock)
{
BlockSize=MinBlock;
Threads=DataSize/BlockSize;
}
CRC32ThreadData td[MaxPoolThreads];
//#undef USE_THREADS
for (size_t I=0;I<Threads;I++)
{
td[I].Data=(byte*)Data+I*BlockSize;
td[I].DataSize=(I+1==Threads) ? DataSize-I*BlockSize : BlockSize;
#ifdef USE_THREADS
ThPool->AddTask(BuildCRC32Thread,(void*)&td[I]);
#else
BuildCRC32Thread((void*)&td[I]);
#endif
}
#ifdef USE_THREADS
ThPool->WaitDone();
#endif // USE_THREADS
uint StdShift=gfExpCRC(uint(8*td[0].DataSize));
for (size_t I=0;I<Threads;I++)
{
// Prepare the multiplier to shift CRC to proper position.
uint ShiftMult;
if (td[I].DataSize==td[0].DataSize)
ShiftMult=StdShift; // Reuse the shift value for typical block size.
else
ShiftMult=gfExpCRC(uint(8*td[I].DataSize)); // 2 power "shift bits".
// To combine the cumulative total and current block CRC32, we multiply
// the total data CRC32 to shift value to place it to proper position.
// Invoke BitReverse32(), because 0xEDB88320 is the reversed polynomial.
// Alternatively we could adjust the multiplication function for reversed
// polynomials, but it would make it less readable without real speed gain.
// If CRC32 threads used 0xffffffff initial value, we would need
// to XOR the total data CRC32 with 0xffffffff before multiplication,
// so 0xffffffff is also shifted left to current block width and replaces
// the initial 0xffffffff CRC32 value with 0 in the current block CRC32
// after XOR'ing it with total data CRC32. Since now CRC32 threads use 0
// initial value, this is not necessary.
CurCRC32=BitReverse32(gfMulCRC(BitReverse32(CurCRC32), ShiftMult));
// Combine the total data and current block CRC32.
CurCRC32^=td[I].DataCRC;
}
}
#endif
uint DataHash::BitReverse32(uint N)
{
uint Reversed=0;
for (uint I=0;I<32;I++,N>>=1)
Reversed|=(N & 1)<<(31-I);
return Reversed;
}
// Galois field multiplication modulo POLY.
uint DataHash::gfMulCRC(uint A, uint B)
{
// For reversed 0xEDB88320 polynomial we bit reverse CRC32 before passing
// to this function, so we must use the normal polynomial here.
// We set the highest polynomial bit 33 for proper multiplication
// in case uint is larger than 32-bit.
const uint POLY=uint(0x104c11db7);
uint R = 0 ; // Multiplication result.
while (A != 0 && B != 0) // If any of multipliers becomes 0, quit early.
{
// For non-zero lowest B bit, add A to result.
R ^= (B & 1)!=0 ? A : 0;
// Make A twice larger before the next iteration.
// Subtract POLY to keep it modulo POLY if high bit is set.
A = (A << 1) ^ ((A & 0x80000000)!=0 ? POLY : 0);
B >>= 1; // Move next B bit to lowest position.
}
return R;
}
// Calculate 2 power N with square-and-multiply algorithm.
uint DataHash::gfExpCRC(uint N)
{
uint S = 2; // Starts from base value and contains the current square.
uint R = 1; // Exponentiation result.
while (N > 1)
{
if ((N & 1)!=0) // If N is odd.
R = gfMulCRC(R, S);
S = gfMulCRC(S, S); // Next square.
N >>= 1;
}
// We could change the loop condition to N > 0 and return R at expense
// of one additional gfMulCRC(S, S).
return gfMulCRC(R, S);
}
void DataHash::Result(HashValue *Result)
{
Result->Type=HashType;
@@ -129,7 +275,9 @@ bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
{
HashValue Final;
Result(&Final);
if (Key!=NULL)
#ifndef RAR_NOCRYPT
if (Key!=nullptr)
ConvertHashToMAC(&Final,Key);
#endif
return Final==*CmpValue;
}

View File

@@ -6,8 +6,16 @@ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
struct HashValue
{
void Init(HASH_TYPE Type);
bool operator == (const HashValue &cmp);
bool operator != (const HashValue &cmp) {return !(*this==cmp);}
// Use the const member, so types on both sides of "==" match.
// Otherwise clang -std=c++20 issues "ambiguity is between a regular call
// to this operator and a call with the argument order reversed" warning.
bool operator == (const HashValue &cmp) const;
// Not actually used now. Const member for same reason as operator == above.
// Can be removed after switching to C++20, which automatically provides "!="
// if operator == is defined.
bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
HASH_TYPE Type;
union
@@ -26,7 +34,24 @@ class DataHash;
class DataHash
{
public:
struct CRC32ThreadData
{
void *Data;
size_t DataSize;
uint DataCRC;
};
private:
void UpdateCRC32MT(const void *Data,size_t DataSize);
uint BitReverse32(uint N);
uint gfMulCRC(uint A, uint B);
uint gfExpCRC(uint N);
// Speed gain seems to vanish above 8 CRC32 threads.
static const uint CRC32_POOL_THREADS=8;
// Thread pool must allow at least BLAKE2_THREADS_NUMBER threads.
static const uint HASH_POOL_THREADS=Max(BLAKE2_THREADS_NUMBER,CRC32_POOL_THREADS);
HASH_TYPE HashType;
uint CurCRC32;
blake2sp_state *blake2ctx;
@@ -35,8 +60,6 @@ class DataHash
ThreadPool *ThPool;
uint MaxThreads;
// Upper limit for maximum threads to prevent wasting threads in pool.
static const uint MaxHashThreads=8;
#endif
public:
DataHash();

View File

@@ -2,7 +2,7 @@
void FileHeader::Reset(size_t SubDataSize)
{
SubData.Alloc(SubDataSize);
SubData.resize(SubDataSize);
BaseBlock::Reset();
FileHash.Init(HASH_NONE);
mtime.Reset();
@@ -37,6 +37,7 @@ void FileHeader::Reset(size_t SubDataSize)
}
/*
FileHeader& FileHeader::operator = (FileHeader &hd)
{
SubData.Reset();
@@ -45,17 +46,10 @@ FileHeader& FileHeader::operator = (FileHeader &hd)
SubData=hd.SubData;
return *this;
}
*/
void MainHeader::Reset()
{
HighPosAV=0;
PosAV=0;
CommentInHeader=false;
PackComment=false;
Locator=false;
QOpenOffset=0;
QOpenMaxSize=0;
RROffset=0;
RRMaxSize=0;
*this={};
}

View File

@@ -6,18 +6,19 @@
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
#define SIZEOF_SHORTBLOCKHEAD 7
#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size.
#define SIZEOF_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26
#define SIZEOF_UOHEAD 18
#define SIZEOF_STREAMHEAD 26
#define VER_PACK 29U
#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive.
#define VER_PACK7 70U // It is stored as 1, but we subtract 70 when saving an archive.
#define VER_UNPACK 29U
#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive.
#define VER_UNPACK7 70U // It is stored as 1, but we add 50 when reading an archive.
#define VER_UNKNOWN 9999U // Just some large value.
#define MHD_VOLUME 0x0001U
@@ -83,7 +84,7 @@ enum HEADER_TYPE {
};
// RAR 2.9 and earlier.
// RAR 2.9 and earlier service haeders, mostly outdated and not supported.
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
@@ -148,6 +149,14 @@ struct BaseBlock
{
SkipIfUnknown=false;
}
// We use it to assign this block data to inherited blocks.
// Such function seems to be cleaner than '(BaseBlock&)' cast or adding
// 'using BaseBlock::operator=;' to every inherited header.
void SetBaseBlock(BaseBlock &Src)
{
*this=Src;
}
};
@@ -162,12 +171,16 @@ struct MainHeader:BaseBlock
ushort HighPosAV;
uint PosAV;
bool CommentInHeader;
bool PackComment; // For RAR 1.4 archive format only.
bool PackComment; // For RAR 1.4 archive format only.
bool Locator;
uint64 QOpenOffset; // Offset of quick list record.
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
uint64 RROffset; // Offset of recovery record.
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
uint64 QOpenOffset; // Offset of quick list record.
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
uint64 RROffset; // Offset of recovery record.
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
std::wstring OrigName; // Original archive name.
RarTime OrigTime; // Original archive time.
void Reset();
};
@@ -181,9 +194,9 @@ struct FileHeader:BlockHeader
uint FileAttr;
uint SubFlags;
};
wchar FileName[NM];
std::wstring FileName;
Array<byte> SubData;
std::vector<byte> SubData;
RarTime mtime;
RarTime ctime;
@@ -223,20 +236,20 @@ struct FileHeader:BlockHeader
bool Dir;
bool CommentInHeader; // RAR 2.0 file comment.
bool Version; // name.ext;ver file name containing the version number.
size_t WinSize;
uint64 WinSize;
bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
// 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
bool LargeFile;
// 'true' for HEAD_SERVICE block, which is a child of preceding file block.
// RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives.
// RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
bool SubBlock;
HOST_SYSTEM_TYPE HSType;
FILE_SYSTEM_REDIRECT RedirType;
wchar RedirName[NM];
std::wstring RedirName;
bool DirTarget;
bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
@@ -253,10 +266,10 @@ struct FileHeader:BlockHeader
bool CmpName(const wchar *Name)
{
return(wcscmp(FileName,Name)==0);
return FileName==Name;
}
FileHeader& operator = (FileHeader &hd);
// FileHeader& operator = (FileHeader &hd);
};
@@ -321,16 +334,6 @@ struct ProtectHeader:BlockHeader
};
struct UnixOwnersHeader:SubBlockHeader
{
ushort OwnerNameSize;
ushort GroupNameSize;
/* dummy */
char OwnerName[256];
char GroupName[256];
};
struct EAHeader:SubBlockHeader
{
uint UnpSize;
@@ -347,7 +350,7 @@ struct StreamHeader:SubBlockHeader
byte Method;
uint StreamCRC;
ushort StreamNameSize;
char StreamName[260];
std::string StreamName;
};

View File

@@ -42,28 +42,42 @@
// RAR 5.0 file compression flags.
#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm.
#define FCI_ALGO_BIT1 0x0002 // 0 .. 63.
#define FCI_ALGO_BIT2 0x0004
#define FCI_ALGO_BIT3 0x0008
#define FCI_ALGO_BIT4 0x0010
#define FCI_ALGO_BIT5 0x0020
#define FCI_SOLID 0x0040 // Solid flag.
#define FCI_METHOD_BIT0 0x0080 // Compression method.
#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used).
#define FCI_METHOD_BIT2 0x0200
#define FCI_DICT_BIT0 0x0400 // Dictionary size.
#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB.
#define FCI_DICT_BIT2 0x1000
#define FCI_DICT_BIT3 0x2000
#define FCI_ALGO_BIT0 0x00000001 // Version of compression algorithm.
#define FCI_ALGO_BIT1 0x00000002 // 0 .. 63.
#define FCI_ALGO_BIT2 0x00000004
#define FCI_ALGO_BIT3 0x00000008
#define FCI_ALGO_BIT4 0x00000010
#define FCI_ALGO_BIT5 0x00000020
#define FCI_SOLID 0x00000040 // Solid flag.
#define FCI_METHOD_BIT0 0x00000080 // Compression method.
#define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used).
#define FCI_METHOD_BIT2 0x00000200
#define FCI_DICT_BIT0 0x00000400 // Dictionary size.
#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 1 TB.
#define FCI_DICT_BIT2 0x00001000
#define FCI_DICT_BIT3 0x00002000
#define FCI_DICT_BIT4 0x00004000
#define FCI_DICT_FRACT0 0x00008000 // Dictionary fraction in 1/32 of size.
#define FCI_DICT_FRACT1 0x00010000
#define FCI_DICT_FRACT2 0x00020000
#define FCI_DICT_FRACT3 0x00040000
#define FCI_DICT_FRACT4 0x00080000
#define FCI_RAR5_COMPAT 0x00100000 // RAR7 compression flags and RAR5 compression algorithm.
// Main header extra field values.
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
#define MHEXTRA_METADATA 0x02 // Archive metadata.
// Flags for MHEXTRA_LOCATOR.
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
// Flags for MHEXTRA_METADATA.
#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
// File and service header extra field values.
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
#define FHEXTRA_HASH 0x02 // File hash.

View File

@@ -1,6 +1,5 @@
#include "rar.hpp"
#ifdef _WIN_ALL
DWORD WinNT()
{
static int dwPlatformId=-1;
@@ -13,6 +12,7 @@ DWORD WinNT()
dwPlatformId=WinVer.dwPlatformId;
dwMajorVersion=WinVer.dwMajorVersion;
dwMinorVersion=WinVer.dwMinorVersion;
}
DWORD Result=0;
if (dwPlatformId==VER_PLATFORM_WIN32_NT)
@@ -21,4 +21,91 @@ DWORD WinNT()
return Result;
}
// Since Windows 10 development is stopped, we can assume that its build
// number never reaches 22000. So we do not need WMI anymore.
#if 0
// Replace it with documented Windows 11 check when available.
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
static bool WMI_IsWindows10()
{
IWbemLocator *pLoc = NULL;
HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
IID_IWbemLocator,(LPVOID *)&pLoc);
if (FAILED(hres))
return false;
IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,0,NULL,NULL,&pSvc);
if (FAILED(hres))
{
pLoc->Release();
return false;
}
hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,
RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);
if (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return false;
}
IEnumWbemClassObject *pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hres) || pEnumerator==NULL)
{
pSvc->Release();
pLoc->Release();
return false;
}
bool Win10=false;
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (pclsObj!=NULL && uReturn>0)
{
VARIANT vtProp;
pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL;
VariantClear(&vtProp);
pclsObj->Release();
}
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
return Win10;
}
#endif
// Replace it with actual check when available.
bool IsWindows11OrGreater()
{
static bool IsSet=false,IsWin11=false;
if (!IsSet)
{
OSVERSIONINFO WinVer;
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
GetVersionEx(&WinVer);
IsWin11=WinVer.dwMajorVersion>10 ||
WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000/* && !WMI_IsWindows10()*/;
IsSet=true;
}
return IsWin11;
}

View File

@@ -10,4 +10,7 @@ enum WINNT_VERSION {
DWORD WinNT();
// Replace it with actual check when available.
bool IsWindows11OrGreater();
#endif

201
unrar/largepage.cpp Normal file
View File

@@ -0,0 +1,201 @@
#include "rar.hpp"
/*
To enable, disable or check Large Memory pages manually:
- open "Local Security Policy" from "Start Menu";
- open "Lock Pages in Memory" in "Local Policies\User Rights Assignment";
- add or remove the user and sign out and sign in or restart Windows.
*/
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
#define ALLOW_LARGE_PAGES
#endif
LargePageAlloc::LargePageAlloc()
{
UseLargePages=false;
#ifdef ALLOW_LARGE_PAGES
PageSize=0;
#endif
}
void LargePageAlloc::AllowLargePages(bool Allow)
{
#ifdef ALLOW_LARGE_PAGES
if (Allow && PageSize==0)
{
HMODULE hKernel=GetModuleHandle(L"kernel32.dll");
if (hKernel!=nullptr)
{
typedef SIZE_T (*GETLARGEPAGEMINIMUM)();
GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum");
if (pGetLargePageMinimum!=nullptr)
PageSize=pGetLargePageMinimum();
}
if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME))
{
UseLargePages=false;
return;
}
}
UseLargePages=Allow;
#endif
}
bool LargePageAlloc::IsPrivilegeAssigned()
{
#ifdef ALLOW_LARGE_PAGES
return SetPrivilege(SE_LOCK_MEMORY_NAME);
#else
return true;
#endif
}
bool LargePageAlloc::AssignPrivilege()
{
#ifdef ALLOW_LARGE_PAGES
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// Get the required buffer size.
DWORD BufSize=0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize);
if (BufSize==0 || BufSize>1000000) // Sanity check for returned value.
{
CloseHandle(hToken);
return false;
}
TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize);
// Get the current user token information.
if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0)
{
CloseHandle(hToken);
return false;
}
// Get SID string for the current user.
LPWSTR ApiSidStr;
ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr);
// Convert SID to C++ string and release API based buffer.
std::wstring SidStr=ApiSidStr;
LocalFree(ApiSidStr);
CloseHandle(hToken);
if (IsUserAdmin())
AssignPrivilegeBySid(SidStr);
else
{
// Define here, so they survive until ShellExecuteEx call.
std::wstring ExeName=GetModuleFileStr();
std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr;
SHELLEXECUTEINFO shExecInfo{};
shExecInfo.cbSize = sizeof(shExecInfo);
shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode.
shExecInfo.lpVerb = L"runas";
shExecInfo.lpFile = ExeName.c_str();
shExecInfo.lpParameters = Param.c_str();
shExecInfo.nShow = SW_SHOWNORMAL;
BOOL Result=ShellExecuteEx(&shExecInfo);
}
#endif
return true;
}
bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid)
{
#ifdef ALLOW_LARGE_PAGES
LSA_HANDLE PolicyHandle;
LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it.
#ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR.
// We define STATUS_SUCCESS here instead of including ntstatus.h to avoid
// macro redefinition warnings. We tried UMDF_USING_NTSTATUS define
// and other workarounds, but it didn't help.
const uint STATUS_SUCCESS=0;
#endif
if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT|
POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS)
return false;
PSID UserSid;
ConvertStringSidToSid(Sid.c_str(),&UserSid);
LSA_UNICODE_STRING LsaString;
LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME;
// It must be in bytes, so multiple it to sizeof(wchar_t).
LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]);
LsaString.MaximumLength=LsaString.Length;
bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS;
LocalFree(UserSid);
LsaClose(PolicyHandle);
mprintf(St(MPrivilegeAssigned));
if (Ask(St(MYesNo)) == 1)
Shutdown(POWERMODE_RESTART);
return Success;
#else
return true;
#endif
}
bool LargePageAlloc::AssignConfirmation()
{
#ifdef ALLOW_LARGE_PAGES
mprintf(St(MLockInMemoryNeeded));
return Ask(St(MYesNo)) == 1;
#else
return false;
#endif
}
void* LargePageAlloc::new_large(size_t Size)
{
void *Allocated=nullptr;
#ifdef ALLOW_LARGE_PAGES
if (UseLargePages && Size>=PageSize)
{
// VirtualAlloc fails if allocation size isn't multiple of page size.
SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize;
Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE);
if (Allocated!=nullptr)
LargeAlloc.push_back(Allocated);
}
#endif
return Allocated;
}
bool LargePageAlloc::delete_large(void *Addr)
{
#ifdef ALLOW_LARGE_PAGES
if (Addr!=nullptr)
for (size_t I=0;I<LargeAlloc.size();I++)
if (LargeAlloc[I]==Addr)
{
LargeAlloc[I]=nullptr;
VirtualFree(Addr,0,MEM_RELEASE);
return true;
}
#endif
return false;
}

54
unrar/largepage.hpp Normal file
View File

@@ -0,0 +1,54 @@
#ifndef _RAR_LARGEPAGE_
#define _RAR_LARGEPAGE_
class LargePageAlloc
{
private:
static constexpr const wchar *LOCKMEM_SWITCH=L"isetup_privilege_lockmem";
void* new_large(size_t Size);
bool delete_large(void *Addr);
#ifdef _WIN_ALL
std::vector<void*> LargeAlloc;
SIZE_T PageSize;
#endif
bool UseLargePages;
public:
LargePageAlloc();
void AllowLargePages(bool Allow);
static bool IsPrivilegeAssigned();
static bool AssignPrivilege();
static bool AssignPrivilegeBySid(const std::wstring &Sid);
static bool AssignConfirmation();
static bool ProcessSwitch(CommandData *Cmd,const wchar *Switch)
{
if (Switch[0]==LOCKMEM_SWITCH[0])
{
size_t Length=wcslen(LOCKMEM_SWITCH);
if (wcsncmp(Switch,LOCKMEM_SWITCH,Length)==0)
{
LargePageAlloc::AssignPrivilegeBySid(Switch+Length);
return true;
}
}
return false;
}
template <class T> T* new_l(size_t Size,bool Clear=false)
{
T *Allocated=(T*)new_large(Size*sizeof(T));
if (Allocated==nullptr)
Allocated=Clear ? new T[Size]{} : new T[Size];
return Allocated;
}
template <class T> void delete_l(T *Addr)
{
if (!delete_large(Addr))
delete[] Addr;
}
};
#endif

View File

@@ -1,7 +1,6 @@
#include "rar.hpp"
static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare);
static void ListSymLink(Archive &Arc);
static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames);
static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
static void ListOldSubHeader(Archive &Arc);
static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
@@ -15,20 +14,17 @@ void ListArchive(CommandData *Cmd)
bool Bare=(Cmd->Command[1]=='B');
bool Verbose=(Cmd->Command[0]=='V');
wchar ArcName[NM];
while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
std::wstring ArcName;
while (Cmd->GetArcName(ArcName))
{
if (Cmd->ManualPassword)
Cmd->Password.Clean(); // Clean user entered password before processing next archive.
Archive Arc(Cmd);
#ifdef _WIN_ALL
Arc.RemoveSequentialFlag();
#endif
if (!Arc.WOpen(ArcName))
continue;
bool FileMatched=true;
while (1)
while (true)
{
int64 TotalPackSize=0,TotalUnpSize=0;
uint FileCount=0;
@@ -38,38 +34,47 @@ void ListArchive(CommandData *Cmd)
if (!Bare)
{
Arc.ViewComment();
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName.c_str());
mprintf(L"\n%s: ",St(MListDetails));
uint SetCount=0;
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt);
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 1.5":L"RAR 5");
mprintf(L"%s", Fmt);
if (Arc.Solid)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid));
mprintf(L", %s", St(MListSolid));
if (Arc.SFXSize>0)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX));
mprintf(L", %s", St(MListSFX));
if (Arc.Volume)
if (Arc.Format==RARFMT50)
{
// RAR 5.0 archives store the volume number in main header,
// so it is already available now.
if (SetCount++ > 0)
mprintf(L", ");
mprintf(L", ");
mprintf(St(MVolumeNumber),Arc.VolNumber+1);
}
else
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume));
mprintf(L", %s", St(MListVolume));
if (Arc.Protected)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR));
mprintf(L", %s", St(MListRR));
if (Arc.Locked)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
mprintf(L", %s", St(MListLock));
if (Arc.Encrypted)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead));
mprintf(L", %s", St(MListEncHead));
if (!Arc.MainHead.OrigName.empty())
mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str());
if (Arc.MainHead.OrigTime.IsSet())
{
wchar DateStr[50];
Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical);
mprintf(L"\n%s: %s",St(MOriginalTime),DateStr);
}
mprintf(L"\n");
}
wchar VolNumText[50];
*VolNumText=0;
while(Arc.ReadHeader()>0)
while (Arc.ReadHeader()>0)
{
Wait(); // Allow quit listing with Ctrl+C.
HEADER_TYPE HeaderType=Arc.GetHeaderType();
@@ -92,10 +97,10 @@ void ListArchive(CommandData *Cmd)
switch(HeaderType)
{
case HEAD_FILE:
FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0;
FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
if (FileMatched)
{
ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare);
ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames);
if (!Arc.FileHead.SplitBefore)
{
TotalUnpSize+=Arc.FileHead.UnpSize;
@@ -105,10 +110,21 @@ void ListArchive(CommandData *Cmd)
}
break;
case HEAD_SERVICE:
// For service blocks dependent on previous block, such as ACL
// or NTFS stream, we use "file matched" flag of host file.
// Independent blocks like RR are matched separately,
// so we can list them by their name. Also we match even
// dependent blocks separately if "vta -idn" are set. User may
// want to see service blocks only in this case.
if (!Arc.SubHead.SubBlock || Cmd->DisableNames)
FileMatched=Cmd->IsProcessFile(Arc.SubHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
if (FileMatched && !Bare)
{
// Here we set DisableNames parameter to true regardless of
// Cmd->DisableNames. If "vta -idn" are set together, user
// wants to see service blocks like RR only.
if (Technical && ShowService)
ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false);
ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,false);
}
break;
}
@@ -125,15 +141,15 @@ void ListArchive(CommandData *Cmd)
if (Verbose)
{
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
mprintf(L"\n----------- ---------- ---------- ----- ---------- ----- -------- ----");
mprintf(L"\n%22ls %10ls %3d%% %-27ls %u",UnpSizeText,
PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
VolNumText,FileCount);
}
else
{
mprintf(L"\n----------- --------- ---------- ----- ----");
mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
mprintf(L"\n----------- ---------- ---------- ----- ----");
mprintf(L"\n%22ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
}
SumFileCount+=FileCount;
@@ -147,7 +163,7 @@ void ListArchive(CommandData *Cmd)
ArcCount++;
#ifndef NOVOLUME
if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter ||
if (Cmd->VolSize==VOLSIZE_AUTO && (Arc.FileHead.SplitAfter ||
Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
MergeArchive(Arc,NULL,false,Cmd->Command[0]))
Arc.Seek(0,SEEK_SET);
@@ -158,7 +174,7 @@ void ListArchive(CommandData *Cmd)
else
{
if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
mprintf(St(MNotRAR),Arc.FileName);
mprintf(St(MNotRAR),Arc.FileName.c_str());
break;
}
}
@@ -188,9 +204,30 @@ enum LISTCOL_TYPE {
};
void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare)
void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames)
{
wchar *Name=hd.FileName;
if (!TitleShown && !Technical && !Bare)
{
if (Verbose)
{
mprintf(L"\n%ls",St(MListTitleV));
if (!DisableNames)
mprintf(L"\n----------- ---------- ---------- ----- ---------- ----- -------- ----");
}
else
{
mprintf(L"\n%ls",St(MListTitleL));
if (!DisableNames)
mprintf(L"\n----------- ---------- ---------- ----- ----");
}
// Must be set even in DisableNames mode to suppress "0 files" output
// unless no files are matched.
TitleShown=true;
}
if (DisableNames)
return;
const wchar *Name=hd.FileName.c_str();
RARFORMAT Format=Arc.Format;
if (Bare)
@@ -199,21 +236,6 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
return;
}
if (!TitleShown && !Technical)
{
if (Verbose)
{
mprintf(L"\n%ls",St(MListTitleV));
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
}
else
{
mprintf(L"\n%ls",St(MListTitleL));
mprintf(L"\n----------- --------- ---------- ----- ----");
}
TitleShown=true;
}
wchar UnpSizeText[30],PackSizeText[30];
if (hd.UnpSize==INT64NDF)
wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText));
@@ -238,7 +260,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
if (hd.SplitAfter)
wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr));
else
swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
swprintf(RatioStr,ASIZE(RatioStr),L"%u%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
wchar DateStr[50];
hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
@@ -252,9 +274,8 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
{
mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
wchar StreamName[NM];
GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName));
mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName);
std::wstring StreamName=GetStreamNameNTFS(Arc);
mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName.c_str());
}
else
{
@@ -278,43 +299,57 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
if (hd.RedirType!=FSREDIR_NONE)
if (Format==RARFMT15)
{
char LinkTargetA[NM];
std::string LinkTargetA;
if (Arc.FileHead.Encrypted)
{
// Link data are encrypted. We would need to ask for password
// and initialize decryption routine to display the link target.
strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA));
LinkTargetA="*<-?->";
}
else
{
int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1);
Arc.Read(LinkTargetA,DataSize);
LinkTargetA[DataSize > 0 ? DataSize : 0] = 0;
size_t DataSize=(size_t)Min(hd.PackSize,MAXPATHSIZE);
std::vector<char> Buf(DataSize+1);
Arc.Read(Buf.data(),DataSize);
Buf[DataSize] = 0;
LinkTargetA=Buf.data();
}
wchar LinkTarget[NM];
CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget));
mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget);
std::wstring LinkTarget;
CharToWide(LinkTargetA,LinkTarget);
mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget.c_str());
}
else
mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName);
mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName.c_str());
}
if (!hd.Dir)
{
mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_RR))
{
// Display the original -rrN percent if available.
int RecoveryPercent=Arc.GetRecoveryPercent();
if (RecoveryPercent>0) // It can be -1 if failed to detect.
mprintf(L"\n%12ls: %u%%",L"RR%", RecoveryPercent);
}
}
bool WinTitles=false;
#ifdef _WIN_ALL
WinTitles=true;
#endif
if (hd.mtime.IsSet())
mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr);
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr);
if (hd.ctime.IsSet())
{
hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr);
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr);
}
if (hd.atime.IsSet())
{
hd.atime.GetText(DateStr,ASIZE(DateStr),true);
mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr);
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr);
}
mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
if (hd.FileHash.Type==HASH_CRC32)
@@ -323,11 +358,11 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
hd.FileHash.CRC32);
if (hd.FileHash.Type==HASH_BLAKE2)
{
wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1];
BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr));
std::wstring BlakeStr;
BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,BlakeStr);
mprintf(L"\n%12ls: %ls",
hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
BlakeStr);
BlakeStr.c_str());
}
const wchar *HostOS=L"";
@@ -344,11 +379,22 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
if (*HostOS!=0)
mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo),
std::wstring WinSize;
if (!hd.Dir)
if (hd.WinSize%1073741824==0)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"g";
else
if (hd.WinSize%1048576==0)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"m";
else
if (hd.WinSize>=1024)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"k";
else
WinSize=L" -md=?";
mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo),
Format==RARFMT15 ? L"1.5":L"5.0",
hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,
hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400,
hd.WinSize>=0x100000 ? L"M":L"K");
hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,WinSize.c_str());
if (hd.Solid || hd.Encrypted)
{
@@ -361,7 +407,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
if (hd.Version)
{
uint Version=ParseVersionFileName(Name,false);
uint Version=ParseVersionFileName(hd.FileName,false);
if (Version!=0)
mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
}
@@ -370,25 +416,27 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
{
mprintf(L"\n%12ls: ",L"Unix owner");
if (*hd.UnixOwnerName!=0)
mprintf(L"%ls:",GetWide(hd.UnixOwnerName));
mprintf(L"%ls",GetWide(hd.UnixOwnerName).c_str());
else
if (hd.UnixOwnerNumeric)
mprintf(L"#%d",hd.UnixOwnerID);
mprintf(L":");
if (*hd.UnixGroupName!=0)
mprintf(L"%ls",GetWide(hd.UnixGroupName));
if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric))
mprintf(L" ");
if (hd.UnixOwnerNumeric)
mprintf(L"#%d:",hd.UnixOwnerID);
if (hd.UnixGroupNumeric)
mprintf(L"#%d:",hd.UnixGroupID);
mprintf(L"%ls",GetWide(hd.UnixGroupName).c_str());
else
if (hd.UnixGroupNumeric)
mprintf(L"#%d",hd.UnixGroupID);
}
mprintf(L"\n");
return;
}
mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
// 2025.12.01: Size field width incremented to properly align 1+ GB sizes.
mprintf(L"\n%c%10ls %10ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
if (Verbose)
mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
mprintf(L"%10ls %4ls ",PackSizeText,RatioStr);
mprintf(L" %ls ",DateStr);
@@ -403,31 +451,11 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
}
else
mprintf(L"???????? ");
mprintf(hd.Dir ? L" ":L"???????? "); // Missing checksum is ok for folder, not for file.
}
mprintf(L"%ls",Name);
}
/*
void ListSymLink(Archive &Arc)
{
if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000)
if (Arc.FileHead.Encrypted)
{
// Link data are encrypted. We would need to ask for password
// and initialize decryption routine to display the link target.
mprintf(L"\n%22ls %ls",L"-->",L"*<-?->");
}
else
{
char FileName[NM];
uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1);
Arc.Read(FileName,DataSize);
FileName[DataSize]=0;
mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName));
}
}
*/
void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
{

View File

@@ -4,7 +4,8 @@
#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit"
#define MContinueQuit L"_Continue_Quit"
#define MRetryAbort L"_Retry_Abort"
#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d"
#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit"
#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %.3s %d"
#define MRegTo L"\nRegistered to %s\n"
#define MShare L"\nTrial version Type 'rar -?' for help\n"
#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n"
@@ -12,21 +13,29 @@
#define MBeta L"beta"
#define Mx86 L"x86"
#define Mx64 L"x64"
#define MMonthJan L"Jan"
#define MMonthFeb L"Feb"
#define MMonthMar L"Mar"
#define MMonthApr L"Apr"
#define MMonthJan L"January"
#define MMonthFeb L"February"
#define MMonthMar L"March"
#define MMonthApr L"April"
#define MMonthMay L"May"
#define MMonthJun L"Jun"
#define MMonthJul L"Jul"
#define MMonthAug L"Aug"
#define MMonthSep L"Sep"
#define MMonthOct L"Oct"
#define MMonthNov L"Nov"
#define MMonthDec L"Dec"
#define MMonthJun L"June"
#define MMonthJul L"July"
#define MMonthAug L"August"
#define MMonthSep L"September"
#define MMonthOct L"October"
#define MMonthNov L"November"
#define MMonthDec L"December"
#define MWeekDayMon L"Monday"
#define MWeekDayTue L"Tuesday"
#define MWeekDayWed L"Wednesday"
#define MWeekDayThu L"Thursday"
#define MWeekDayFri L"Friday"
#define MWeekDaySat L"Saturday"
#define MWeekDaySun L"Sunday"
#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>"
#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>"
#define MCHelpCmd L"\n\n<Commands>"
#define MCHelpCmdA L"\n a Add files to archive"
#define MCHelpCmdC L"\n c Add archive comment"
@@ -54,9 +63,10 @@
#define MCHelpSwm L"\n - Stop switches scanning"
#define MCHelpSwAT L"\n @[+] Disable [enable] file lists"
#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction"
#define MCHelpSwAD L"\n ad Append archive name to destination path"
#define MCHelpSwAD L"\n ad[1,2] Alternate destination path"
#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
#define MCHelpSwAI L"\n ai Ignore file attributes"
#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]"
#define MCHelpSwAO L"\n ao Add files with Archive attribute set"
#define MCHelpSwAP L"\n ap<path> Set path inside archive"
#define MCHelpSwAS L"\n as Synchronize archive contents"
@@ -71,15 +81,15 @@
#define MCHelpSwDW L"\n dw Wipe files after archiving"
#define MCHelpSwEa L"\n e[+]<attr> Set file exclude and include attributes"
#define MCHelpSwED L"\n ed Do not add empty directories"
#define MCHelpSwEN L"\n en Do not put 'end of archive' block"
#define MCHelpSwEP L"\n ep Exclude paths from names"
#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names"
#define MCHelpSwEP2 L"\n ep2 Expand paths to full"
#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter"
#define MCHelpSwEP4 L"\n ep4<path> Exclude the path prefix from names"
#define MCHelpSwF L"\n f Freshen files"
#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers"
#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum"
#define MCHelpSwIDP L"\n id[c,d,p,q] Disable messages"
#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages"
#define MCHelpSwIEML L"\n ieml[addr] Send archive by email"
#define MCHelpSwIERR L"\n ierr Send all messages to stderr"
#define MCHelpSwILOG L"\n ilog[name] Log errors to file"
@@ -91,9 +101,10 @@
#define MCHelpSwKB L"\n kb Keep broken extracted files"
#define MCHelpSwLog L"\n log[f][=name] Write names to log file"
#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)"
#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format"
#define MCHelpSwMC L"\n mc<par> Set advanced compression parameters"
#define MCHelpSwMD L"\n md<n>[k,m,g] Dictionary size in KB, MB or GB"
#define MCHelpSwMD L"\n md[x]<n>[kmg] Dictionary size in KB, MB or GB"
#define MCHelpSwME L"\n me[par] Set encryption parameters"
#define MCHelpSwMLP L"\n mlp Use large memory pages"
#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store"
#define MCHelpSwMT L"\n mt<threads> Set the number of threads"
#define MCHelpSwN L"\n n<file> Additionally filter included files"
@@ -103,13 +114,14 @@
#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute"
#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file"
#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references"
#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]"
#define MCHelpSwOL L"\n ol[a,-] Process symbolic links as the link [absolute paths, skip]"
#define MCHelpSwOM L"\n om[-|1][=lst] Propagate Mark of the Web"
#define MCHelpSwONI L"\n oni Allow potentially incompatible names"
#define MCHelpSwOP L"\n op<path> Set the output path for extracted files"
#define MCHelpSwOR L"\n or Rename files automatically"
#define MCHelpSwOS L"\n os Save NTFS streams"
#define MCHelpSwOW L"\n ow Save or restore file owner and group"
#define MCHelpSwP L"\n p[password] Set password"
#define MCHelpSwPm L"\n p- Do not query password"
#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]"
#define MCHelpSwR L"\n r Recurse subdirectories"
#define MCHelpSwRm L"\n r- Disable recursion"
@@ -117,15 +129,14 @@
#define MCHelpSwRI L"\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms"
#define MCHelpSwRR L"\n rr[N] Add data recovery record"
#define MCHelpSwRV L"\n rv[N] Create recovery volumes"
#define MCHelpSwS L"\n s[<N>,v[-],e] Create solid archive"
#define MCHelpSwSm L"\n s- Disable solid archiving"
#define MCHelpSwS L"\n s[=<par>] Create solid archive"
#define MCHelpSwSC L"\n sc<chr>[obj] Specify the character set"
#define MCHelpSwSFX L"\n sfx[name] Create SFX archive"
#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)"
#define MCHelpSwSL L"\n sl<size> Process files with size less than specified"
#define MCHelpSwSM L"\n sm<size> Process files with size more than specified"
#define MCHelpSwSL L"\n sl<size>[u] Process files with size less than specified"
#define MCHelpSwSM L"\n sm<size>[u] Process files with size more than specified"
#define MCHelpSwT L"\n t Test files after archiving"
#define MCHelpSwTK L"\n tk Keep original archive time"
#define MCHelpSwTK L"\n tk[<date>] Keep the original or set the specified archive time"
#define MCHelpSwTL L"\n tl Set archive time to latest file"
#define MCHelpSwTN L"\n tn[mcao]<t> Process files newer than <t> time"
#define MCHelpSwTO L"\n to[mcao]<t> Process files older than <t> time"
@@ -135,10 +146,9 @@
#define MCHelpSwU L"\n u Update files"
#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes"
#define MCHelpSwVUnr L"\n v List all volumes"
#define MCHelpSwVn L"\n v<size>[k,b] Create volumes with size=<size>*1000 [*1024, *1]"
#define MCHelpSwVn L"\n v<size>[u] Create volumes with size in [bBkKmMgGtT] units"
#define MCHelpSwVD L"\n vd Erase disk contents before creating volume"
#define MCHelpSwVER L"\n ver[n] File version control"
#define MCHelpSwVN L"\n vn Use the old style volume naming scheme"
#define MCHelpSwVP L"\n vp Pause before each volume"
#define MCHelpSwW L"\n w<path> Assign work directory"
#define MCHelpSwX L"\n x<file> Exclude specified file"
@@ -161,7 +171,7 @@
#define MErrRename L"\nCannot rename %s to %s"
#define MAbsNextVol L"\nCannot find volume %s"
#define MBreak L"\nUser break\n"
#define MAskCreatVol L"\nCreate next volume ?"
#define MAskCreatVol L"\nCreate next volume?"
#define MAskNextDisk L"\nDisk full. Insert next"
#define MCreatVol L"\n\nCreating %sarchive %s\n"
#define MAskNextVol L"\nInsert disk with %s"
@@ -206,9 +216,9 @@
#define MAddAnalyze L"\nAnalyzing archived files: "
#define MRepacking L"\nRepacking archived files: "
#define MCRCFailed L"\n%-20s - checksum error"
#define MExtrTest L"\n\nTesting archive %s\n"
#define MExtracting L"\n\nExtracting from %s\n"
#define MUseCurPsw L"\n%s - use current password ?"
#define MExtrTest L"\nTesting archive %s\n"
#define MExtracting L"\nExtracting from %s\n"
#define MUseCurPsw L"\n%s - use current password?"
#define MCreatDir L"\nCreating %-56s"
#define MExtrSkipFile L"\nSkipping %-56s"
#define MExtrTestFile L"\nTesting %-56s"
@@ -221,18 +231,17 @@
#define MExtrAllOk L"\nAll OK"
#define MExtrTotalErr L"\nTotal errors: %ld"
#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n"
#define MAskOverwrite L"\nOverwrite %s ?"
#define MAskOverwrite L"\nOverwrite %s?"
#define MAskNewName L"\nEnter new name: "
#define MHeaderBroken L"\nCorrupt header is found"
#define MMainHeaderBroken L"\nMain archive header is corrupt"
#define MLogFileHead L"\n%s - the file header is corrupt"
#define MLogProtectHead L"The data recovery header is corrupt"
#define MArcComment L"\nArchive comment"
#define MReadStdinCmt L"\nReading comment from stdin\n"
#define MReadCommFrom L"\nReading comment from %s"
#define MDelComment L"\nDeleting comment from %s"
#define MAddComment L"\nAdding comment to %s"
#define MFCommAdd L"\nAdding file comments"
#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n"
#define MDelComment L"\nDeleting a comment from %s"
#define MAddComment L"\nAdding a comment to %s"
#define MLogCommBrk L"\nThe archive comment is corrupt"
#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:"
#define MWriteCommTo L"\nWrite comment to %s"
@@ -252,8 +261,8 @@
#define MListLock L"lock"
#define MListEnc L"encrypted"
#define MListEncHead L"encrypted headers"
#define MListTitleL L" Attributes Size Date Time Name"
#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name"
#define MListTitleL L" Attributes Size Date Time Name"
#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name"
#define MListName L"Name"
#define MListType L"Type"
#define MListFile L"File"
@@ -271,17 +280,18 @@
#define MListMtime L"mtime"
#define MListCtime L"ctime"
#define MListAtime L"atime"
#define MListModified L"Modified"
#define MListCreated L"Created"
#define MListAccessed L"Accessed"
#define MListAttr L"Attributes"
#define MListFlags L"Flags"
#define MListCompInfo L"Compression"
#define MListHostOS L"Host OS"
#define MListFileVer L"File version"
#define MListService L"Service"
#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s"
#define MListNTACLHead L"\n NTFS security data"
#define MListStrmHead L"\n NTFS stream: %s"
#define MListUnkHead L"\n Unknown subheader type: 0x%04x"
#define MFileComment L"\nComment: "
#define MYes L"Yes"
#define MNo L"No"
#define MListNoFiles L" 0 files\n"
@@ -289,10 +299,10 @@
#define MRprBuild L"\nBuilding %s"
#define MRprOldFormat L"\nCannot repair archive with old format"
#define MRprFind L"\nFound %s"
#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?"
#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid?"
#define MRprNoFiles L"\nNo files found"
#define MLogUnexpEOF L"\nUnexpected end of archive"
#define MRepAskReconst L"\nReconstruct archive structure ?"
#define MRepAskReconst L"\nReconstruct archive structure?"
#define MRRSearch L"\nSearching for recovery record"
#define MAnalyzeFileData L"\nAnalyzing file data"
#define MRecRNotFound L"\nData recovery record not found"
@@ -300,7 +310,7 @@
#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged"
#define MRecCorrected L" - data recovered"
#define MRecFailed L" - cannot recover data"
#define MAddRecRec L"\nAdding data recovery record"
#define MAddRecRec L"\nAdding the data recovery record"
#define MEraseForVolume L"\n\nErasing contents of drive %c:\n"
#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n"
#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n"
@@ -309,7 +319,7 @@
#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n"
#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s"
#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists"
#define MAskRetryCreate L"\nCannot create %s. Retry ?"
#define MAskRetryCreate L"\nCannot create %s. Retry?"
#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s"
#define MFileRO L"\n%s is read-only"
#define MACLGetError L"\nWARNING: Cannot get %s security data\n"
@@ -319,14 +329,14 @@
#define MStreamBroken L"\nERROR: %s stream data are corrupt\n"
#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n"
#define MInvalidName L"\nERROR: Invalid file name %s"
#define MProcessArc L"\n\nProcessing archive %s"
#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file name"
#define MProcessArc L"\nProcessing archive %s"
#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name"
#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s"
#define MUnknownOption L"\nERROR: Unknown option: %s"
#define MSwSyntaxError L"\nERROR: '-' is expected in the beginning of: %s"
#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored"
#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored"
#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block"
#define MSubHeadType L"\nData header type: %s"
#define MScanError L"\nCannot read contents of %s"
#define MNotVolume L"\n%s is not volume"
#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets"
@@ -351,7 +361,7 @@
#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d"
#define MVolumeNumber L"volume %d"
#define MCannotDelete L"\nCannot delete %s"
#define MRecycleFailed L"\nCannot move some files and folders to Recycle Bin"
#define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin"
#define MCalcCRC L"\nCalculating the checksum"
#define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB."
#define MCalcCRCAllVol L"\nCalculating checksums of all volumes."
@@ -379,4 +389,28 @@
#define MNeedAdmin L"\nYou may need to run RAR as administrator"
#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB."
#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary."
#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file."
#define MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u GB compression dictionary."
#define MSuggest64bit L"\n64-bit RAR version is necessary."
#define MOpenErrAtime L"\nYou may need to remove -tsp switch or run RAR as administrator to open this file."
#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort."
#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n"
#define MErrReadCount L"\n%u files are archived incompletely because of read errors."
#define MDirNameExists L"\nDirectory with such name already exists"
#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin"
#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated."
#define MAdjustValue L"\nAdjusting %s value to %s."
#define MOpFailed L"\nOperation failed"
#define MSkipEncArc L"\nSkipping the encrypted archive %s"
#define MOrigName L"Original name"
#define MOriginalTime L"Original time"
#define MFileRenamed L"\n%s is renamed to %s"
#define MDictNotAllowed L"\n%u GB dictionary exceeds %u GB limit and needs more than %u GB memory to unpack."
#define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway."
#define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data."
#define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary."
#define MSkipUnsafeLink L"\nSkipping the potentially unsafe %s -> %s link. For archives from a trustworthy source use -ola to extract it anyway."
#define MTruncService L"\nTruncated at the service block: %s"
#define MHeaderQO L"quick open information"
#define MHeaderRR L"recovery record"
#define MLockInMemoryNeeded L"-mlp switch requires ""Lock pages in memory"" privilege. Do you wish to assign it to the current user account?"
#define MPrivilegeAssigned L"User privilege has been successfully assigned and will be activated after Windows restart. Restart now?"

View File

@@ -1,13 +1,14 @@
#include "rar.hpp"
static wchar LogName[NM];
static RAR_CHARSET LogCharset=RCH_DEFAULT;
void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet)
void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet)
{
}
void CloseLogOptions()
{
wcsncpyz(LogName,LogFileName,ASIZE(LogName));
LogCharset=CSet;
}
@@ -19,17 +20,15 @@ void Log(const wchar *ArcName,const wchar *fmt,...)
uiAlarm(UIALARM_ERROR);
// This buffer is for format string only, not for entire output,
// so it can be short enough.
wchar fmtw[1024];
PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
safebuf wchar Msg[2*NM+1024];
va_list arglist;
va_start(arglist,fmt);
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
std::wstring s=vwstrprintf(fmt,arglist);
ReplaceEsc(s);
va_end(arglist);
eprintf(L"%ls",Msg);
eprintf(L"%ls",s.c_str());
ErrHandler.SetSystemErrorCode(Code);
}
#endif

View File

@@ -1,7 +1,8 @@
#ifndef _RAR_LOG_
#define _RAR_LOG_
void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet);
void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet);
void CloseLogOptions();
#ifdef SILENT
inline void Log(const wchar *ArcName,const wchar *fmt,...) {}

View File

@@ -2,8 +2,11 @@
# Makefile for UNIX - unrar
# Linux using GCC
# 2024.08.19: -march=native isn't recognized on some platforms such as RISCV64.
# Thus we removed it. Clang ARM users can add -march=armv8-a+crypto to enable
# ARM NEON crypto.
CXX=c++
CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
CXXFLAGS=-O2 -std=c++11 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
LIBFLAGS=-fPIC
DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
STRIP=strip
@@ -11,107 +14,6 @@ AR=ar
LDFLAGS=-pthread
DESTDIR=/usr
# Linux using LCC
#CXX=lcc
#CXXFLAGS=-O2
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# CYGWIN using GCC
#CXX=c++
#CXXFLAGS=-O2
#LIBFLAGS=
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
#STRIP=strip
#AR=ar
#LDFLAGS=-pthread
#DESTDIR=/usr
# HP UX using aCC
#CXX=aCC
#CXXFLAGS=-AA +O2 +Onolimit
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# IRIX using GCC
#CXX=g++
#CXXFLAGS=-O2
#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# IRIX using MIPSPro (experimental)
#CXX=CC
#CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std
#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# AIX using xlC (IBM VisualAge C++ 5.0)
#CXX=xlC
#CXXFLAGS=-O -qinline -qro -qroconst -qmaxmem=16384 -qcpluscmt
#DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API
#LIBS=-lbsd
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# Solaris using CC
#CXX=CC
#CXXFLAGS=-fast -erroff=wvarhidemem
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
#AR=ar
#DESTDIR=/usr
# Solaris using GCC (optimized for UltraSPARC 1 CPU)
#CXX=g++
#CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=/usr/ccs/bin/strip
#AR=/usr/ccs/bin/ar
#DESTDIR=/usr
# Tru64 5.1B using GCC3
#CXX=g++
#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500
#STRIP=strip
#AR=ar
#LDFLAGS=-rpath /usr/local/gcc/lib
#DESTDIR=/usr
# Tru64 5.1B using DEC C++
#CXX=cxx
#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long
#STRIP=strip
#AR=ar
#LDFLAGS=
#DESTDIR=/usr
# QNX 6.x using GCC
#CXX=g++
#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions
#STRIP=strip
#AR=ar
#LDFLAGS=-fexceptions
#DESTDIR=/usr
# Cross-compile
# Linux using arm-linux-g++
#CXX=arm-linux-g++
#CXXFLAGS=-O2
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=arm-linux-strip
#AR=arm-linux-ar
#LDFLAGS=-static
#DESTDIR=/usr
##########################
COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES)
@@ -123,10 +25,10 @@ UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o
LIB_OBJ=filestr.o scantree.o dll.o qopen.o
OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \
archive.o arcread.o unicode.o system.o isnt.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 \
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 largepage.o
.cpp.o:
$(COMPILE) -D$(WHAT) -c $<
@@ -142,20 +44,23 @@ clean:
@rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ)
@rm -f unrar libunrar.*
unrar: clean $(OBJECTS) $(UNRAR_OBJ)
# We removed 'clean' from dependencies, because it prevented parallel
# 'make -Jn' builds.
unrar: $(OBJECTS) $(UNRAR_OBJ)
@rm -f unrar
$(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
$(STRIP) unrar
sfx: WHAT=SFX_MODULE
sfx: clean $(OBJECTS)
sfx: $(OBJECTS)
@rm -f default.sfx
$(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS)
$(STRIP) default.sfx
lib: WHAT=RARDLL
lib: CXXFLAGS+=$(LIBFLAGS)
lib: clean $(OBJECTS) $(LIB_OBJ)
lib: $(OBJECTS) $(LIB_OBJ)
@rm -f libunrar.*
$(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
$(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)

View File

@@ -3,6 +3,7 @@
static bool match(const wchar *pattern,const wchar *string,bool ForceCase);
static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase);
static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase);
static bool IsWildcard(const wchar *Str,size_t CheckSize);
inline uint touppercw(uint ch,bool ForceCase)
{
@@ -16,12 +17,15 @@ inline uint touppercw(uint ch,bool ForceCase)
}
bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode)
{
bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0;
CmpMode&=MATCH_MODEMASK;
wchar *Name1=PointToName(Wildcard);
wchar *Name2=PointToName(Name);
if (CmpMode!=MATCH_NAMES)
{
size_t WildLength=wcslen(Wildcard);
@@ -32,47 +36,48 @@ bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
// "path1" mask must match "path1\path2\filename.ext" and "path1" names.
wchar NextCh=Name[WildLength];
if (NextCh==L'\\' || NextCh==L'/' || NextCh==0)
return(true);
return true;
}
// Nothing more to compare for MATCH_SUBPATHONLY.
if (CmpMode==MATCH_SUBPATHONLY)
return(false);
return false;
// 2023.08.29: We tried std::wstring Path1 and Path2 here, but performance
// impact for O(n^2) complexity loop in CmdExtract::AnalyzeArchive()
// was rather noticeable, 1.7s instead of 0.9s when extracting ~300 files
// with @listfile from archive with ~7000 files.
// This function can be invoked from other O(n^2) loops. So for now
// we prefer to avoid wstring and use pointers and path sizes here.
// Another option could be using std::wstring_view.
wchar Path1[NM],Path2[NM];
GetFilePath(Wildcard,Path1,ASIZE(Path1));
GetFilePath(Name,Path2,ASIZE(Path2));
size_t Path1Size=Name1-Wildcard;
size_t Path2Size=Name2-Name;
if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
mwcsicompc(Path1,Path2,ForceCase)!=0)
return(false);
if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
(Path1Size!=Path2Size ||
mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0))
return false;
if (CmpMode==MATCH_ALLWILD)
return match(Wildcard,Name,ForceCase);
if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH)
if (IsWildcard(Path1))
return(match(Wildcard,Name,ForceCase));
if (IsWildcard(Wildcard,Path1Size))
return match(Wildcard,Name,ForceCase);
else
if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard))
{
if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0)
return(false);
if (Path1Size>0 && mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)
return false;
}
else
if (mwcsicompc(Path1,Path2,ForceCase)!=0)
return(false);
if (Path1Size!=Path2Size || mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)
return false;
}
wchar *Name1=PointToName(Wildcard);
wchar *Name2=PointToName(Name);
// Always return false for RAR temporary files to exclude them
// from archiving operations.
// if (mwcsnicompc(L"__rar_",Name2,6,false)==0)
// return(false);
if (CmpMode==MATCH_EXACT)
return(mwcsicompc(Name1,Name2,ForceCase)==0);
return mwcsicompc(Name1,Name2,ForceCase)==0;
return(match(Name1,Name2,ForceCase));
return match(Name1,Name2,ForceCase);
}
@@ -85,18 +90,18 @@ bool match(const wchar *pattern,const wchar *string,bool ForceCase)
switch (patternc)
{
case 0:
return(stringc==0);
return stringc==0;
case '?':
if (stringc == 0)
return(false);
return false;
break;
case '*':
if (*pattern==0)
return(true);
return true;
if (*pattern=='.')
{
if (pattern[1]=='*' && pattern[2]==0)
return(true);
return true;
const wchar *dot=wcschr(string,'.');
if (pattern[1]==0)
return (dot==NULL || dot[1]==0);
@@ -104,22 +109,22 @@ bool match(const wchar *pattern,const wchar *string,bool ForceCase)
{
string=dot;
if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL)
return(mwcsicompc(pattern+1,string+1,ForceCase)==0);
return mwcsicompc(pattern+1,string+1,ForceCase)==0;
}
}
while (*string)
if (match(pattern,string++,ForceCase))
return(true);
return(false);
return true;
return false;
default:
if (patternc != stringc)
{
// Allow "name." mask match "name" and "name.\" match "name\".
if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.'))
return(match(pattern,string,ForceCase));
return match(pattern,string,ForceCase);
else
return(false);
return false;
}
break;
}
@@ -145,3 +150,18 @@ int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase)
return wcsnicomp(Str1,Str2,N);
#endif
}
bool IsWildcard(const wchar *Str,size_t CheckSize)
{
size_t CheckPos=0;
#ifdef _WIN_ALL
// Not treat the special NTFS \\?\d: path prefix as a wildcard.
if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\')
CheckPos+=4;
#endif
for (size_t I=CheckPos;I<CheckSize && Str[I]!=0;I++)
if (Str[I]=='*' || Str[I]=='?')
return true;
return false;
}

View File

@@ -33,6 +33,11 @@ enum {
#define MATCH_MODEMASK 0x0000ffff
#define MATCH_FORCECASESENSITIVE 0x80000000
bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode);
bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode);
inline bool CmpName(const std::wstring &Wildcard,const std::wstring &Name,uint CmpMode)
{
return CmpName(Wildcard.c_str(),Name.c_str(),CmpMode);
}
#endif

View File

@@ -339,13 +339,13 @@ inline void ModelPPM::UpdateModel()
else
{
cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf);
pc->U.SummFreq += cf;
pc->U.SummFreq += (ushort)cf;
}
p=pc->U.Stats+ns1;
p->Successor=Successor;
p->Symbol = fs.Symbol;
p->Freq = cf;
pc->NumStats=++ns1;
p->Freq = (byte)cf;
pc->NumStats=(ushort)++ns1;
}
MaxContext=MinContext=fs.Successor;
return;
@@ -532,15 +532,17 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
pps--;
// 2022.12.02: we removed pps-- here and changed the code below to avoid
// "array subscript -1 is outside array bounds" warning in some compilers.
do
{
pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount;
pps++;
} while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale;
psee2c->Summ += (ushort)Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
}
return true;

View File

@@ -25,7 +25,7 @@ struct RARPPM_SEE2_CONTEXT : RARPPM_DEF
}
uint getMean()
{
uint RetVal=GET_SHORT16(Summ) >> Shift;
short RetVal=GET_SHORT16(Summ) >> Shift;
Summ -= RetVal;
return RetVal+(RetVal == 0);
}

134
unrar/motw.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include "rar.hpp"
/* Zone.Identifier stream can include the text like:
[ZoneTransfer]
ZoneId=3
HostUrl=https://site/path/file.ext
ReferrerUrl=d:\path\archive.ext
Where ZoneId can be:
0 = My Computer
1 = Local intranet
2 = Trusted sites
3 = Internet
4 = Restricted sites
*/
MarkOfTheWeb::MarkOfTheWeb()
{
ZoneIdValue=-1; // -1 indicates the missing MOTW.
AllFields=false;
}
void MarkOfTheWeb::Clear()
{
ZoneIdValue=-1;
}
void MarkOfTheWeb::ReadZoneIdStream(const std::wstring &FileName,bool AllFields)
{
MarkOfTheWeb::AllFields=AllFields;
ZoneIdValue=-1;
ZoneIdStream.clear();
std::wstring StreamName=FileName+MOTW_STREAM_NAME;
File SrcFile;
if (SrcFile.Open(StreamName))
{
ZoneIdStream.resize(MOTW_STREAM_MAX_SIZE);
int BufSize=SrcFile.Read(&ZoneIdStream[0],ZoneIdStream.size());
ZoneIdStream.resize(BufSize<0 ? 0:BufSize);
if (BufSize<=0)
return;
ZoneIdValue=ParseZoneIdStream(ZoneIdStream);
}
}
// 'Stream' contains the raw "Zone.Identifier" NTFS stream data on input
// and either raw or cleaned stream data on output.
int MarkOfTheWeb::ParseZoneIdStream(std::string &Stream)
{
if (!starts_with(Stream,"[ZoneTransfer]"))
return -1; // Not a valid Mark of the Web. Prefer the archive MOTW if any.
std::string::size_type ZoneId=Stream.find("ZoneId=",0);
if (ZoneId==std::string::npos || !IsDigit(Stream[ZoneId+7]))
return -1; // Not a valid Mark of the Web.
int ZoneIdValue=atoi(&Stream[ZoneId+7]);
if (ZoneIdValue<0 || ZoneIdValue>4)
return -1; // Not a valid Mark of the Web.
if (!AllFields)
Stream="[ZoneTransfer]\r\nZoneId=" + std::to_string(ZoneIdValue) + "\r\n";
return ZoneIdValue;
}
void MarkOfTheWeb::CreateZoneIdStream(const std::wstring &Name,StringList &MotwList)
{
if (ZoneIdValue==-1)
return;
size_t ExtPos=GetExtPos(Name);
const wchar *Ext=ExtPos==std::wstring::npos ? L"":&Name[ExtPos+1];
bool Matched=false;
const wchar *CurMask;
MotwList.Rewind();
while ((CurMask=MotwList.GetString())!=nullptr)
{
// Perform the fast extension comparison for simple *.ext masks.
// Also we added the fast path to wcsicomp for English only strings.
// When extracting 100000 files with "Exe and office" masks set
// this loop spent 85ms with this optimization and wcsicomp optimized
// for English strings, 415ms with this optimization only, 475ms with
// wcsicomp optimized only and 795ms without both optimizations.
bool FastCmp=CurMask[0]=='*' && CurMask[1]=='.' && wcspbrk(CurMask+2,L"*?")==NULL;
if (FastCmp && wcsicomp(Ext,CurMask+2)==0 || !FastCmp && CmpName(CurMask,Name,MATCH_NAMES))
{
Matched=true;
break;
}
}
if (!Matched)
return;
std::wstring StreamName=Name+MOTW_STREAM_NAME;
File StreamFile;
if (StreamFile.Create(StreamName)) // Can fail on FAT.
{
// We got a report that write to stream failed on Synology 2411+ NAS drive.
// So we handle it silently instead of aborting.
StreamFile.SetExceptions(false);
if (StreamFile.Write(&ZoneIdStream[0],ZoneIdStream.size()))
StreamFile.Close();
}
}
bool MarkOfTheWeb::IsNameConflicting(const std::wstring &StreamName)
{
// We must use the case insensitive comparison for L":Zone.Identifier"
// to catch specially crafted archived streams like L":zone.identifier".
return wcsicomp(StreamName,MOTW_STREAM_NAME)==0 && ZoneIdValue!=-1;
}
// Return true and prepare the file stream to write if its ZoneId is stricter
// than archive ZoneId. If it is missing, less or equally strict, return false.
bool MarkOfTheWeb::IsFileStreamMoreSecure(std::string &FileStream)
{
int StreamZone=ParseZoneIdStream(FileStream);
return StreamZone>ZoneIdValue;
}

26
unrar/motw.hpp Normal file
View File

@@ -0,0 +1,26 @@
#ifndef _RAR_MOTW_
#define _RAR_MOTW_
class MarkOfTheWeb
{
private:
const size_t MOTW_STREAM_MAX_SIZE=1024;
const wchar* MOTW_STREAM_NAME=L":Zone.Identifier"; // Must start from ':'.
int ParseZoneIdStream(std::string &Stream);
std::string ZoneIdStream; // Store archive ":Zone.Identifier" NTFS stream data.
int ZoneIdValue; // -1 if missing.
bool AllFields; // Copy all MOTW fields or ZoneId only.
public:
MarkOfTheWeb();
void Clear();
void ReadZoneIdStream(const std::wstring &FileName,bool AllFields);
void CreateZoneIdStream(const std::wstring &Name,StringList &MotwList);
bool IsNameConflicting(const std::wstring &StreamName);
bool IsFileStreamMoreSecure(std::string &FileStream);
};
#endif

View File

@@ -6,18 +6,11 @@ RAROptions::RAROptions()
}
RAROptions::~RAROptions()
{
// It is important for security reasons, so we do not have the unnecessary
// password data left in memory.
memset(this,0,sizeof(RAROptions));
}
void RAROptions::Init()
{
memset(this,0,sizeof(RAROptions));
WinSize=0x2000000;
WinSizeLimit=0x100000000;
Overwrite=OVERWRITE_DEFAULT;
Method=3;
MsgStream=MSG_STDOUT;

View File

@@ -1,7 +1,7 @@
#ifndef _RAR_OPTIONS_
#define _RAR_OPTIONS_
#define DEFAULT_RECOVERY -3
#define DEFAULT_RECOVERY 3
#define DEFAULT_RECVOLUMES -10
@@ -15,10 +15,18 @@ enum PATH_EXCL_MODE {
EXCL_ABSPATH // -ep3 (the full path with the disk letter)
};
enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4,
SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16};
enum {
SOLID_NONE=0, // Non-solid mode.
SOLID_NORMAL=1, // Standard solid mode.
SOLID_COUNT=2, // Reset the solid data after specified file count.
SOLID_FILEEXT=4, // Reset the solid data for every new file extension.
SOLID_VOLUME_DEPENDENT=8, // Preserve solid data in all volumes.
SOLID_VOLUME_INDEPENDENT=16, // Reset solid data in all volumes.
SOLID_RESET=32, // Reset solid data for newly added files.
SOLID_BLOCK_SIZE=64 // Reset solid data after the specified size.
};
enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST};
enum ARCTIME_MODE {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST,ARCTIME_SPECIFIED};
enum EXTTIME_MODE {
EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX
@@ -33,7 +41,7 @@ enum RECURSE_MODE
RECURSE_NONE=0, // no recurse switches
RECURSE_DISABLE, // switch -r-
RECURSE_ALWAYS, // switch -r
RECURSE_WILDCARDS, // switch -r0
RECURSE_WILDCARDS // switch -r0
};
enum OVERWRITE_MODE
@@ -45,13 +53,25 @@ enum OVERWRITE_MODE
OVERWRITE_FORCE_ASK
};
enum ARC_METADATA
{
ARCMETA_NONE=0,
ARCMETA_SAVE, // -ams
ARCMETA_RESTORE // -amr
};
enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
enum QOPEN_MODE { QOPEN_NONE=0, QOPEN_AUTO, QOPEN_ALWAYS };
enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 };
#define MAX_FILTER_TYPES 16
enum FilterState {FILTER_DEFAULT=0,FILTER_AUTO,FILTER_FORCE,FILTER_DISABLE};
enum FilterState {
FILTER_DEFAULT=0, // No -mc<filter> switch.
FILTER_AUTO, // -mc<filter> switch is present.
FILTER_FORCE, // -mc<filter>+ switch is present.
FILTER_DISABLE // -mc<filter>- switch is present.
};
enum SAVECOPY_MODE {
@@ -61,7 +81,8 @@ enum SAVECOPY_MODE {
enum APPENDARCNAME_MODE
{
APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNDIR
APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR,
APPENDARCNAME_OWNDIR
};
enum POWER_MODE {
@@ -83,13 +104,15 @@ struct FilterMode
#define MAX_GENERATE_MASK 128
// Here we store simple data types, which we can clear and move all together
// quickly. Rest of data types goes to CommandData.
class RAROptions
{
public:
RAROptions();
~RAROptions();
void Init();
uint ExclFileAttr;
uint InclFileAttr;
@@ -99,42 +122,40 @@ class RAROptions
bool InclDir;
bool InclAttrSet;
size_t WinSize;
wchar TempPath[NM];
wchar SFXModule[NM];
uint64 WinSize;
uint64 WinSizeLimit; // Switch -mdx<size>.
#ifdef USE_QOPEN
QOPEN_MODE QOpenMode;
#endif
bool ConfigDisabled; // Switch -cfg-.
wchar ExtrPath[NM];
wchar CommentFile[NM];
RAR_CHARSET CommentCharset;
RAR_CHARSET FilelistCharset;
RAR_CHARSET ErrlogCharset;
RAR_CHARSET RedirectCharset;
wchar ArcPath[NM];
SecPassword Password;
bool EncryptHeaders;
bool SkipEncrypted;
bool ManualPassword; // Password entered manually during operation, might need to clean for next archive.
wchar LogName[NM];
MESSAGE_TYPE MsgStream;
SOUND_NOTIFY_MODE Sound;
OVERWRITE_MODE Overwrite;
int Method;
HASH_TYPE HashType;
int Recovery;
uint Recovery;
int RecVolNumber;
ARC_METADATA ArcMetadata;
bool DisablePercentage;
bool DisableCopyright;
bool DisableDone;
bool DisableNames;
bool PrintVersion;
int Solid;
int SolidCount;
uint Solid;
uint SolidCount;
uint64 SolidBlockSize;
bool ClearArc;
bool AddArcOnly;
bool DisableComment;
@@ -143,19 +164,26 @@ class RAROptions
PATH_EXCL_MODE ExclPath;
RECURSE_MODE Recurse;
int64 VolSize;
Array<int64> NextVolSizes;
uint CurVolNum;
bool AllYes;
bool MoreInfo; // -im, show more information, used only in "WinRAR t" now.
bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.
bool DisableSortSolid;
int ArcTime;
int ConvertNames;
bool ProcessOwners;
bool SaveSymLinks;
bool SaveHardLinks;
bool AbsoluteLinks;
bool SkipSymLinks;
int Priority;
int SleepTime;
bool UseLargePages;
// Quit after processing some system integration related switch,
// like enabling the large memory pages privilege.
// menu for non-admin user and quit.
bool SetupComplete;
bool KeepBroken;
bool OpenShared;
bool DeleteFiles;
@@ -173,6 +201,9 @@ class RAROptions
bool SyncFiles;
bool ProcessEA;
bool SaveStreams;
#ifdef PROPAGATE_MOTW
bool MotwAllFields;
#endif
bool SetCompressedAttr;
bool IgnoreGeneralAttr;
RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore;
@@ -185,7 +216,6 @@ class RAROptions
bool Test;
bool VolumePause;
FilterMode FilterModes[MAX_FILTER_TYPES];
wchar EmailTo[NM];
uint VersionControl;
APPENDARCNAME_MODE AppendArcNameToPath;
POWER_MODE Shutdown;
@@ -193,7 +223,6 @@ class RAROptions
EXTTIME_MODE xctime;
EXTTIME_MODE xatime;
bool PreserveAtime;
wchar CompressStdin[NM];
uint Threads; // We use it to init hash even if RAR_SMP is not defined.
@@ -202,7 +231,6 @@ class RAROptions
#ifdef RARDLL
wchar DllDestName[NM];
int DllOpMode;
int DllError;
LPARAM UserData;
@@ -210,5 +238,6 @@ class RAROptions
CHANGEVOLPROC ChangeVolProc;
PROCESSDATAPROC ProcessDataProc;
#endif
};
#endif

View File

@@ -4,24 +4,22 @@
#define FALSE 0
#define TRUE 1
#ifdef __EMX__
#define INCL_BASE
#endif
#if defined(RARDLL) && !defined(SILENT)
#define SILENT
#endif
#include <new>
#if defined(_WIN_ALL) || defined(_EMX)
#define LITTLE_ENDIAN
#define NM 2048
#include <string>
#include <vector>
#include <deque>
#include <memory> // For automatic pointers.
#include <algorithm>
#ifdef _WIN_ALL
#define LITTLE_ENDIAN
// We got a report that just "#define STRICT" is incompatible with
// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order
@@ -36,12 +34,11 @@
// re-definition warnings in third party projects.
#ifndef UNICODE
#define UNICODE
#define _UNICODE // Set _T() macro to convert from narrow to wide strings.
#endif
#undef WINVER
#undef _WIN32_WINNT
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define WINVER _WIN32_WINNT_WINXP
#define _WIN32_WINNT _WIN32_WINNT_WINXP
#if !defined(ZIPSFX)
#define RAR_SMP
@@ -55,35 +52,35 @@
#pragma comment(lib, "Shlwapi.lib")
#include <PowrProf.h>
#pragma comment(lib, "PowrProf.lib")
#include <psapi.h>
#pragma comment(lib, "Psapi.lib") // For GetProcessMemoryInfo().
#include <shellapi.h>
#include <shlobj.h>
#include <winioctl.h>
#include <wincrypt.h>
#include <wchar.h>
#include <wctype.h>
#include <Sddl.h>
#include <ntsecapi.h>
#endif // _WIN_ALL
// For WMI requests.
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
#include <sys/types.h>
#include <sys/stat.h>
#include <dos.h>
#include <direct.h>
#include <intrin.h>
#if !defined(_EMX) && !defined(_MSC_VER)
#include <dir.h>
#endif
#ifdef _MSC_VER
#if _MSC_VER<1500
#define for if (0) ; else for
#endif
#include <direct.h>
#include <intrin.h>
// Use SSE only for x86/x64, not ARM Windows.
#if defined(_M_IX86) || defined(_M_X64)
#define USE_SSE
#define SSE_ALIGNMENT 16
#else
#include <dirent.h>
#endif // _MSC_VER
#endif
#include <stdio.h>
#include <stdlib.h>
@@ -96,7 +93,6 @@
#include <time.h>
#include <signal.h>
#define SAVE_LINKS
#define ENABLE_ACCESS
@@ -106,7 +102,7 @@
#define SPATHDIVIDER L"\\"
#define CPATHDIVIDER '\\'
#define CPATHDIVIDER L'\\'
#define MASKALL L"*"
#define READBINARY "rb"
@@ -116,25 +112,13 @@
#define WRITEBINARY "wb"
#define APPENDTEXT "at"
#if defined(_WIN_ALL)
#ifdef _MSC_VER
#define _stdfunction __cdecl
#define _forceinline __forceinline
#else
#define _stdfunction _USERENTRY
#define _forceinline inline
#endif
#else
#define _stdfunction
#define _forceinline inline
#endif
#define _stdfunction __cdecl
#define _forceinline __forceinline
#endif // defined(_WIN_ALL) || defined(_EMX)
#endif // _WIN_ALL
#ifdef _UNIX
#define NM 2048
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -142,7 +126,7 @@
#if defined(__QNXNTO__)
#include <sys/param.h>
#endif
#if defined(RAR_SMP) && defined(__APPLE__)
#ifdef _APPLE
#include <sys/sysctl.h>
#endif
#ifndef SFX_MODULE
@@ -155,6 +139,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h> // Needed for ptrdiff_t in some UnRAR source builds.
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
@@ -165,6 +150,28 @@
#include <utime.h>
#include <locale.h>
#ifdef __GNUC__
#if defined(__i386__) || defined(__x86_64__)
#include <x86intrin.h>
#define USE_SSE
#define SSE_ALIGNMENT 16
#endif
#endif
#if defined(__aarch64__) && (defined(__ARM_FEATURE_CRYPTO) || defined(__ARM_FEATURE_CRC32))
#include <arm_neon.h>
#ifndef _APPLE
#include <sys/auxv.h>
#include <asm/hwcap.h>
#endif
#ifdef __ARM_FEATURE_CRYPTO
#define USE_NEON_AES
#endif
#ifdef __ARM_FEATURE_CRC32
#define USE_NEON_CRC32
#endif
#endif
#ifdef S_IFLNK
#define SAVE_LINKS
@@ -182,7 +189,7 @@
#define SPATHDIVIDER L"/"
#define CPATHDIVIDER '/'
#define CPATHDIVIDER L'/'
#define MASKALL L"*"
#define READBINARY "r"
@@ -212,25 +219,23 @@
#endif
#endif
#if _POSIX_C_SOURCE >= 200809L
#ifdef __VMS
# define LITTLE_ENDIAN
#endif
// Unlike Apple x64, utimensat shall be available in all Apple M1 systems.
#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__)
#define UNIX_TIME_NS // Nanosecond time precision in Unix.
#endif
#endif // _UNIX
#if 0
#define MSGID_INT
typedef int MSGID;
#else
typedef const wchar* MSGID;
#endif
#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required.
#define SSE_ALIGNMENT 1
#endif
#define safebuf static
// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN.
#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN)
#define LITTLE_ENDIAN
@@ -261,8 +266,8 @@
#endif
#endif
#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__)
// Allow not aligned integer access, increases speed in some operations.
#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
// Allow unaligned integer access, increases speed in some operations.
#define ALLOW_MISALIGNED
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -2,40 +2,52 @@
#define _RAR_PATHFN_
wchar* PointToName(const wchar *Path);
std::wstring PointToName(const std::wstring &Path);
size_t GetNamePos(const std::wstring &Path);
wchar* PointToLastChar(const wchar *Path);
wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize);
void SetName(wchar *FullName,const wchar *Name,size_t MaxSize);
void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize);
void SetSFXExt(wchar *SFXName,size_t MaxSize);
wchar GetLastChar(const std::wstring &Path);
size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath);
void SetName(std::wstring &FullName,const std::wstring &Name);
void SetExt(std::wstring &Name,std::wstring NewExt);
void RemoveExt(std::wstring &Name);
void SetSFXExt(std::wstring &SFXName);
wchar *GetExt(const wchar *Name);
bool CmpExt(const wchar *Name,const wchar *Ext);
bool IsWildcard(const wchar *Str);
std::wstring GetExt(const std::wstring &Name);
std::wstring::size_type GetExtPos(const std::wstring &Name);
bool CmpExt(const std::wstring &Name,const std::wstring &Ext);
bool IsWildcard(const std::wstring &Str);
bool IsPathDiv(int Ch);
bool IsDriveDiv(int Ch);
bool IsDriveLetter(const wchar *Path);
int GetPathDisk(const wchar *Path);
void AddEndSlash(wchar *Path,size_t MaxLength);
void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize);
void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength);
void RemoveNameFromPath(wchar *Path);
bool IsDriveLetter(const std::wstring &Path);
int GetPathDisk(const std::wstring &Path);
void AddEndSlash(std::wstring &Path);
void MakeName(const std::wstring &Path,const std::wstring &Name,std::wstring &Pathname);
void GetPathWithSep(const std::wstring &FullName,std::wstring &Path);
void RemoveNameFromPath(std::wstring &Path);
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create);
void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create);
bool GetAppDataPath(std::wstring &Path,bool Create);
void GetRarDataPath(std::wstring &Path,bool Create);
#endif
#ifdef _WIN_ALL
bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path);
#endif
#ifndef SFX_MODULE
bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create);
void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create);
bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create);
void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create);
#endif
wchar* GetVolNumPart(const wchar *ArcName);
void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering);
bool IsNameUsable(const wchar *Name);
void MakeNameUsable(char *Name,bool Extended);
void MakeNameUsable(wchar *Name,bool Extended);
size_t GetVolNumPos(const std::wstring &ArcName);
void NextVolumeName(std::wstring &ArcName,bool OldNumbering);
bool IsNameUsable(const std::wstring &Name);
void MakeNameUsable(std::wstring &Name,bool Extended);
void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);
void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength);
void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength);
void UnixSlashToDos(const std::string &SrcName,std::string &DestName);
void UnixSlashToDos(const std::wstring &SrcName,std::wstring &DestName);
void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength);
void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength);
void DosSlashToUnix(const std::string &SrcName,std::string &DestName);
void DosSlashToUnix(const std::wstring &SrcName,std::wstring &DestName);
inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength)
{
@@ -46,6 +58,15 @@ inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength)
#endif
}
inline void SlashToNative(const std::string &SrcName,std::string &DestName)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName);
#else
DosSlashToUnix(SrcName,DestName);
#endif
}
inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength)
{
#ifdef _WIN_ALL
@@ -55,22 +76,45 @@ inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength)
#endif
}
void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize);
bool IsFullPath(const wchar *Path);
bool IsFullRootPath(const wchar *Path);
void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize);
int ParseVersionFileName(wchar *Name,bool Truncate);
wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering);
wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize);
inline void SlashToNative(const std::wstring &SrcName,std::wstring &DestName)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName);
#else
DosSlashToUnix(SrcName,DestName);
#endif
}
void ConvertNameToFull(const std::wstring &Src,std::wstring &Dest);
bool IsFullPath(const std::wstring &Path);
bool IsFullRootPath(const std::wstring &Path);
void GetPathRoot(const std::wstring &Path,std::wstring &Root);
int ParseVersionFileName(std::wstring &Name,bool Truncate);
size_t VolNameToFirstName(const std::wstring &VolName,std::wstring &FirstName,bool NewNumbering);
#ifndef SFX_MODULE
void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving);
void GenerateArchiveName(std::wstring &ArcName,const std::wstring &GenerateMask,bool Archiving);
#endif
#ifdef _WIN_ALL
bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize);
void ConvertToPrecomposed(wchar *Name,size_t NameSize);
void MakeNameCompatible(wchar *Name);
bool GetWinLongPath(const std::wstring &Src,std::wstring &Dest);
void ConvertToPrecomposed(std::wstring &Name);
void MakeNameCompatible(std::wstring &Name);
#endif
#ifdef _WIN_ALL
std::wstring GetModuleFileStr();
std::wstring GetProgramFile(const std::wstring &Name);
#endif
#if defined(_WIN_ALL)
bool SetCurDir(const std::wstring &Dir);
#endif
#ifdef _WIN_ALL
bool GetCurDir(std::wstring &Dir);
#endif
#endif

View File

@@ -97,7 +97,7 @@ void QuickOpen::Load(uint64 BlockPos)
if (Arc->SubHead.Encrypted)
{
RAROptions *Cmd=Arc->GetRAROptions();
CommandData *Cmd=Arc->GetCommandData();
#ifndef RAR_NOCRYPT
if (Cmd->Password.IsSet())
Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
@@ -114,7 +114,7 @@ void QuickOpen::Load(uint64 BlockPos)
RawDataPos=0;
ReadBufSize=0;
ReadBufPos=0;
LastReadHeader.Reset();
LastReadHeader.clear();
LastReadHeaderPos=0;
ReadBuffer();
@@ -126,7 +126,7 @@ bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
if (!Loaded)
return false;
// Find next suitable cached block.
while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
while (LastReadHeaderPos+LastReadHeader.size()<=SeekPos)
if (!ReadNext())
break;
if (!Loaded)
@@ -138,9 +138,9 @@ bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
return false;
}
if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.size())
{
memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
memcpy(Data,&LastReadHeader[size_t(SeekPos-LastReadHeaderPos)],Size);
Result=Size;
SeekPos+=Size;
UnsyncSeekPos=true;
@@ -292,8 +292,8 @@ bool QuickOpen::ReadNext()
size_t HeaderSize=(size_t)Raw.GetV();
if (HeaderSize>MAX_HEADER_SIZE_RAR5)
return false;
LastReadHeader.Alloc(HeaderSize);
Raw.GetB(&LastReadHeader[0],HeaderSize);
LastReadHeader.resize(HeaderSize);
Raw.GetB(LastReadHeader.data(),HeaderSize);
// Calculate the absolute position as offset from quick open service header.
LastReadHeaderPos=QOHeaderPos-Offset;
return true;

View File

@@ -43,7 +43,7 @@ class QuickOpen
uint64 RawDataPos; // Current read position in QO data.
size_t ReadBufSize; // Size of Buf data currently read from QO.
size_t ReadBufPos; // Current read position in Buf data.
Array<byte> LastReadHeader;
std::vector<byte> LastReadHeader;
uint64 LastReadHeaderPos;
uint64 SeekPos;
bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer.

View File

@@ -12,11 +12,11 @@ int main(int argc, char *argv[])
ErrHandler.SetSignalHandlers(true);
#ifdef SFX_MODULE
wchar ModuleName[NM];
std::wstring ModuleName;
#ifdef _WIN_ALL
GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName));
ModuleName=GetModuleFileStr();
#else
CharToWide(argv[0],ModuleName,ASIZE(ModuleName));
CharToWide(argv[0],ModuleName);
#endif
#endif
@@ -35,9 +35,10 @@ int main(int argc, char *argv[])
try
{
CommandData *Cmd=new CommandData;
// Use std::unique_ptr to free Cmd in case of exception.
std::unique_ptr<CommandData> Cmd(new CommandData);
#ifdef SFX_MODULE
wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command));
Cmd->Command=L"X";
char *Switch=argc>1 ? argv[1]:NULL;
if (Switch!=NULL && Cmd->IsSwitch(Switch[0]))
{
@@ -46,7 +47,10 @@ int main(int argc, char *argv[])
{
case 'T':
case 'V':
Cmd->Command[0]=UpperCmd;
// Also copy 't' and 'a' modifiers for -v[t,a], if present.
Cmd->Command.clear();
for (char *c=Switch+1;*c!=0;c++)
Cmd->Command+=etoupper(*c);
break;
case '?':
Cmd->OutHelp(RARX_SUCCESS);
@@ -68,7 +72,7 @@ int main(int argc, char *argv[])
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
ShutdownOnClose=Cmd->Shutdown;
if (ShutdownOnClose)
if (ShutdownOnClose!=POWERMODE_KEEP)
ShutdownCheckAnother(true);
#endif
@@ -78,7 +82,6 @@ int main(int argc, char *argv[])
Cmd->OutTitle();
Cmd->ProcessCommand();
delete Cmd;
}
catch (RAR_EXIT ErrCode)
{
@@ -100,6 +103,7 @@ int main(int argc, char *argv[])
Shutdown(ShutdownOnClose);
#endif
ErrHandler.MainExit=true;
CloseLogOptions();
return ErrHandler.GetErrorCode();
}
#endif

View File

@@ -12,10 +12,11 @@
#include "version.hpp"
#include "rardefs.hpp"
#include "rarlang.hpp"
#include "rawint.hpp"
#include "unicode.hpp"
#include "errhnd.hpp"
#include "secpassword.hpp"
#include "array.hpp"
#include "strlist.hpp"
#include "timefn.hpp"
#include "sha1.hpp"
#include "sha256.hpp"
@@ -28,13 +29,14 @@
#include "headers.hpp"
#include "pathfn.hpp"
#include "strfn.hpp"
#include "strlist.hpp"
#ifdef _WIN_ALL
#include "isnt.hpp"
#endif
#ifdef PROPAGATE_MOTW
#include "motw.hpp"
#endif
#include "file.hpp"
#include "crc.hpp"
#include "ui.hpp"
#include "filefn.hpp"
#include "filestr.hpp"
#include "find.hpp"
@@ -47,11 +49,11 @@
#include "archive.hpp"
#include "match.hpp"
#include "cmddata.hpp"
#include "ui.hpp"
#include "filcreat.hpp"
#include "consio.hpp"
#include "system.hpp"
#include "log.hpp"
#include "rawint.hpp"
#include "rawread.hpp"
#include "encname.hpp"
#include "resource.hpp"
@@ -62,6 +64,8 @@
#include "threadpool.hpp"
#include "largepage.hpp"
#include "unpack.hpp"
@@ -85,9 +89,6 @@
#include "global.hpp"
#if 0
#include "benchmark.hpp"
#endif

View File

@@ -9,17 +9,36 @@
#define ASIZE(x) (sizeof(x)/sizeof(x[0]))
// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16)
// for CryptProtectMemory in SecPassword.
#define MAXPASSWORD 128
// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of
// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword.
// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords
// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR
// to 128 for compatibility and because it is enough for AES-256.
#define MAXPASSWORD 512
#define MAXPASSWORD_RAR 128
#define MAXSFXSIZE 0x200000
// Set some arbitrary sensible limit to maximum path length to prevent
// the excessive memory allocation for dynamically allocated strings.
#define MAXPATHSIZE 0x10000
#define MAXSFXSIZE 0x400000
#define MAXCMTSIZE 0x40000
#ifdef _WIN_32
#define DefSFXName L"default32.sfx"
#else
#define DefSFXName L"default.sfx"
#endif
#define DefSortListName L"rarfiles.lst"
// Maximum dictionary allowed by compression. Can be less than
// maximum dictionary supported by decompression.
#define PACK_MAX_DICT 0x1000000000ULL // 64 GB.
// Maximum dictionary allowed by decompression.
#define UNPACK_MAX_DICT 0x1000000000ULL // 64 GB.
#ifndef SFX_MODULE
#define USE_QOPEN
@@ -28,4 +47,8 @@
// Produce the value, which is equal or larger than 'v' and aligned to 'a'.
#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) )
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
#define PROPAGATE_MOTW // Propagate the archive Mark of the Web.
#endif
#endif

View File

@@ -1,15 +1,6 @@
#ifndef _RAR_RAROS_
#define _RAR_RAROS_
#ifdef __EMX__
#define _EMX
#endif
#ifdef __DJGPP__
#define _DJGPP
#define _EMX
#endif
#if defined(__WIN32__) || defined(_WIN32)
#define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop.
#ifdef _M_X64
@@ -29,7 +20,7 @@
#define _APPLE
#endif
#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE)
#if !defined(_WIN_ALL) && !defined(_UNIX)
#define _UNIX
#endif

View File

@@ -5,7 +5,7 @@
typedef uint8_t byte; // Unsigned 8 bits.
typedef uint16_t ushort; // Preferably 16 bits, but can be more.
typedef unsigned int uint; // 32 bits or more.
typedef unsigned int uint; // Preferably 32 bits, likely can be more.
typedef uint32_t uint32; // 32 bits exactly.
typedef int32_t int32; // Signed 32 bits exactly.
typedef uint64_t uint64; // 64 bits exactly.

View File

@@ -246,7 +246,7 @@ bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
}
else
Predicted=PrevByte;
DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++));
PrevByte=DestData[I]=(byte)(Predicted-*(SrcData++));
}
}
for (uint I=PosR,Border=DataSize-2;I<Border;I+=3)

View File

@@ -43,7 +43,7 @@ inline void RawPut2(uint Field,void *Data)
}
inline void RawPut4(uint32 Field,void *Data)
inline void RawPut4(uint Field,void *Data)
{
#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
byte *D=(byte *)Data;
@@ -52,7 +52,7 @@ inline void RawPut4(uint32 Field,void *Data)
D[2]=(byte)(Field>>16);
D[3]=(byte)(Field>>24);
#else
*(uint32 *)Data=Field;
*(uint32 *)Data=(uint32)Field;
#endif
}
@@ -84,21 +84,35 @@ inline uint32 RawGetBE4(const byte *m)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_ulong(*(uint32 *)m);
#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
return __builtin_bswap32(*(uint32 *)m);
#else
return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3];
return uint32(m[0])<<24 | uint32(m[1])<<16 | uint32(m[2])<<8 | m[3];
#endif
}
// Load 8 big endian bytes from memory and return uint64.
inline uint64 RawGetBE8(const byte *m)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_uint64(*(uint64 *)m);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
return __builtin_bswap64(*(uint64 *)m);
#else
return uint64(m[0])<<56 | uint64(m[1])<<48 | uint64(m[2])<<40 | uint64(m[3])<<32 |
uint64(m[4])<<24 | uint64(m[5])<<16 | uint64(m[6])<<8 | m[7];
#endif
}
// Save integer to memory as big endian.
inline void RawPutBE4(uint32 i,byte *mem)
inline void RawPutBE4(uint i,byte *mem)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint32*)mem = _byteswap_ulong(i);
#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
*(uint32*)mem = __builtin_bswap32(i);
*(uint32*)mem = _byteswap_ulong((uint32)i);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
*(uint32*)mem = __builtin_bswap32((uint32)i);
#else
mem[0]=byte(i>>24);
mem[1]=byte(i>>16);
@@ -108,15 +122,60 @@ inline void RawPutBE4(uint32 i,byte *mem)
}
// Save integer to memory as big endian.
inline void RawPutBE8(uint64 i,byte *mem)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint64*)mem = _byteswap_uint64(i);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
*(uint64*)mem = __builtin_bswap64(i);
#else
mem[0]=byte(i>>56);
mem[1]=byte(i>>48);
mem[2]=byte(i>>40);
mem[3]=byte(i>>32);
mem[4]=byte(i>>24);
mem[5]=byte(i>>16);
mem[6]=byte(i>>8);
mem[7]=byte(i);
#endif
}
inline uint32 ByteSwap32(uint32 i)
{
#ifdef _MSC_VER
return _byteswap_ulong(i);
#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap32(i);
#else
return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);
#endif
}
inline bool IsPow2(uint64 n) // Check if 'n' is power of 2.
{
return (n & (n-1))==0;
}
inline uint64 GetGreaterOrEqualPow2(uint64 n)
{
uint64 p=1;
while (p<n)
p*=2;
return p;
}
inline uint64 GetLessOrEqualPow2(uint64 n)
{
uint64 p=1;
while (p*2<=n)
p*=2;
return p;
}
#endif

View File

@@ -2,7 +2,7 @@
RawRead::RawRead()
{
RawRead::SrcFile=NULL;
RawRead::SrcFile=nullptr;
Reset();
}
@@ -16,7 +16,7 @@ RawRead::RawRead(File *SrcFile)
void RawRead::Reset()
{
Data.SoftReset();
Data.clear();
ReadPos=0;
DataSize=0;
Crypt=NULL;
@@ -31,7 +31,7 @@ size_t RawRead::Read(size_t Size)
{
// Full size of buffer with already read data including data read
// for encryption block alignment.
size_t FullSize=Data.Size();
size_t FullSize=Data.size();
// Data read for alignment and not processed yet.
size_t DataLeft=FullSize-DataSize;
@@ -40,7 +40,7 @@ size_t RawRead::Read(size_t Size)
{
size_t SizeToRead=Size-DataLeft;
size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK);
Data.Add(AlignedReadSize);
Data.resize(FullSize+AlignedReadSize);
ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize);
Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize);
DataSize+=ReadSize==0 ? 0:Size;
@@ -55,7 +55,7 @@ size_t RawRead::Read(size_t Size)
#endif
if (Size!=0)
{
Data.Add(Size);
Data.resize(Data.size()+Size);
ReadSize=SrcFile->Read(&Data[DataSize],Size);
DataSize+=ReadSize;
}
@@ -67,13 +67,24 @@ void RawRead::Read(byte *SrcData,size_t Size)
{
if (Size!=0)
{
Data.Add(Size);
Data.resize(Data.size()+Size);
memcpy(&Data[DataSize],SrcData,Size);
DataSize+=Size;
}
}
// Move unread data to beginning of buffer and adjust buffer size.
void RawRead::Compact()
{
if (ReadPos<DataSize) // We would access beyond the vector end otherwise.
memmove(&Data[0],&Data[ReadPos],DataSize-ReadPos);
DataSize-=ReadPos;
ReadPos=0;
Data.resize(DataSize);
}
byte RawRead::Get1()
{
return ReadPos<DataSize ? Data[ReadPos++]:0;
@@ -96,8 +107,7 @@ uint RawRead::Get4()
{
if (ReadPos+3<DataSize)
{
uint Result=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+
(Data[ReadPos+3]<<24);
uint Result=RawGet4(&Data[ReadPos]);
ReadPos+=4;
return Result;
}

View File

@@ -4,7 +4,7 @@
class RawRead
{
private:
Array<byte> Data;
std::vector<byte> Data;
File *SrcFile;
size_t DataSize;
size_t ReadPos;
@@ -15,6 +15,7 @@ class RawRead
void Reset();
size_t Read(size_t Size);
void Read(byte *SrcData,size_t Size);
void Compact();
byte Get1();
ushort Get2();
uint Get4();
@@ -27,7 +28,7 @@ class RawRead
uint GetCRC50();
byte* GetDataPtr() {return &Data[0];}
size_t Size() {return DataSize;}
size_t PaddedSize() {return Data.Size()-DataSize;}
size_t PaddedSize() {return Data.size()-DataSize;}
size_t DataLeft() {return DataSize-ReadPos;}
size_t GetPos() {return ReadPos;}
void SetPos(size_t Pos) {ReadPos=Pos;}

View File

@@ -16,6 +16,7 @@ void ComprDataIO::Init()
UnpackFromMemory=false;
UnpackToMemory=false;
UnpPackedSize=0;
UnpPackedLeft=0;
ShowProgress=true;
TestMode=false;
SkipUnpCRC=false;
@@ -35,7 +36,9 @@ void ComprDataIO::Init()
SubHead=NULL;
SubHeadPos=NULL;
CurrentCommand=0;
ProcessedArcSize=TotalArcSize=0;
ProcessedArcSize=0;
LastArcSize=0;
TotalArcSize=0;
}
@@ -75,10 +78,10 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count)
}
else
{
size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count;
size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count;
if (SizeToRead > 0)
{
if (UnpVolume && Decryption && (int64)Count>UnpPackedSize)
if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft)
{
// We need aligned blocks for decryption and we want "Keep broken
// files" to work efficiently with missing encrypted volumes.
@@ -109,7 +112,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count)
ReadAddr+=ReadSize;
Count-=ReadSize;
#endif
UnpPackedSize-=ReadSize;
UnpPackedLeft-=ReadSize;
// Do not ask for next volume if we read something from current volume.
// If next volume is missing, we need to process all data from current
@@ -118,7 +121,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count)
// we ask for next volume also if we have non-aligned encryption block.
// Since we adjust data size for decryption earlier above,
// it does not hurt "Keep broken files" mode efficiency.
if (UnpVolume && UnpPackedSize == 0 &&
if (UnpVolume && UnpPackedLeft == 0 &&
(ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) )
{
#ifndef NOVOLUME
@@ -134,7 +137,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count)
}
Archive *SrcArc=(Archive *)SrcFile;
if (SrcArc!=NULL)
ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize);
ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize);
if (ReadSize!=-1)
{
ReadSize=TotalRead;
@@ -148,18 +151,11 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count)
}
#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
// Disable the run time stack check for unrar.dll, so we can manipulate
// with ProcessDataProc call type below. Run time check would intercept
// a wrong ESP before we restore it.
#pragma runtime_checks( "s", off )
#endif
void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#ifdef RARDLL
RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions();
CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
@@ -167,28 +163,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
ErrHandler.Exit(RARX_USERBREAK);
if (Cmd->ProcessDataProc!=NULL)
{
// Here we preserve ESP value. It is necessary for those developers,
// who still define ProcessDataProc callback as "C" type function,
// even though in year 2001 we announced in unrar.dll whatsnew.txt
// that it will be PASCAL type (for compatibility with Visual Basic).
#if defined(_MSC_VER)
#ifndef _WIN_64
__asm mov ebx,esp
#endif
#elif defined(_WIN_ALL) && defined(__BORLANDC__)
_EBX=_ESP;
#endif
int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
// Restore ESP after ProcessDataProc with wrongly defined calling
// convention broken it.
#if defined(_MSC_VER)
#ifndef _WIN_64
__asm mov esp,ebx
#endif
#elif defined(_WIN_ALL) && defined(__BORLANDC__)
_ESP=_EBX;
#endif
if (RetCode==0)
ErrHandler.Exit(RARX_USERBREAK);
}
@@ -216,11 +191,6 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
Wait();
}
#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
// Restore the run time stack check for unrar.dll.
#pragma runtime_checks( "s", restore )
#endif
@@ -230,15 +200,11 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
{
if (ShowProgress && SrcFile!=NULL)
{
if (TotalArcSize!=0)
{
// important when processing several archives or multivolume archive
ArcSize=TotalArcSize;
ArcPos+=ProcessedArcSize;
}
// Important when processing several archives or multivolume archive.
ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
RAROptions *Cmd=SrcArc->GetRAROptions();
CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
@@ -280,28 +246,28 @@ void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
}
void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
// Return true if encryption or decryption mode is set correctly.
bool ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,const byte *InitV,
uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
#ifndef RAR_NOCRYPT
#ifdef RAR_NOCRYPT
return false;
#else
if (Encrypt)
{
Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Encryption;
}
else
{
Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Decryption;
}
#endif
}
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetAV15Encryption()
{
Decryption=true;
Decrypt->SetAV15Encryption();
}
#endif
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetCmt13Encryption()
{
@@ -319,3 +285,42 @@ void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
UnpackToMemoryAddr=Addr;
UnpackToMemorySize=Size;
}
// Extraction progress is based on the position in archive and we adjust
// the total archives size here, so trailing blocks do not prevent progress
// reaching 100% at the end of extraction. Alternatively we could print "100%"
// after completing the entire archive extraction, but then we would need
// to take into account possible messages like the checksum error after
// last file percent progress.
void ComprDataIO::AdjustTotalArcSize(Archive *Arc)
{
// If we know a position of QO or RR blocks, use them to adjust the total
// packed size to beginning of these blocks. Earlier we already calculated
// the total size based on entire archive sizes. We also set LastArcSize
// to start of first trailing block, to add it later to ProcessedArcSize.
uint64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0;
// QO is always preceding RR record.
// Also we check QO and RR to be less than archive length to prevent
// negative "ArcLength-LastArcSize" and possible signed integer overflow
// when calculating TotalArcSize.
if (Arc->MainHead.QOpenOffset>0 && Arc->MainHead.QOpenOffset<ArcLength)
LastArcSize=Arc->MainHead.QOpenOffset;
else
if (Arc->MainHead.RROffset>0 && Arc->MainHead.RROffset<ArcLength)
LastArcSize=Arc->MainHead.RROffset;
else
{
// If neither QO nor RR are found, exclude the approximate size of
// end of archive block.
// We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC,
// but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file
// name, so we do not have two files with 100% at the end of archive.
const uint EndBlock=23;
if (ArcLength>EndBlock)
LastArcSize=ArcLength-EndBlock;
}
TotalArcSize-=ArcLength-LastArcSize;
}

View File

@@ -1,14 +1,11 @@
#ifndef _RAR_DATAIO_
#define _RAR_DATAIO_
class Archive;
class CmdAdd;
class Unpack;
class ArcFileSearch;
#if 0
// We use external i/o calls for Benchmark command.
#define COMPRDATAIO_EXTIO
#endif
class ComprDataIO
{
@@ -29,6 +26,7 @@ class ComprDataIO
byte *UnpWrAddr;
int64 UnpPackedSize;
int64 UnpPackedLeft;
bool ShowProgress;
bool TestMode;
@@ -61,25 +59,24 @@ class ComprDataIO
void UnpWrite(byte *Addr,size_t Count);
void EnableShowProgress(bool Show) {ShowProgress=Show;}
void GetUnpackedData(byte **Data,size_t *Size);
void SetPackedSizeToRead(int64 Size) {UnpPackedSize=Size;}
void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;}
void SetTestMode(bool Mode) {TestMode=Mode;}
void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;}
void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;}
void SetFiles(File *SrcFile,File *DestFile);
void SetCommand(CmdAdd *Cmd) {Command=Cmd;}
void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;}
void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
bool SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
void SetAV15Encryption();
void SetCmt13Encryption();
void SetUnpackToMemory(byte *Addr,uint Size);
void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;}
void AdjustTotalArcSize(Archive *Arc);
bool PackVolume;
bool UnpVolume;
bool NextVolumeMissing;
int64 UnpArcSize;
int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite;
@@ -87,6 +84,9 @@ class ComprDataIO
// Used to calculate the total operation progress.
int64 ProcessedArcSize;
// Last extracted archive size up to QO or RR block.
int64 LastArcSize;
int64 TotalArcSize;
DataHash PackedDataHash; // Packed write and unpack read hash.

View File

@@ -1,11 +1,12 @@
#include "rar.hpp"
#include "recvol3.cpp"
#include "recvol5.cpp"
bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent)
bool RecVolumesRestore(CommandData *Cmd,const std::wstring &Name,bool Silent)
{
Archive Arc(Cmd);
if (!Arc.Open(Name))
@@ -42,24 +43,20 @@ 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 std::wstring &Name)
{
wchar RevName[NM];
*RevName=0;
if (Arc!=NULL)
std::wstring RevName;
if (Arc==NULL)
RevName=Name;
else
{
// We received .rar or .exe volume as a parameter, trying to find
// the matching .rev file number 1.
bool NewNumbering=Arc->NewNumbering;
wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName));
wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
wchar RecVolMask[NM];
wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
size_t BaseNamePartLength=VolNumStart-ArcName;
wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
std::wstring RecVolMask;
size_t VolNumStart=VolNameToFirstName(Name,RecVolMask,NewNumbering);
RecVolMask.replace(VolNumStart, std::wstring::npos, L"*.rev");
FindFile Find;
Find.SetMask(RecVolMask);
@@ -67,31 +64,30 @@ void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name)
while (Find.Next(&RecData))
{
wchar *Num=GetVolNumPart(RecData.Name);
if (*Num!='1') // Name must have "0...01" numeric part.
size_t NumPos=GetVolNumPos(RecData.Name);
if (RecData.Name[NumPos]!='1') // Name must have "0...01" numeric part.
continue;
bool FirstVol=true;
while (--Num>=RecData.Name && IsDigit(*Num))
if (*Num!='0')
while (NumPos>0 && IsDigit(RecData.Name[--NumPos]))
if (RecData.Name[NumPos]!='0')
{
FirstVol=false;
break;
}
if (FirstVol)
{
wcsncpyz(RevName,RecData.Name,ASIZE(RevName));
Name=RevName;
RevName=RecData.Name;
break;
}
}
if (*RevName==0) // First .rev file not found.
if (RevName.empty()) // First .rev file not found.
return;
}
File RevFile;
if (!RevFile.Open(Name))
if (!RevFile.Open(RevName))
{
ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN.
ErrHandler.OpenErrorMsg(RevName); // It also sets RARX_OPEN.
return;
}
mprintf(L"\n");
@@ -101,11 +97,11 @@ void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name)
if (Rev5)
{
RecVolumes5 RecVol(Cmd,true);
RecVol.Test(Cmd,Name);
RecVol.Test(Cmd,RevName);
}
else
{
RecVolumes3 RecVol(Cmd,true);
RecVol.Test(Cmd,Name);
RecVol.Test(Cmd,RevName);
}
}

View File

@@ -8,24 +8,24 @@ class RecVolumes3
{
private:
File *SrcFile[256];
Array<byte> Buf;
std::vector<byte> Buf;
#ifdef RAR_SMP
ThreadPool *RSThreadPool;
#endif
public:
RecVolumes3(RAROptions *Cmd,bool TestOnly);
RecVolumes3(CommandData *Cmd,bool TestOnly);
~RecVolumes3();
void Make(RAROptions *Cmd,wchar *ArcName);
bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
void Test(RAROptions *Cmd,const wchar *Name);
void Make(CommandData *Cmd,std::wstring ArcName);
bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent);
void Test(CommandData *Cmd,const std::wstring &Name);
};
struct RecVolItem
{
File *f;
wchar Name[NM];
std::wstring Name;
uint CRC;
uint64 FileSize;
bool New; // Newly created RAR volume.
@@ -48,11 +48,11 @@ struct RecRSThreadData
class RecVolumes5
{
private:
void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode);
void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode);
uint ReadHeader(File *RecFile,bool FirstRev);
Array<RecVolItem> RecItems;
std::vector<RecVolItem> RecItems;
byte *RealReadBuffer; // Real pointer returned by 'new'.
byte *ReadBuffer; // Pointer aligned for SSE instructions.
@@ -76,13 +76,13 @@ class RecVolumes5
public: // 'public' only because called from thread functions.
void ProcessAreaRS(RecRSThreadData *td);
public:
RecVolumes5(RAROptions *Cmd,bool TestOnly);
RecVolumes5(CommandData *Cmd,bool TestOnly);
~RecVolumes5();
bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
void Test(RAROptions *Cmd,const wchar *Name);
bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent);
void Test(CommandData *Cmd,const std::wstring &Name);
};
bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent);
void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name);
bool RecVolumesRestore(CommandData *Cmd,const std::wstring &Name,bool Silent);
void RecVolumesTest(CommandData *Cmd,Archive *Arc,const std::wstring &Name);
#endif

View File

@@ -23,12 +23,6 @@ class RSEncode // Encode or decode data area, one object per one thread.
#ifdef RAR_SMP
THREAD_PROC(RSEncodeThread)
{
RSEncode *rs=(RSEncode *)Data;
rs->EncodeBuf();
}
THREAD_PROC(RSDecodeThread)
{
RSEncode *rs=(RSEncode *)Data;
@@ -36,7 +30,7 @@ THREAD_PROC(RSDecodeThread)
}
#endif
RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly)
{
memset(SrcFile,0,sizeof(SrcFile));
if (TestOnly)
@@ -47,8 +41,7 @@ RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
}
else
{
Buf.Alloc(TotalBufferSize);
memset(SrcFile,0,sizeof(SrcFile));
Buf.resize(TotalBufferSize);
#ifdef RAR_SMP
RSThreadPool=new ThreadPool(Cmd->Threads);
#endif
@@ -68,30 +61,16 @@ RecVolumes3::~RecVolumes3()
void RSEncode::EncodeBuf()
{
for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
{
byte Data[256],Code[256];
for (int I=0;I<FileNumber;I++)
Data[I]=Buf[I*RecBufferSize+BufPos];
RSC.Encode(Data,FileNumber,Code);
for (int I=0;I<RecVolNumber;I++)
OutBuf[I*RecBufferSize+BufPos]=Code[I];
}
}
// Check for names like arc5_3_1.rev created by RAR 3.0.
static bool IsNewStyleRev(const wchar *Name)
static bool IsNewStyleRev(const std::wstring &Name)
{
wchar *Ext=GetExt(Name);
if (Ext==NULL)
size_t ExtPos=GetExtPos(Name);
if (ExtPos==std::wstring::npos || ExtPos==0)
return true;
int DigitGroup=0;
for (Ext--;Ext>Name;Ext--)
if (!IsDigit(*Ext))
if (*Ext=='_' && IsDigit(*(Ext-1)))
for (ExtPos--;ExtPos>0;ExtPos--)
if (!IsDigit(Name[ExtPos]))
if (Name[ExtPos]=='_' && IsDigit(Name[ExtPos-1]))
DigitGroup++;
else
break;
@@ -99,19 +78,19 @@ static bool IsNewStyleRev(const wchar *Name)
}
bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
bool RecVolumes3::Restore(CommandData *Cmd,const std::wstring &Name,bool Silent)
{
wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName));
wchar *Ext=GetExt(ArcName);
std::wstring ArcName=Name;
bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10.
bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
bool RevName=CmpExt(ArcName,L"rev");
if (RevName)
{
NewStyle=IsNewStyleRev(ArcName);
while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
Ext--;
wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName));
size_t ExtPos=GetExtPos(ArcName);
while (ExtPos>1 && (IsDigit(ArcName[ExtPos-1]) || ArcName[ExtPos-1]=='_'))
ExtPos--;
ArcName.replace(ExtPos,std::wstring::npos,L"*.*");
FindFile Find;
Find.SetMask(ArcName);
@@ -121,7 +100,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
Archive Arc(Cmd);
if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
{
wcsncpyz(ArcName,fd.Name,ASIZE(ArcName));
ArcName=fd.Name;
break;
}
}
@@ -138,11 +117,10 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
bool NewNumbering=Arc.NewNumbering;
Arc.Close();
wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
wchar RecVolMask[NM];
wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
size_t BaseNamePartLength=VolNumStart-ArcName;
wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
size_t VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering);
std::wstring RecVolMask=ArcName;
RecVolMask.replace(VolNumStart,std::wstring::npos,L"*.rev");
size_t BaseNamePartLength=VolNumStart;
int64 RecFileSize=0;
@@ -155,25 +133,25 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
Find.SetMask(RecVolMask);
FindData RecData;
int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
wchar PrevName[NM];
std::wstring PrevName;
while (Find.Next(&RecData))
{
wchar *CurName=RecData.Name;
std::wstring CurName=RecData.Name;
int P[3];
if (!RevName && !NewStyle)
{
NewStyle=true;
wchar *Dot=GetExt(CurName);
if (Dot!=NULL)
size_t DotPos=GetExtPos(CurName);
if (DotPos!=std::wstring::npos)
{
int LineCount=0;
Dot--;
while (Dot>CurName && *Dot!='.')
uint LineCount=0;
DotPos--;
while (DotPos>0 && CurName[DotPos]!='.')
{
if (*Dot=='_')
if (CurName[DotPos]=='_')
LineCount++;
Dot--;
DotPos--;
}
if (LineCount==2)
NewStyle=false;
@@ -209,24 +187,24 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
}
else
{
wchar *Dot=GetExt(CurName);
if (Dot==NULL)
size_t DotPos=GetExtPos(CurName);
if (DotPos==std::wstring::npos)
continue;
bool WrongParam=false;
for (size_t I=0;I<ASIZE(P);I++)
{
do
{
Dot--;
} while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength);
P[I]=atoiw(Dot+1);
DotPos--;
} while (IsDigit(CurName[DotPos]) && DotPos>=BaseNamePartLength);
P[I]=atoiw(&CurName[DotPos+1]);
if (P[I]==0 || P[I]>255)
WrongParam=true;
}
if (WrongParam)
continue;
}
if (P[1]+P[2]>255)
if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255)
continue;
if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
{
@@ -235,10 +213,17 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
}
RecVolNumber=P[1];
FileNumber=P[2];
wcsncpyz(PrevName,CurName,ASIZE(PrevName));
PrevName=CurName;
File *NewFile=new File;
NewFile->TOpen(CurName);
SrcFile[FileNumber+P[0]-1]=NewFile;
// This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255
// checks above. Still we keep it here for better clarity and security.
int SrcPos=FileNumber+P[0]-1;
if (SrcPos<0 || SrcPos>=ASIZE(SrcFile))
continue;
SrcFile[SrcPos]=NewFile;
FoundRecVolumes++;
if (RecFileSize==0)
@@ -249,11 +234,9 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (FoundRecVolumes==0)
return false;
bool WriteFlags[256];
memset(WriteFlags,0,sizeof(WriteFlags));
bool WriteFlags[256]{};
wchar LastVolName[NM];
*LastVolName=0;
std::wstring LastVolName;
for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
{
@@ -289,9 +272,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (!ValidVolume)
{
NewFile->Close();
wchar NewName[NM];
wcsncpyz(NewName,ArcName,ASIZE(NewName));
wcsncatz(NewName,L".bad",ASIZE(NewName));
std::wstring NewName=ArcName+L".bad";
uiMsg(UIMSG_BADARCHIVE,ArcName);
uiMsg(UIMSG_RENAMING,ArcName,NewName);
@@ -322,13 +303,13 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
MissingVolumes++;
if (CurArcNum==FileNumber-1)
wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName));
LastVolName=ArcName;
uiMsg(UIMSG_MISSINGVOL,ArcName);
uiMsg(UIEVENT_NEWARCHIVE,ArcName);
}
SrcFile[CurArcNum]=(File*)NewFile;
NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
NextVolumeName(ArcName,!NewNumbering);
}
uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
@@ -453,7 +434,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
CurFile->Close();
SrcFile[I]=NULL;
}
if (*LastVolName!=0)
if (!LastVolName.empty())
{
// Truncate the last volume to its real size.
Archive Arc(Cmd);
@@ -497,7 +478,7 @@ void RSEncode::DecodeBuf()
}
void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
void RecVolumes3::Test(CommandData *Cmd,const std::wstring &Name)
{
if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
{
@@ -505,8 +486,7 @@ void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
return;
}
wchar VolName[NM];
wcsncpyz(VolName,Name,ASIZE(VolName));
std::wstring VolName=Name;
while (FileExist(VolName))
{
@@ -518,7 +498,7 @@ void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
}
if (!uiStartFileExtract(VolName,false,true,false))
return;
mprintf(St(MExtrTestFile),VolName);
mprintf(St(MExtrTestFile),VolName.c_str());
mprintf(L" ");
CurFile.Seek(0,SEEK_END);
int64 Length=CurFile.Tell();
@@ -539,6 +519,6 @@ void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
ErrHandler.SetErrorCode(RARX_CRC);
}
NextVolumeName(VolName,ASIZE(VolName),false);
NextVolumeName(VolName,false);
}
}

View File

@@ -1,6 +1,10 @@
static const uint MaxVolumes=65535;
RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly)
// We select this limit arbitrarily, to prevent user creating too many
// rev files by mistake.
#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files.
RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly)
{
RealBuf=NULL;
RealReadBuffer=NULL;
@@ -44,8 +48,8 @@ RecVolumes5::~RecVolumes5()
{
delete[] RealBuf;
delete[] RealReadBuffer;
for (uint I=0;I<RecItems.Size();I++)
delete RecItems[I].f;
for (RecVolItem &Item : RecItems)
delete Item.f;
for (uint I=0;I<MaxUserThreads;I++)
delete ThreadData[I].RS;
delete[] ThreadData;
@@ -66,7 +70,7 @@ THREAD_PROC(RecThreadRS)
#endif
void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
{
/*
RSCoder16 RS;
@@ -137,20 +141,19 @@ void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
bool RecVolumes5::Restore(CommandData *Cmd,const std::wstring &Name,bool Silent)
{
wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName));
std::wstring ArcName=Name;
wchar *Num=GetVolNumPart(ArcName);
while (Num>ArcName && IsDigit(*(Num-1)))
Num--;
if (Num==ArcName)
size_t NumPos=GetVolNumPos(ArcName);
while (NumPos>0 && IsDigit(ArcName[NumPos-1]))
NumPos--;
if (NumPos<=GetNamePos(ArcName))
return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume.
wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName));
ArcName.replace(NumPos,std::wstring::npos,L"*.*");
wchar FirstVolName[NM];
*FirstVolName=0;
std::wstring FirstVolName;
std::wstring LongestRevName;
int64 RecFileSize=0;
@@ -164,7 +167,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
Archive *Vol=new Archive(Cmd);
int ItemPos=-1;
if (Vol->WOpen(fd.Name))
if (!fd.IsDir && Vol->WOpen(fd.Name))
{
if (CmpExt(fd.Name,L"rev"))
{
@@ -176,6 +179,9 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
ItemPos=RecNum;
FoundRecVolumes++;
if (fd.Name.size()>LongestRevName.size())
LongestRevName=fd.Name;
}
}
else
@@ -194,35 +200,35 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
// RAR volume found. Get its number, store the handle in appropriate
// array slot, clean slots in between if we had to grow the array.
wchar *Num=GetVolNumPart(fd.Name);
size_t NumPos=GetVolNumPos(fd.Name);
uint VolNum=0;
for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--)
VolNum+=(*Num-'0')*K;
for (uint K=1;(int)NumPos>=0 && IsDigit(fd.Name[NumPos]);K*=10,NumPos--)
VolNum+=(fd.Name[NumPos]-'0')*K;
if (VolNum==0 || VolNum>MaxVolumes)
continue;
size_t CurSize=RecItems.Size();
size_t CurSize=RecItems.size();
if (VolNum>CurSize)
{
RecItems.Alloc(VolNum);
for (size_t I=CurSize;I<VolNum;I++)
RecItems[I].f=NULL;
RecItems.resize(VolNum);
// for (size_t I=CurSize;I<VolNum;I++)
// RecItems[I].f=NULL;
}
ItemPos=VolNum-1;
if (*FirstVolName==0)
VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true);
if (FirstVolName.empty())
VolNameToFirstName(fd.Name,FirstVolName,true);
}
}
if (ItemPos==-1)
delete Vol; // Skip found file, it is not RAR or REV volume.
else
if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed.
if ((uint)ItemPos<RecItems.size()) // Check if found more REV than needed.
{
// Store found RAR or REV volume.
RecVolItem *Item=RecItems+ItemPos;
RecVolItem *Item=&RecItems[ItemPos];
Item->f=Vol;
Item->New=false;
wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name));
Item->Name=fd.Name;
}
}
@@ -231,6 +237,15 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (FoundRecVolumes==0)
return false;
// If we did not find even a single .rar volume, create .rar volume name
// based on the longest .rev file name. Use longest .rev, so we have
// enough space for volume number.
if (FirstVolName.empty())
{
SetExt(LongestRevName,L"rar");
VolNameToFirstName(LongestRevName,FirstVolName,true);
}
uiMsg(UIMSG_RECVOLCALCCHECKSUM);
MissingVolumes=0;
@@ -290,9 +305,8 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
{
Item->f->Close();
wchar NewName[NM];
wcsncpyz(NewName,Item->Name,ASIZE(NewName));
wcsncatz(NewName,L".bad",ASIZE(NewName));
std::wstring NewName;
NewName=Item->Name+L".bad";
uiMsg(UIMSG_BADARCHIVE,Item->Name);
uiMsg(UIMSG_RENAMING,Item->Name,NewName);
@@ -301,14 +315,14 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
Item->f=NULL;
}
if ((Item->New=(Item->f==NULL))) // Additional parentheses to avoid GCC warning.
if ((Item->New=(Item->f==NULL))==true)
{
wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name));
Item->Name=FirstVolName;
uiMsg(UIMSG_CREATING,Item->Name);
uiMsg(UIEVENT_NEWARCHIVE,Item->Name);
File *NewVol=new File;
bool UserReject;
if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject))
if (!FileCreate(Cmd,NewVol,Item->Name,&UserReject))
{
if (!UserReject)
ErrHandler.CreateErrorMsg(Item->Name);
@@ -316,9 +330,8 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
}
NewVol->Prealloc(Item->FileSize);
Item->f=NewVol;
Item->New=true;
}
NextVolumeName(FirstVolName,ASIZE(FirstVolName),false);
NextVolumeName(FirstVolName,false);
}
@@ -346,13 +359,11 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE.
#endif
uint *Data=new uint[TotalCount];
RSCoder16 RS;
if (!RS.Init(DataCount,RecCount,ValidFlags))
{
uiMsg(UIERROR_OPFAILED);
delete[] ValidFlags;
delete[] Data;
return false; // Should not happen, we check parameter validity above.
}
@@ -373,7 +384,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
J++;
VolNum=J++; // Use next valid REV volume data instead of RAR.
}
RecVolItem *Item=RecItems+VolNum;
RecVolItem *Item=&RecItems[VolNum];
byte *B=&ReadBuf[0];
int ReadSize=0;
@@ -395,7 +406,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
for (uint I=0,J=0;I<DataCount;I++)
if (!ValidFlags[I])
{
RecVolItem *Item=RecItems+I;
RecVolItem *Item=&RecItems[I];
size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize);
Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize);
Item->FileSize-=WriteSize;
@@ -415,7 +426,6 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
RecItems[I].f->Close();
delete[] ValidFlags;
delete[] Data;
#if !defined(SILENT)
if (!Cmd->DisablePercentage)
mprintf(L"\b\b\b\b100%%");
@@ -462,10 +472,10 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
{
// If we have read the first valid REV file, init data structures
// using information from REV header.
size_t CurSize=RecItems.Size();
RecItems.Alloc(TotalCount);
for (size_t I=CurSize;I<TotalCount;I++)
RecItems[I].f=NULL;
size_t CurSize=RecItems.size();
RecItems.resize(TotalCount);
// for (size_t I=CurSize;I<TotalCount;I++)
// RecItems[I].f=NULL;
for (uint I=0;I<DataCount;I++)
{
RecItems[I].FileSize=Raw.Get8();
@@ -479,10 +489,9 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
}
void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
void RecVolumes5::Test(CommandData *Cmd,const std::wstring &Name)
{
wchar VolName[NM];
wcsncpyz(VolName,Name,ASIZE(VolName));
std::wstring VolName=Name;
uint FoundRecVolumes=0;
while (FileExist(VolName))
@@ -495,7 +504,7 @@ void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
}
if (!uiStartFileExtract(VolName,false,true,false))
return;
mprintf(St(MExtrTestFile),VolName);
mprintf(St(MExtrTestFile),VolName.c_str());
mprintf(L" ");
bool Valid=false;
uint RecNum=ReadHeader(&CurFile,FoundRecVolumes==0);
@@ -518,6 +527,6 @@ void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
ErrHandler.SetErrorCode(RARX_CRC);
}
NextVolumeName(VolName,ASIZE(VolName),false);
NextVolumeName(VolName,false);
}
}

View File

@@ -9,14 +9,5 @@ const wchar* St(MSGID StringId)
{
return StringId;
}
// Needed for Unix swprintf to convert %s to %ls in legacy language resources.
const wchar *StF(MSGID StringId)
{
static wchar FormattedStr[512];
PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr));
return FormattedStr;
}
#endif

View File

@@ -3,10 +3,8 @@
#ifdef RARDLL
#define St(x) (L"")
#define StF(x) (L"")
#else
const wchar *St(MSGID StringId);
const wchar *StF(MSGID StringId);
#endif

View File

@@ -1,22 +1,41 @@
/***************************************************************************
* This code is based on public domain Szymon Stefanek AES implementation: *
* http://www.pragmaware.net/software/rijndael/index.php *
* *
* Dynamic tables generation is based on the Brian Gladman work: *
* http://fp.gladman.plus.com/cryptography_technology/rijndael *
***************************************************************************/
/**************************************************************************
* This code is based on Szymon Stefanek public domain AES implementation *
**************************************************************************/
#include "rar.hpp"
#ifdef USE_SSE
#include <wmmintrin.h>
#endif
static byte S[256],S5[256],rcon[30];
static byte S[256]=
{
99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,
202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,
186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22
};
static byte S5[256];
// Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256.
static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36};
static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4];
static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4];
static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4];
inline void Xor128(void *dest,const void *arg1,const void *arg2)
{
#ifdef ALLOW_MISALIGNED
@@ -63,26 +82,53 @@ inline void Copy128(byte *dest,const byte *src)
Rijndael::Rijndael()
{
if (S[0]==0)
if (S5[0]==0)
GenerateTables();
m_uRounds = 0;
CBCMode = true; // Always true for RAR.
#ifdef USE_SSE
AES_NI=false;
#endif
#ifdef USE_NEON_AES
AES_Neon=false;
#endif
}
void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector)
{
#ifdef USE_SSE
// Check SSE here instead of constructor, so if object is a part of some
// structure memset'ed before use, this variable is not lost.
// Check SIMD here instead of constructor, so if object is a part of some
// structure memset'ed before use, these variables are not lost.
#if defined(USE_SSE)
#ifdef _MSC_VER
int CPUInfo[4];
__cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function.
if ((CPUInfo[0] & 0x7fffffff)>=1)
__cpuid(CPUInfo, 0);
if (CPUInfo[0]>=1) // Check the maximum supported cpuid function.
{
__cpuid(CPUInfo, 1);
AES_NI=(CPUInfo[2] & 0x2000000)!=0;
}
else
AES_NI=0;
AES_NI=false;
#elif defined(__GNUC__)
AES_NI=__builtin_cpu_supports("aes");
#endif
#elif defined(USE_NEON_AES)
#ifdef _APPLE
// getauxval isn't available in OS X
uint Value=0;
size_t Size=sizeof(Value);
int RetCode=sysctlbyname("hw.optional.arm.FEAT_AES",&Value,&Size,NULL,0);
// We treat sysctlbyname failure with -1 return code as AES presence,
// because "hw.optional.arm.FEAT_AES" was missing in OS X 11, but AES
// still was supported by Neon.
AES_Neon=RetCode!=0 || Value!=0;
#else
AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0;
#endif
#endif
// Other developers asked us to initialize it to suppress "may be used
@@ -122,18 +168,25 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe
keyEncToDec();
}
void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer)
{
if (inputLen <= 0)
return;
size_t numBlocks = inputLen/16;
#ifdef USE_SSE
#if defined(USE_SSE)
if (AES_NI)
{
blockEncryptSSE(input,numBlocks,outBuffer);
return;
}
#elif defined(USE_NEON_AES)
if (AES_Neon)
{
blockEncryptNeon(input,numBlocks,outBuffer);
return;
}
#endif
byte *prevBlock = m_initVector;
@@ -220,6 +273,40 @@ void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffe
}
#endif
#ifdef USE_NEON_AES
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)
{
@@ -227,12 +314,18 @@ void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer)
return;
size_t numBlocks=inputLen/16;
#ifdef USE_SSE
#if defined(USE_SSE)
if (AES_NI)
{
blockDecryptSSE(input,numBlocks,outBuffer);
return;
}
#elif defined(USE_NEON_AES)
if (AES_Neon)
{
blockDecryptNeon(input,numBlocks,outBuffer);
return;
}
#endif
byte block[16], iv[4][4];
@@ -324,6 +417,41 @@ void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuf
#endif
#ifdef USE_NEON_AES
void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer)
{
byte iv[16];
memcpy(iv,m_initVector,16);
while (numBlocks > 0)
{
uint8x16_t data = vld1q_u8(input);
for (int i=m_uRounds-1; i>0; i--)
{
data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1]));
data = vaesimcq_u8(data);
}
data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1]));
data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0]));
if (CBCMode)
data = veorq_u8(data, vld1q_u8(iv));
vst1q_u8(iv, vld1q_u8(input));
vst1q_u8(outBuffer, data);
input += 16;
outBuffer += 16;
numBlocks--;
}
memcpy(m_initVector,iv,16);
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ALGORITHM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -416,51 +544,40 @@ void Rijndael::keyEncToDec()
}
#define ff_poly 0x011b
#define ff_hi 0x80
static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication.
{
const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial.
byte result = 0;
while (b>0)
{
if ((b & 1) != 0)
result ^= a;
a = (a & 0x80) ? (a<<1)^poly : a<<1;
b >>= 1;
}
return result;
}
#define FFinv(x) ((x) ? pow[255 - log[x]]: 0)
#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0)
#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0)
#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0)
#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0)
#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0)
#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0)
#define fwd_affine(x) \
(w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8))))
#define inv_affine(x) \
(w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8))))
// 2021-09-24: changed to slower and simpler code without interim tables.
// It is still fast enough for our purpose.
void Rijndael::GenerateTables()
{
unsigned char pow[512],log[256];
int i = 0, w = 1;
do
for (int I=0;I<256;I++)
S5[S[I]]=I;
for (int I=0;I<256;I++)
{
pow[i] = (byte)w;
pow[i + 255] = (byte)w;
log[w] = (byte)i++;
w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0);
} while (w != 1);
for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++)
{
rcon[i] = w;
w = (w << 1) ^ (w & ff_hi ? ff_poly : 0);
}
for(int i = 0; i < 256; ++i)
{
unsigned char b=S[i]=fwd_affine(FFinv((byte)i));
T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b;
T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b);
T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b);
S5[i] = b = FFinv(inv_affine((byte)i));
U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b);
U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b);
U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b);
U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b);
byte s=S[I];
T1[I][1]=T1[I][2]=T2[I][2]=T2[I][3]=T3[I][0]=T3[I][3]=T4[I][0]=T4[I][1]=s;
T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2);
T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3);
byte b=S5[I];
U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[I][3]=T6[I][0]=T7[I][1]=T8[I][2]=gmul(b,0xb);
U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9);
U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd);
U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe);
}
}
@@ -493,16 +610,16 @@ void TestRijndael()
for (uint L=0;L<3;L++)
{
byte Out[16];
wchar Str[sizeof(Out)*2+1];
std::wstring Str;
uint KeyLength=128+L*64;
rij.Init(true,Key[L],KeyLength,IV);
for (uint I=0;I<sizeof(PT);I+=16)
rij.blockEncrypt(PT+I,16,Out);
BinToHex(Chk[L],16,NULL,Str,ASIZE(Str));
mprintf(L"\nAES-%d expected: %s",KeyLength,Str);
BinToHex(Out,sizeof(Out),NULL,Str,ASIZE(Str));
mprintf(L"\nAES-%d result: %s",KeyLength,Str);
BinToHex(Chk[L],16,Str);
mprintf(L"\nAES-%d expected: %s",KeyLength,Str.c_str());
BinToHex(Out,sizeof(Out),Str);
mprintf(L"\nAES-%d result: %s",KeyLength,Str.c_str());
if (memcmp(Out,Chk[L],16)==0)
mprintf(L" OK");
else

View File

@@ -2,11 +2,7 @@
#define _RIJNDAEL_H_
/**************************************************************************
* This code is based on Szymon Stefanek AES implementation: *
* http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz *
* *
* Dynamic tables generation is based on the Brian Gladman's work: *
* http://fp.gladman.plus.com/cryptography_technology/rijndael *
* This code is based on Szymon Stefanek public domain AES implementation *
**************************************************************************/
#define _MAX_KEY_COLUMNS (256/32)
@@ -16,12 +12,31 @@
class Rijndael
{
private:
#ifdef USE_SSE
#ifdef __GNUC__
__attribute__((target("aes")))
#endif
void blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer);
#ifdef __GNUC__
__attribute__((target("aes")))
#endif
void blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer);
bool AES_NI;
#endif
#ifdef USE_NEON_AES
// In Android we must specify -march=armv8-a+crypto compiler switch
// to support Neon AES commands, "crypto" attribute seems to be optional.
__attribute__((target("+crypto")))
void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer);
__attribute__((target("+crypto")))
void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer);
bool AES_Neon;
#endif
void keySched(byte key[_MAX_KEY_COLUMNS][4]);
void keyEncToDec();
void GenerateTables();

View File

@@ -95,7 +95,9 @@ bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags)
if (NE > ValidECC || NE == 0 || ValidECC == 0)
return false;
}
if (ND + NR > gfSize || NR > ND || ND == 0 || NR == 0)
// 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check.
if (ND + NR > gfSize || /*NR > ND ||*/ ND == 0 || NR == 0)
return false;
delete[] MX;
@@ -144,7 +146,7 @@ void RSCoder16::MakeDecoderMatrix()
}
// Apply GaussJordan elimination to find inverse of decoder matrix.
// Apply Gauss-Jordan elimination to find inverse of decoder matrix.
// We have the square NDxND matrix, but we do not store its trivial
// diagonal "1" rows matching valid data, so we work with NExND matrix.
// Our original Cauchy matrix does not contain 0, so we skip search
@@ -331,14 +333,14 @@ bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte
for (uint I=0;I<16;I++)
{
((byte *)&T0L)[I]=gfMul(I,M);
((byte *)&T0H)[I]=gfMul(I,M)>>8;
((byte *)&T1L)[I]=gfMul(I<<4,M);
((byte *)&T1H)[I]=gfMul(I<<4,M)>>8;
((byte *)&T2L)[I]=gfMul(I<<8,M);
((byte *)&T2H)[I]=gfMul(I<<8,M)>>8;
((byte *)&T3L)[I]=gfMul(I<<12,M);
((byte *)&T3H)[I]=gfMul(I<<12,M)>>8;
((byte *)&T0L)[I]=byte(gfMul(I,M));
((byte *)&T0H)[I]=byte(gfMul(I,M)>>8);
((byte *)&T1L)[I]=byte(gfMul(I<<4,M));
((byte *)&T1H)[I]=byte(gfMul(I<<4,M)>>8);
((byte *)&T2L)[I]=byte(gfMul(I<<8,M));
((byte *)&T2H)[I]=byte(gfMul(I<<8,M)>>8);
((byte *)&T3L)[I]=byte(gfMul(I<<12,M));
((byte *)&T3H)[I]=byte(gfMul(I<<12,M)>>8);
}
size_t Pos=0;

View File

@@ -17,6 +17,9 @@ class RSCoder16
void InvertDecoderMatrix();
#ifdef USE_SSE
#if defined(USE_SSE) && defined(__GNUC__)
__attribute__((target("ssse3")))
#endif
bool SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize);
#endif

Some files were not shown because too many files have changed in this diff Show More