diff --git a/CMakeLists.txt b/CMakeLists.txt
index 38a69f3f2e..a15ea12d82 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,7 @@ string(TIMESTAMP TODAY "%Y%m%d")
set(VERSION_SUFFIX "")
project( ClamAV
- VERSION "1.0.2"
+ VERSION "1.0.3"
DESCRIPTION "ClamAV open source email, web, and end-point anti-virus toolkit." )
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
diff --git a/Jenkinsfile b/Jenkinsfile
index 34598cee04..a3996e84f5 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -10,7 +10,7 @@ properties(
parameters(
[
string(name: 'VERSION',
- defaultValue: '1.0.2',
+ defaultValue: '1.0.3',
description: 'ClamAV version string'),
string(name: 'FRAMEWORK_BRANCH',
defaultValue: '1.0',
diff --git a/NEWS.md b/NEWS.md
index f139701469..d11b535508 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -3,6 +3,13 @@
Note: This file refers to the official packages. Things described here may
differ slightly from third-party binary packages.
+## 1.0.3
+
+ClamAV 1.0.3 is a critical patch release with the following fixes:
+
+- Upgrade the bundled UnRAR library (libclamunrar) to version 6.2.10.
+ - GitHub pull request: https://github.com/Cisco-Talos/clamav/pull/1010
+
## 1.0.2
ClamAV 1.0.2 is a critical patch release with the following fixes:
diff --git a/libclamav/bytecode_api.h b/libclamav/bytecode_api.h
index caee7f600c..ebb58df145 100644
--- a/libclamav/bytecode_api.h
+++ b/libclamav/bytecode_api.h
@@ -166,6 +166,7 @@ enum FunctionalityLevels {
FUNC_LEVEL_1_0 = 160, /**< LibClamAV release 1.0.0 */
FUNC_LEVEL_1_0_1 = 161, /**< LibClamAV release 1.0.1 */
FUNC_LEVEL_1_0_2 = 162, /**< LibClamAV release 1.0.2 */
+ FUNC_LEVEL_1_0_3 = 163, /**< LibClamAV release 1.0.3 */
};
/**
diff --git a/libclamav/others.h b/libclamav/others.h
index 761d2c17fa..3893a8254a 100644
--- a/libclamav/others.h
+++ b/libclamav/others.h
@@ -73,7 +73,7 @@
* in re-enabling affected modules.
*/
-#define CL_FLEVEL 161
+#define CL_FLEVEL 163
#define CL_FLEVEL_DCONF CL_FLEVEL
#define CL_FLEVEL_SIGTOOL CL_FLEVEL
diff --git a/libclamunrar/CMakeLists.txt b/libclamunrar/CMakeLists.txt
index 6b924b466a..e54348f845 100644
--- a/libclamunrar/CMakeLists.txt
+++ b/libclamunrar/CMakeLists.txt
@@ -68,9 +68,12 @@ endif()
if(ENABLE_SHARED_LIB)
# The unrar shared library.
add_library( clamunrar SHARED )
- set_target_properties(clamunrar PROPERTIES
+ set_target_properties( clamunrar PROPERTIES
VERSION ${LIBCLAMAV_VERSION}
- SOVERSION ${LIBCLAMAV_SOVERSION})
+ SOVERSION ${LIBCLAMAV_SOVERSION}
+ CXX_STANDARD 11
+ )
+
target_sources( clamunrar
PRIVATE
${UNRAR_SOURCES}
@@ -110,6 +113,9 @@ endif()
if(ENABLE_STATIC_LIB)
# The clamunrar static library.
add_library( clamunrar_static STATIC)
+ set_target_properties( clamunrar_static PROPERTIES
+ CXX_STANDARD 11
+ )
target_sources( clamunrar_static
PRIVATE
${UNRAR_SOURCES}
diff --git a/libclamunrar/UnRARDll.vcxproj b/libclamunrar/UnRARDll.vcxproj
index ec5c17b00e..72cecd8dba 100644
--- a/libclamunrar/UnRARDll.vcxproj
+++ b/libclamunrar/UnRARDll.vcxproj
@@ -138,7 +138,7 @@
Sync
EnableFastChecks
MultiThreadedDebug
- 4Bytes
+ Default
false
Use
rar.hpp
@@ -168,7 +168,7 @@
Sync
EnableFastChecks
MultiThreadedDebug
- 4Bytes
+ Default
false
Use
rar.hpp
@@ -198,7 +198,7 @@
false
Sync
MultiThreaded
- 4Bytes
+ Default
true
true
NoExtensions
@@ -239,7 +239,7 @@
false
Sync
MultiThreaded
- 4Bytes
+ Default
true
true
false
@@ -274,7 +274,7 @@
false
Sync
MultiThreaded
- 4Bytes
+ Default
true
true
NoExtensions
@@ -315,7 +315,7 @@
false
Sync
MultiThreaded
- 4Bytes
+ Default
true
true
false
diff --git a/libclamunrar/archive.cpp b/libclamunrar/archive.cpp
index 0f5de946f7..25f0c3b79d 100644
--- a/libclamunrar/archive.cpp
+++ b/libclamunrar/archive.cpp
@@ -3,15 +3,15 @@
#include "arccmt.cpp"
-Archive::Archive(RAROptions *InitCmd)
+Archive::Archive(CommandData *InitCmd)
{
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
DummyCmd=(InitCmd==NULL);
- Cmd=DummyCmd ? (new RAROptions):InitCmd;
+ Cmd=DummyCmd ? (new CommandData):InitCmd;
OpenShared=Cmd->OpenShared;
- Format=RARFMT15;
+ Format=RARFMT_NONE;
Solid=false;
Volume=false;
MainComment=false;
@@ -31,9 +31,9 @@ Archive::Archive(RAROptions *InitCmd)
NextBlockPos=0;
- memset(&MainHead,0,sizeof(MainHead));
- memset(&CryptHead,0,sizeof(CryptHead));
- memset(&EndArcHead,0,sizeof(EndArcHead));
+ MainHead.Reset();
+ CryptHead={};
+ EndArcHead.Reset();
VolNumber=0;
VolWrite=0;
diff --git a/libclamunrar/archive.hpp b/libclamunrar/archive.hpp
index d9518f1dc4..c0019aef57 100644
--- a/libclamunrar/archive.hpp
+++ b/libclamunrar/archive.hpp
@@ -32,8 +32,8 @@ class Archive:public File
size_t ReadHeader14();
size_t ReadHeader15();
size_t ReadHeader50();
- void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
- void RequestArcPassword();
+ void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
+ void RequestArcPassword(RarCheckPassword *SelPwd);
void UnexpEndArcMsg();
void BrokenHeaderMsg();
void UnkEncVerMsg(const wchar *Name,const wchar *Info);
@@ -45,7 +45,7 @@ class Archive:public File
#endif
ComprDataIO SubDataIO;
bool DummyCmd;
- RAROptions *Cmd;
+ CommandData *Cmd;
RarTime LatestTime;
@@ -58,7 +58,7 @@ class Archive:public File
bool ProhibitQOpen;
#endif
public:
- Archive(RAROptions *InitCmd=NULL);
+ Archive(CommandData *InitCmd=NULL);
~Archive();
static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
@@ -83,7 +83,7 @@ class Archive:public File
const wchar *Name,uint Flags);
bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode);
HEADER_TYPE GetHeaderType() {return CurHeaderType;}
- RAROptions* GetRAROptions() {return Cmd;}
+ CommandData* GetCommandData() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
#if 0
void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
@@ -107,7 +107,6 @@ class Archive:public File
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
- UnixOwnersHeader UOHead;
EAHeader EAHead;
StreamHeader StreamHead;
diff --git a/libclamunrar/arcread.cpp b/libclamunrar/arcread.cpp
index 1a401f48d3..f5ef9aa517 100644
--- a/libclamunrar/arcread.cpp
+++ b/libclamunrar/arcread.cpp
@@ -100,6 +100,9 @@ void Archive::UnexpEndArcMsg()
// If block positions are equal to file size, this is not an error.
// It can happen when we reached the end of older RAR 1.5 archive,
// which did not have the end of archive block.
+ // We can't replace this check by checking that read size is exactly 0
+ // in the beginning of file header, because in this case the read position
+ // still can be beyond the end of archive.
if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{
uiMsg(UIERROR_UNEXPEOF,FileName);
@@ -145,7 +148,7 @@ size_t Archive::ReadHeader15()
#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
return 0;
#else
- RequestArcPassword();
+ RequestArcPassword(NULL);
byte Salt[SIZE_SALT30];
if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
@@ -251,7 +254,11 @@ size_t Archive::ReadHeader15()
hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
+
+ // RAR versions earlier than 2.0 do not set the solid flag
+ // in file header. They use only a global solid archive flag.
hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
+
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
@@ -469,19 +476,6 @@ size_t Archive::ReadHeader15()
SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
- case UO_HEAD:
- *(SubBlockHeader *)&UOHead=SubBlockHead;
- UOHead.OwnerNameSize=Raw.Get2();
- UOHead.GroupNameSize=Raw.Get2();
- if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
- UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
- if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
- UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
- Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
- Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
- UOHead.OwnerName[UOHead.OwnerNameSize]=0;
- UOHead.GroupName[UOHead.GroupNameSize]=0;
- break;
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
EAHead.UnpSize=Raw.Get4();
@@ -513,8 +507,12 @@ size_t Archive::ReadHeader15()
ushort HeaderCRC=Raw.GetCRC15(false);
// Old AV header does not have header CRC properly set.
+ // Old Unix owners header didn't include string fields into header size,
+ // but included them into CRC, so it couldn't be verified with generic
+ // approach here.
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
- ShortBlock.HeaderType!=HEAD3_AV)
+ ShortBlock.HeaderType!=HEAD3_AV &&
+ (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD))
{
bool Recovered=false;
if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
@@ -577,14 +575,20 @@ size_t Archive::ReadHeader50()
// in -p to not stop batch processing for encrypted archives.
bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
+ RarCheckPassword CheckPwd;
+ if (CryptHead.UsePswCheck && !BrokenHeader)
+ CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck);
+
while (true) // Repeat the password prompt for wrong passwords.
{
- RequestArcPassword();
+ RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL);
byte PswCheck[SIZE_PSWCHECK];
HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
- // Verify password validity.
- if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
+ // Verify password validity. If header is damaged, we cannot rely on
+ // password check value, because it can be damaged too.
+ if (CryptHead.UsePswCheck && !BrokenHeader &&
+ memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
{
if (GlobalPassword) // For -p or Ctrl+P.
{
@@ -850,8 +854,6 @@ size_t Archive::ReadHeader50()
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
- hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
-
char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
@@ -875,25 +877,6 @@ size_t Archive::ReadHeader50()
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true;
-#if 0
- // For RAR5 format we read the user specified recovery percent here.
- // It would be useful to do it for shell extension too, so we display
- // the correct recovery record size in archive properties. But then
- // we would need to include the entire recovery record processing
- // code to shell extension, which is not done now.
- if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
- {
- // It is stored as a single byte up to RAR 6.02 and as vint since
- // 6.10, where we extended the maximum RR size from 99% to 1000%.
- RawRead RawPercent;
- RawPercent.Read(&hd->SubData[0],hd->SubData.Size());
- RecoveryPercent=(int)RawPercent.GetV();
-
- RSBlockHeader Header;
- GetRRInfo(this,&Header);
- RecoverySize=Header.RecSectionSize*Header.RecCount;
- }
-#endif
if (BadCRC) // Add the file name to broken header message displayed above.
uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
@@ -916,7 +899,7 @@ size_t Archive::ReadHeader50()
#if !defined(RAR_NOCRYPT)
-void Archive::RequestArcPassword()
+void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
{
if (!Cmd->Password.IsSet())
{
@@ -946,7 +929,7 @@ void Archive::RequestArcPassword()
ErrHandler.Exit(RARX_USERBREAK);
}
#else
- if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
+ if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd))
{
Close();
uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
@@ -959,7 +942,7 @@ void Archive::RequestArcPassword()
#endif
-void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
+void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
{
// Read extra data from the end of block skipping any fields before it.
size_t ExtraStart=Raw->Size()-ExtraSize;
@@ -982,22 +965,57 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
if (bb->HeaderType==HEAD_MAIN)
{
MainHeader *hd=(MainHeader *)bb;
- if (FieldType==MHEXTRA_LOCATOR)
+ switch(FieldType)
{
- hd->Locator=true;
- uint Flags=(uint)Raw->GetV();
- if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
- {
- uint64 Offset=Raw->GetV();
- if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
- hd->QOpenOffset=Offset+CurBlockPos;
- }
- if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
- {
- uint64 Offset=Raw->GetV();
- if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
- hd->RROffset=Offset+CurBlockPos;
- }
+ case MHEXTRA_LOCATOR:
+ {
+ hd->Locator=true;
+ uint Flags=(uint)Raw->GetV();
+ if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->QOpenOffset=Offset+CurBlockPos;
+ }
+ if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->RROffset=Offset+CurBlockPos;
+ }
+ }
+ break;
+ case MHEXTRA_METADATA:
+ {
+ uint Flags=(uint)Raw->GetV();
+ if ((Flags & MHEXTRA_METADATA_NAME)!=0)
+ {
+ uint64 NameSize=Raw->GetV();
+ if (NameSize<0x10000) // Prevent excessive allocation.
+ {
+ std::vector NameU((size_t)NameSize); // UTF-8 name.
+ Raw->GetB(&NameU[0],(size_t)NameSize);
+ // If starts from 0, the name was longer than reserved space
+ // when saving this extra field.
+ if (NameU[0]!=0)
+ {
+ NameU.push_back(0);
+ std::vector NameW(NameU.size()*4);
+ UtfToWide(&NameU[0],&NameW[0],NameW.size());
+ hd->OrigName.assign(&NameW[0]);
+ }
+ }
+ }
+ if ((Flags & MHEXTRA_METADATA_CTIME)!=0)
+ if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0)
+ if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0)
+ hd->OrigTime.SetUnixNS(Raw->Get8());
+ else
+ hd->OrigTime.SetUnix((time_t)Raw->Get4());
+ else
+ hd->OrigTime.SetWin(Raw->Get8());
+ }
+ break;
}
}
@@ -1453,7 +1471,9 @@ bool Archive::ReadSubData(Array *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;
}
diff --git a/libclamunrar/array.hpp b/libclamunrar/array.hpp
index 20d258d5b8..ac786f7109 100644
--- a/libclamunrar/array.hpp
+++ b/libclamunrar/array.hpp
@@ -10,7 +10,6 @@ template class Array
size_t BufSize;
size_t AllocSize;
size_t MaxSize;
- bool Secure; // Clean memory if true.
public:
Array();
Array(size_t Size);
@@ -24,14 +23,13 @@ template class Array
void Alloc(size_t Items);
void Reset();
void SoftReset();
- void operator = (Array &Src);
+ Array& operator = (const Array &Src);
void Push(T Item);
void Append(T *Item,size_t Count);
T* Addr(size_t Item) {return Buffer+Item;}
void SetMaxSize(size_t Size) {MaxSize=Size;}
T* Begin() {return Buffer;}
T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
- void SetSecure() {Secure=true;}
};
@@ -41,7 +39,6 @@ template void Array::CleanData()
BufSize=0;
AllocSize=0;
MaxSize=0;
- Secure=false;
}
@@ -71,11 +68,7 @@ template Array::Array(const Array &Src)
template Array::~Array()
{
if (Buffer!=NULL)
- {
- if (Secure)
- cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
- }
}
@@ -111,25 +104,9 @@ template void Array::Add(size_t Items)
size_t Suggested=AllocSize+AllocSize/4+32;
size_t NewSize=Max(BufSize,Suggested);
- T *NewBuffer;
- if (Secure)
- {
- NewBuffer=(T *)malloc(NewSize*sizeof(T));
- if (NewBuffer==NULL)
- ErrHandler.MemoryError();
- if (Buffer!=NULL)
- {
- memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
- cleandata(Buffer,AllocSize*sizeof(T));
- free(Buffer);
- }
- }
- else
- {
- NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
- if (NewBuffer==NULL)
- ErrHandler.MemoryError();
- }
+ T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
+ if (NewBuffer==NULL)
+ ErrHandler.MemoryError();
Buffer=NewBuffer;
AllocSize=NewSize;
}
@@ -165,12 +142,13 @@ template void Array::SoftReset()
}
-template void Array::operator =(Array &Src)
+template Array& Array::operator =(const Array &Src)
{
Reset();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
+ return *this;
}
diff --git a/libclamunrar/blake2s.hpp b/libclamunrar/blake2s.hpp
index f88ef37802..06e396a77b 100644
--- a/libclamunrar/blake2s.hpp
+++ b/libclamunrar/blake2s.hpp
@@ -20,10 +20,15 @@ enum blake2s_constant
// 'new' operator.
struct blake2s_state
{
- enum { BLAKE_ALIGNMENT = 64 };
+ // Use constexpr instead of enums, because otherwise clang -std=c++20
+ // issues a warning about "arithmetic between different enumeration types"
+ // in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration.
+ static constexpr size_t BLAKE_ALIGNMENT = 64;
// buffer and uint32 h[8], t[2], f[2];
- enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
+ // 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip.
+ // It might differ in later versions.
+ static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES;
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
diff --git a/libclamunrar/cmddata.cpp b/libclamunrar/cmddata.cpp
index 82d24f44f0..ffc5ff90db 100644
--- a/libclamunrar/cmddata.cpp
+++ b/libclamunrar/cmddata.cpp
@@ -26,9 +26,10 @@ void CommandData::Init()
FileArgs.Reset();
ExclArgs.Reset();
InclArgs.Reset();
- StoreArgs.Reset();
ArcNames.Reset();
- NextVolSizes.Reset();
+ StoreArgs.Reset();
+ Password.Clean();
+ NextVolSizes.clear();
}
@@ -314,6 +315,21 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'I':
IgnoreGeneralAttr=true;
break;
+ case 'M':
+ switch(toupperw(Switch[2]))
+ {
+ case 0:
+ case 'S':
+ ArcMetadata=ARCMETA_SAVE;
+ break;
+ case 'R':
+ ArcMetadata=ARCMETA_RESTORE;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ break;
case 'N': // Reserved for archive name.
break;
case 'O':
@@ -415,7 +431,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
else
if (!Password.IsSet())
{
- uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
eprintf(L"\n");
}
break;
@@ -685,7 +701,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'P':
if (Switch[1]==0)
{
- uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
eprintf(L"\n");
}
else
@@ -927,6 +943,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
void CommandData::BadSwitch(const wchar *Switch)
{
mprintf(St(MUnknownOption),Switch);
+ mprintf(L"\n");
ErrHandler.Exit(RARX_USERERROR);
}
#endif
diff --git a/libclamunrar/cmddata.hpp b/libclamunrar/cmddata.hpp
index 2a21922006..0feb404b3e 100644
--- a/libclamunrar/cmddata.hpp
+++ b/libclamunrar/cmddata.hpp
@@ -2,7 +2,7 @@
#define _RAR_CMDDATA_
-#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx"
+#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst"
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
@@ -65,6 +65,10 @@ class CommandData:public RAROptions
StringList InclArgs;
StringList ArcNames;
StringList StoreArgs;
+
+ SecPassword Password;
+
+ std::vector NextVolSizes;
};
#endif
diff --git a/libclamunrar/cmdfilter.cpp b/libclamunrar/cmdfilter.cpp
index d098d1088c..e0add14bfa 100644
--- a/libclamunrar/cmdfilter.cpp
+++ b/libclamunrar/cmdfilter.cpp
@@ -289,8 +289,8 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy
return 0;
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
return 0;
- if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 ||
- FileHead.Dir && !InclDir))
+ if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
+ (!FileHead.Dir || !InclDir))
return 0;
if (!Dir && SizeCheck(FileHead.UnpSize))
return 0;
diff --git a/libclamunrar/cmdmix.cpp b/libclamunrar/cmdmix.cpp
index 1fc4b8caff..6bd1e1ac0f 100644
--- a/libclamunrar/cmdmix.cpp
+++ b/libclamunrar/cmdmix.cpp
@@ -92,6 +92,13 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
if (Found)
continue;
#endif
+#ifdef _UNIX
+ if (CmpMSGID(Help[I],MRARTitle2))
+ {
+ mprintf(St(MFwrSlTitle2));
+ continue;
+ }
+#endif
#if !defined(_UNIX) && !defined(_WIN_ALL)
if (CmpMSGID(Help[I],MCHelpSwOW))
continue;
diff --git a/libclamunrar/compress.hpp b/libclamunrar/compress.hpp
index 73f7ee41ae..4ef8570f4e 100644
--- a/libclamunrar/compress.hpp
+++ b/libclamunrar/compress.hpp
@@ -17,6 +17,7 @@ class PackDef
static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3;
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
+ static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3;
static const uint LOW_DIST_REP_COUNT = 16;
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
diff --git a/libclamunrar/crc.cpp b/libclamunrar/crc.cpp
index 0e5e13206f..0c6aef1607 100644
--- a/libclamunrar/crc.cpp
+++ b/libclamunrar/crc.cpp
@@ -110,3 +110,164 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
#endif
+#if 0
+static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64.
+
+void InitCRC64(uint64 *CRCTab)
+{
+ const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42;
+ for (uint I=0;I<256;I++)
+ {
+ uint64 C=I;
+ for (uint J=0;J<8;J++)
+ C=(C & 1) ? (C>>1)^poly: (C>>1);
+ CRCTab[I]=C;
+ }
+}
+
+
+static void InitTables64()
+{
+ InitCRC64(crc64_tables[0]);
+
+ for (uint I=0;I<256;I++) // Build additional lookup tables.
+ {
+ uint64 C=crc64_tables[0][I];
+ for (uint J=1;J<8;J++)
+ {
+ C=crc64_tables[0][(byte)C]^(C>>8);
+ crc64_tables[J][I]=C;
+ }
+ }
+}
+
+
+// We cannot place the intialization to CRC64(), because we use this function
+// in multithreaded mode and it conflicts with multithreading.
+struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64;
+
+uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size)
+{
+ byte *Data=(byte *)Addr;
+
+ // Align Data to 8 for better performance.
+ for (;Size>0 && ((size_t)Data & 7)!=0;Size--,Data++)
+ StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ for (byte *DataEnd=Data+Size/8*8; Data> 8 ) ] ^
+ crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^
+ crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^
+ crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^
+ crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^
+ crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^
+ crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ;
+ }
+
+ for (Size%=8;Size>0;Size--,Data++) // Process left data.
+ StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ return StartCRC;
+}
+
+
+#if 0
+static void TestCRC();
+struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC;
+
+void TestCRC()
+{
+ const uint FirstSize=300;
+ byte b[FirstSize];
+
+ if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84)
+ mprintf(L"\nCRC32 test1 OK");
+ else
+ mprintf(L"\nCRC32 test1 FAILED");
+
+ if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE)
+ mprintf(L"\nCRC32 test2 OK");
+ else
+ mprintf(L"\nCRC32 test2 FAILED");
+
+ for (uint I=0;I<14;I++) // Check for possible int sign extension.
+ b[I]=(byte)0x7f+I;
+ if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA)
+ mprintf(L"\nCRC32 test3 OK");
+ else
+ mprintf(L"\nCRC32 test3 FAILED");
+
+ for (uint I=0;IIsSet() || Method==CRYPT_NONE)
+ if (Method==CRYPT_NONE || !Password->IsSet())
return false;
CryptData::Method=Method;
wchar PwdW[MAXPASSWORD];
Password->Get(PwdW,ASIZE(PwdW));
+ PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
+
char PwdA[MAXPASSWORD];
WideToChar(PwdW,PwdA,ASIZE(PwdA));
+ PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
switch(Method)
{
diff --git a/libclamunrar/crypt.hpp b/libclamunrar/crypt.hpp
index f6382ef504..9dc0215998 100644
--- a/libclamunrar/crypt.hpp
+++ b/libclamunrar/crypt.hpp
@@ -30,6 +30,18 @@ class CryptData
uint Lg2Count; // Log2 of PBKDF2 repetition count.
byte PswCheckValue[SHA256_DIGEST_SIZE];
byte HashKeyValue[SHA256_DIGEST_SIZE];
+
+ KDF5CacheItem() {Clean();}
+ ~KDF5CacheItem() {Clean();}
+
+ void Clean()
+ {
+ cleandata(Salt,sizeof(Salt));
+ cleandata(Key,sizeof(Key));
+ cleandata(&Lg2Count,sizeof(Lg2Count));
+ cleandata(PswCheckValue,sizeof(PswCheckValue));
+ cleandata(HashKeyValue,sizeof(HashKeyValue));
+ }
};
struct KDF3CacheItem
@@ -39,6 +51,17 @@ class CryptData
byte Key[16];
byte Init[16];
bool SaltPresent;
+
+ KDF3CacheItem() {Clean();}
+ ~KDF3CacheItem() {Clean();}
+
+ void Clean()
+ {
+ cleandata(Salt,sizeof(Salt));
+ cleandata(Key,sizeof(Key));
+ cleandata(Init,sizeof(Init));
+ cleandata(&SaltPresent,sizeof(SaltPresent));
+ }
};
@@ -77,7 +100,6 @@ class CryptData
ushort Key15[4];
public:
CryptData();
- ~CryptData();
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,
byte *HashKey,byte *PswCheck);
@@ -88,6 +110,54 @@ class CryptData
static void SetSalt(byte *Salt,size_t SaltSize);
};
+
+class CheckPassword
+{
+ public:
+ enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
+ virtual CONFIDENCE GetConfidence()=0;
+ virtual bool Check(SecPassword *Password)=0;
+};
+
+class RarCheckPassword:public CheckPassword
+{
+ private:
+ CryptData *Crypt;
+ uint Lg2Count;
+ byte Salt[SIZE_SALT50];
+ byte InitV[SIZE_INITV];
+ byte PswCheck[SIZE_PSWCHECK];
+ public:
+ RarCheckPassword()
+ {
+ Crypt=NULL;
+ }
+ ~RarCheckPassword()
+ {
+ delete Crypt;
+ }
+ void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
+ {
+ if (Crypt==NULL)
+ Crypt=new CryptData;
+ memcpy(this->Salt,Salt,sizeof(this->Salt));
+ memcpy(this->InitV,InitV,sizeof(this->InitV));
+ this->Lg2Count=Lg2Count;
+ memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
+ }
+ bool IsSet() {return Crypt!=NULL;}
+
+ // RAR5 provides the higly reliable 64 bit password verification value.
+ CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
+
+ bool Check(SecPassword *Password)
+ {
+ byte PswCheck[SIZE_PSWCHECK];
+ Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
+ return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
+ }
+};
+
void GetRnd(byte *RndBuf,size_t BufSize);
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
diff --git a/libclamunrar/crypt3.cpp b/libclamunrar/crypt3.cpp
index fe3bf97b80..e6e3a82cbb 100644
--- a/libclamunrar/crypt3.cpp
+++ b/libclamunrar/crypt3.cpp
@@ -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);
diff --git a/libclamunrar/crypt5.cpp b/libclamunrar/crypt5.cpp
index 7562469f33..5ed65af811 100644
--- a/libclamunrar/crypt5.cpp
+++ b/libclamunrar/crypt5.cpp
@@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
sha256_context ICtx;
if (ICtxOpt!=NULL && *SetIOpt)
- ICtx=*ICtxOpt; // Use already calculated first block context.
+ ICtx=*ICtxOpt; // Use already calculated the first block context.
else
{
// This calculation is the same for all iterations with same password.
@@ -90,10 +90,10 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
byte SaltData[MaxSalt+4];
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
- SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
- SaltData[SaltLength + 1] = 0;
- SaltData[SaltLength + 2] = 0;
- SaltData[SaltLength + 3] = 1;
+ SaltData[SaltLength + 0] = 0; // Block index appened to salt.
+ SaltData[SaltLength + 1] = 0; //
+ SaltData[SaltLength + 2] = 0; // Since we do not request the key width
+ SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
// First iteration: HMAC of password, salt and block index (1).
byte U1[SHA256_DIGEST_SIZE];
@@ -140,7 +140,7 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password &&
+ if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{
memcpy(Key,Item->Key,sizeof(Key));
diff --git a/libclamunrar/dll.rc b/libclamunrar/dll.rc
index 7c399f23a4..3ca50eb1f7 100644
--- a/libclamunrar/dll.rc
+++ b/libclamunrar/dll.rc
@@ -2,8 +2,8 @@
#include
VS_VERSION_INFO VERSIONINFO
-FILEVERSION 6, 12, 100, 489
-PRODUCTVERSION 6, 12, 100, 489
+FILEVERSION 6, 23, 100, 944
+PRODUCTVERSION 6, 23, 100, 944
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
@@ -14,9 +14,9 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
- VALUE "FileVersion", "6.12.0\0"
- VALUE "ProductVersion", "6.12.0\0"
- VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0"
+ VALUE "FileVersion", "6.23.0\0"
+ VALUE "ProductVersion", "6.23.0\0"
+ VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
}
diff --git a/libclamunrar/errhnd.cpp b/libclamunrar/errhnd.cpp
index d473b1f898..97193e5ac2 100644
--- a/libclamunrar/errhnd.cpp
+++ b/libclamunrar/errhnd.cpp
@@ -169,10 +169,13 @@ void ErrorHandler::OpenErrorMsg(const wchar *FileName)
void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName)
{
- Wait(); // Keep GUI responsive if many files cannot be opened when archiving.
uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_OPEN);
+
+ // Keep GUI responsive if many files cannot be opened when archiving.
+ // Call after SysErrMsg to avoid modifying the error code and SysErrMsg text.
+ Wait();
}
diff --git a/libclamunrar/extinfo.cpp b/libclamunrar/extinfo.cpp
index 5cb90a4080..4bf8046a74 100644
--- a/libclamunrar/extinfo.cpp
+++ b/libclamunrar/extinfo.cpp
@@ -21,17 +21,11 @@
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{
+#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
-#ifdef _UNIX
- case UO_HEAD:
- if (Cmd->ProcessOwners)
- ExtractUnixOwner20(Arc,Name);
- break;
-#endif
-#ifdef _WIN_ALL
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
@@ -39,8 +33,8 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
case STREAM_HEAD:
ExtractStreams20(Arc,Name);
break;
-#endif
}
+#endif
}
#endif
@@ -112,6 +106,68 @@ static bool LinkInPath(const wchar *Name)
}
+// Delete symbolic links in file path, if any, and replace them by directories.
+// Prevents extracting files outside of destination folder with symlink chains.
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked)
+{
+ // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
+ // "lnk1/../dir", but converts the path to "dir". In Unix we need to call
+ // this function to prevent placing unpacked files outside of destination
+ // folder if previously we unpacked "dir/lnk1" -> "..",
+ // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
+ // We may still need this function to prevent abusing symlink chains
+ // in link source path if we remove detection of such chains
+ // in IsRelativeSymlinkSafe. This function seems to make other symlink
+ // related safety checks redundant, but for now we prefer to keep them too.
+ //
+ // 2022.12.01: the performance impact is minimized after adding the check
+ // against the previous path and enabling this verification only after
+ // extracting a symlink with ".." in target. So we enabled it for Windows
+ // as well for extra safety.
+//#ifdef _UNIX
+ wchar Path[NM];
+ if (wcslen(SrcName)>=ASIZE(Path))
+ return false; // It should not be that long, skip.
+ wcsncpyz(Path,SrcName,ASIZE(Path));
+
+ size_t SkipLength=wcslen(SkipPart);
+
+ if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0)
+ SkipLength=0; // Parameter validation, not really needed now.
+
+ // Do not check parts already checked in previous path to improve performance.
+ for (uint I=0;Path[I]!=0 && ISkipLength)
+ SkipLength=I;
+
+ wchar *Name=Path;
+ if (SkipLength>0)
+ {
+ // Avoid converting symlinks in destination path part specified by user.
+ Name+=SkipLength;
+ while (IsPathDiv(*Name))
+ Name++;
+ }
+
+ for (wchar *s=Path+wcslen(Path)-1;s>Name;s--)
+ if (IsPathDiv(*s))
+ {
+ *s=0;
+ FindData FD;
+ if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
+#ifdef _WIN_ALL
+ if (!DelDir(Path))
+#else
+ if (!DelFile(Path))
+#endif
+ return false; // Couldn't delete the symlink to replace it with directory.
+ }
+ LastChecked=SrcName;
+//#endif
+ return true;
+}
+
+
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
@@ -131,10 +187,14 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
UpLevels++;
TargetName++;
}
- // If link target includes "..", it must not have another links
- // in the path, because they can bypass our safety check. For example,
+ // If link target includes "..", it must not have another links in its
+ // source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
- // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
+ // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
+ // file "dir/lnk1/lnk2/poc.txt" last.
+ // Do not confuse with link chains in target, this is in link source path.
+ // It is important for Windows too, though this check can be omitted
+ // if LinksToDirs is invoked in Windows as well.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;
@@ -160,15 +220,26 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
}
-bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink)
{
+ // Returning true in Uplink indicates that link target might include ".."
+ // and enables additional checks. It is ok to falsely return true here,
+ // as it implies only the minor performance penalty. But we shall always
+ // return true for links with ".." in target for security reason.
+
+ UpLink=true; // Assume the target might include potentially unsafe "..".
+#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
+ if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
+ UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL;
+#endif
+
#if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15)
- return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
+ return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
-#elif defined _WIN_ALL
+#elif defined(_WIN_ALL)
// RAR 5.0 archives store link information in file header, so there is
// no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50)
diff --git a/libclamunrar/extinfo.hpp b/libclamunrar/extinfo.hpp
index f3c7511b48..d8551d463d 100644
--- a/libclamunrar/extinfo.hpp
+++ b/libclamunrar/extinfo.hpp
@@ -1,8 +1,9 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked);
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
-bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const wchar *FileName);
#endif
diff --git a/libclamunrar/extract.cpp b/libclamunrar/extract.cpp
index dc109b9d90..4b746fc020 100644
--- a/libclamunrar/extract.cpp
+++ b/libclamunrar/extract.cpp
@@ -5,10 +5,30 @@ CmdExtract::CmdExtract(CommandData *Cmd)
CmdExtract::Cmd=Cmd;
*ArcName=0;
-
*DestFileName=0;
+ ArcAnalyzed=false;
+ Analyze=new AnalyzeData;
+ memset(Analyze,0,sizeof(*Analyze));
+
TotalFileCount=0;
+
+ // Common for all archives involved. Set here instead of DoExtract()
+ // to use in unrar.dll too. Allows to avoid LinksToDirs() calls
+ // and save CPU time in no symlinks including ".." in target were extracted.
+#if defined(_WIN_ALL)
+ // We can't expand symlink path components in another symlink target
+ // in Windows. We can't create symlinks in Android now. Even though we do not
+ // really need LinksToDirs() calls in these systems, we still call it
+ // for extra safety, but only if symlink with ".." in target was extracted.
+ ConvertSymlinkPaths=false;
+#else
+ // We enable it by default in Unix to care about the case when several
+ // archives are unpacked to same directory with several independent RAR runs.
+ // Worst case performance penalty for a lot of small files seems to be ~3%.
+ ConvertSymlinkPaths=true;
+#endif
+
Unp=new Unpack(&DataIO);
#ifdef RAR_SMP
Unp->SetThreads(Cmd->Threads);
@@ -18,7 +38,26 @@ CmdExtract::CmdExtract(CommandData *Cmd)
CmdExtract::~CmdExtract()
{
+ FreeAnalyzeData();
delete Unp;
+ delete Analyze;
+}
+
+
+void CmdExtract::FreeAnalyzeData()
+{
+ for (size_t I=0;ICommand[0]);
- FindData FD;
- while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
- if (FindFile::FastFind(ArcName,&FD))
- DataIO.TotalArcSize+=FD.Size;
+ if (*Cmd->UseStdin==0)
+ {
+ FindData FD;
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ if (FindFile::FastFind(ArcName,&FD))
+ DataIO.TotalArcSize+=FD.Size;
+ }
Cmd->ArcNames.Rewind();
while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
{
if (Cmd->ManualPassword)
Cmd->Password.Clean(); // Clean user entered password before processing next archive.
-
+
ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit().
UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit().
while (true)
@@ -56,7 +98,7 @@ void CmdExtract::DoExtract()
if (Cmd->ManualPassword)
Cmd->Password.Clean();
- if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
+ if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
{
if (!PasswordCancelled)
@@ -97,7 +139,11 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
AllMatchesExact=true;
AnySolidDataUnpackedWell=false;
+ ArcAnalyzed=false;
+
StartTime.SetCurrentTime();
+
+ LastCheckedSymlink.clear();
}
@@ -168,15 +214,28 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
}
#endif
+ Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT.
+
int64 VolumeSetSize=0; // Total size of volumes after the current volume.
+#ifndef SFX_MODULE
+ if (!ArcAnalyzed && *Cmd->UseStdin==0)
+ {
+ AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering);
+ ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT.
+ }
+#endif
+
if (Arc.Volume)
{
#ifndef SFX_MODULE
// Try to speed up extraction for independent solid volumes by starting
// extraction from non-first volume if we can.
- if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering))
+ if (*Analyze->StartName!=0)
{
+ wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName));
+ *Analyze->StartName=0;
+
UseExactVolName=true;
return EXTRACT_ARC_REPEAT;
}
@@ -190,7 +249,7 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
while (true)
{
- // First volume is already added to DataIO.TotalArcSize
+ // First volume is already added to DataIO.TotalArcSize
// in initial TotalArcSize calculation in DoExtract.
// So we skip it and start from second volume.
NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
@@ -216,7 +275,13 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
else
uiStartArchiveExtract(!Cmd->Test,ArcName);
- Arc.ViewComment();
+#ifndef SFX_MODULE
+ if (Analyze->StartPos!=0)
+ {
+ Arc.Seek(Analyze->StartPos,SEEK_SET);
+ Analyze->StartPos=0;
+ }
+#endif
while (1)
@@ -272,7 +337,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
return false;
HEADER_TYPE HeaderType=Arc.GetHeaderType();
- if (HeaderType!=HEAD_FILE)
+ if (HeaderType==HEAD_FILE)
+ {
+ // Unlike Arc.FileName, ArcName might store an old volume name here.
+ if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos &&
+ (*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0))
+ return false;
+ }
+ else
{
#ifndef SFX_MODULE
if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
@@ -315,6 +387,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Arc.FileHead.UnpSize<0)
Arc.FileHead.UnpSize=0;
+ // 2022.03.20: We might remove this check in the future.
+ // It duplicates Analyze->EndPos and Analyze->EndName in all cases except
+ // volumes on removable media.
if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
return false;
@@ -413,13 +488,39 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
FirstFile=false;
#endif
+ bool RefTarget=false;
+ if (!MatchFound)
+ for (size_t I=0;ITest) // While harmless, it is useless for 't'.
+ {
+ // If reference source isn't selected, but target is selected,
+ // we unpack the source under the temporary name and then rename
+ // or copy it to target name. We do not unpack it under the target
+ // name immediately, because the same source can be used by multiple
+ // targets and it is possible that first target isn't unpacked
+ // for some reason. Also targets might have associated service blocks
+ // like ACLs. All this would complicate processing a lot.
+ wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName));
+ AddEndSlash(DestFileName,ASIZE(DestFileName));
+ wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName));
+ MkTemp(DestFileName,ASIZE(DestFileName));
+ MatchedRef->TmpName=wcsdup(DestFileName);
+ }
+ RefTarget=true; // Need it even for 't' to test the reference source.
+ break;
+ }
+
if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted)
if (Arc.Solid)
return false; // Abort the entire extraction for solid archive.
else
MatchFound=false; // Skip only the current file for non-solid archive.
-
- if (MatchFound || (SkipSolid=Arc.Solid)!=0)
+
+ if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0)
{
// First common call of uiStartFileExtract. It is done before overwrite
// prompts, so if SkipSolid state is changed below, we'll need to make
@@ -427,7 +528,8 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
return false;
- ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
+ if (!RefTarget)
+ ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
// DestFileName can be set empty in case of excessive -ap switch.
ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
@@ -439,10 +541,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
{
if (FD.mtime >= Arc.FileHead.mtime)
{
- // If directory already exists and its modification time is newer
- // than start of extraction, it is likely it was created
- // when creating a path to one of already extracted items.
- // In such case we'll better update its time even if archived
+ // If directory already exists and its modification time is newer
+ // than start of extraction, it is likely it was created
+ // when creating a path to one of already extracted items.
+ // In such case we'll better update its time even if archived
// directory is older.
if (!FD.IsDir || FD.mtimePassword;
-#if defined(_WIN_ALL) && !defined(SFX_MODULE)
- ConvertDosPassword(Arc,FilePassword);
-#endif
- byte PswCheck[SIZE_PSWCHECK];
- DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
- Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
- Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
- Arc.FileHead.HashKey,PswCheck);
-
- // If header is damaged, we cannot rely on password check value,
- // because it can be damaged too.
- if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
- memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
- !Arc.BrokenHeader)
- {
- if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop.
- {
- // This message is used by Android GUI to reset cached passwords.
- // Update appropriate code if changed.
- uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
- }
- else // For passwords entered manually.
+ // Set a password before creating the file, so we can skip creating
+ // in case of wrong password.
+ SecPassword FilePassword=Cmd->Password;
+ #if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ConvertDosPassword(Arc,FilePassword);
+ #endif
+
+ byte PswCheck[SIZE_PSWCHECK];
+ DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
+ Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
+ Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
+ Arc.FileHead.HashKey,PswCheck);
+
+ // If header is damaged, we cannot rely on password check value,
+ // because it can be damaged too.
+ if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader &&
+ memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0)
{
- // This message is used by Android GUI and Windows GUI and SFX to
- // reset cached passwords. Update appropriate code if changed.
- uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
- Cmd->Password.Clean();
-
- // Avoid new requests for unrar.dll to prevent the infinite loop
- // if app always returns the same password.
-#ifndef RARDLL
- continue; // Request a password again.
-#endif
+ if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop.
+ {
+ // This message is used by Android GUI to reset cached passwords.
+ // Update appropriate code if changed.
+ uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
+ }
+ else // For passwords entered manually.
+ {
+ // This message is used by Android GUI and Windows GUI and SFX to
+ // reset cached passwords. Update appropriate code if changed.
+ uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
+ Cmd->Password.Clean();
+
+ // Avoid new requests for unrar.dll to prevent the infinite loop
+ // if app always returns the same password.
+ #ifndef RARDLL
+ continue; // Request a password again.
+ #endif
+ }
+ #ifdef RARDLL
+ // If we already have ERAR_EOPEN as result of missing volume,
+ // we should not replace it with less precise ERAR_BAD_PASSWORD.
+ if (Cmd->DllError!=ERAR_EOPEN)
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+ #endif
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ ExtrFile=false;
}
-#ifdef RARDLL
- // If we already have ERAR_EOPEN as result of missing volume,
- // we should not replace it with less precise ERAR_BAD_PASSWORD.
- if (Cmd->DllError!=ERAR_EOPEN)
- Cmd->DllError=ERAR_BAD_PASSWORD;
-#endif
- ErrHandler.SetErrorCode(RARX_BADPWD);
- ExtrFile=false;
+ break;
}
- break;
}
+ else
+ DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL);
#ifdef RARDLL
if (*Cmd->DllDestName!=0)
@@ -542,11 +649,12 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
File CurFile;
bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
- if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
+ if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY))
{
if (ExtrFile && Command!='P' && !Cmd->Test)
{
- // Overwrite prompt for symbolic and hard links.
+ // Overwrite prompt for symbolic and hard links and when we move
+ // a temporary file to the file reference instead of copying it.
bool UserReject=false;
if (FileExist(DestFileName) && !UserReject)
FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
@@ -576,7 +684,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
ExtrFile=true;
// We changed SkipSolid, so we need to call uiStartFileExtract
- // with "Skip" parameter to change the operation status
+ // with "Skip" parameter to change the operation status
// from "extracting" to "skipping". For example, it can be necessary
// if user answered "No" to overwrite prompt when unpacking
// a solid archive.
@@ -667,7 +775,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
{
wchar RedirName[NM];
- ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
+
+ // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with
+ // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning
+ // from 5.10 even Windows version uses / internally and converts
+ // them to \ when reading FHEXTRA_REDIR.
+ // We must perform this conversion before ConvertPath call,
+ // so paths mixing different slashes like \dir1/dir2\file are
+ // processed correctly.
+ SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
+
+ ConvertPath(RedirName,RedirName,ASIZE(RedirName));
wchar NameExisting[NM];
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
@@ -675,20 +793,40 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_HARDLINK)
LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting));
else
- LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
+ LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize);
}
else
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
{
if (FileCreateMode)
- LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
+ {
+ bool UpLink;
+ LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink);
+
+ // Unix symlink can have its own owner data.
+ if (LinkSuccess)
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+
+ ConvertSymlinkPaths|=LinkSuccess && UpLink;
+
+ // We do not actually need to reset the cache here if we cache
+ // only the single last checked path, because at this point
+ // it will always contain the link own path and link can't
+ // overwrite its parent folder. But if we ever decide to cache
+ // several already checked paths, we'll need to reset them here.
+ // Otherwise if no files were created in one of such paths,
+ // let's say because of file create error, it might be possible
+ // to overwrite the path with link and avoid checks. We keep this
+ // code here as a reminder in case of possible modifications.
+ LastCheckedSymlink.clear(); // Reset cache for safety reason.
+ }
}
else
{
- uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName);
+ uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName);
LinkSuccess=false;
}
-
+
if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
{
// RAR 5.x links have a valid data checksum even in case of
@@ -709,6 +847,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE
+ // RAR 1.3 - 1.5 archives do not set per file solid flag.
if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
else
@@ -735,9 +874,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
else
if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
AnySolidDataUnpackedWell=true;
-
+
bool BrokenFile=false;
-
+
// Checksum is not calculated in skip solid mode for performance reason.
if (!SkipSolid && ShowChecksum)
{
@@ -749,7 +888,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
}
else
{
- if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
+ if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
else
@@ -770,7 +909,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
// We check SkipSolid to remove percent for skipped solid files only.
// We must not apply these \b to links with ShowChecksum==false
// and their possible error messages.
- if (SkipSolid)
+ if (SkipSolid)
mprintf(L"\b\b\b\b\b ");
}
@@ -783,7 +922,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess;
if (!TestMode && (Command=='X' || Command=='E') &&
- (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
+ (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
(!BrokenFile || Cmd->KeepBroken))
{
// Below we use DestFileName instead of CurFile.FileName,
@@ -810,7 +949,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
}
-
+
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
if (Cmd->SetCompressedAttr &&
(Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
@@ -866,22 +1005,64 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
}
-bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize)
{
- SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
-
File Existing;
- if (!Existing.WOpen(NameExisting))
+ if (!Existing.Open(NameExisting))
{
- uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
- uiMsg(UIERROR_FILECOPYHINT,ArcName);
+ bool OpenFailed=true;
+ // If we couldn't find the existing file, check if match is present
+ // in temporary reference sources list.
+ for (size_t I=0;IDllError=ERAR_EREFERENCE;
+ Cmd->DllError=ERAR_EREFERENCE;
#endif
- return false;
+ return false;
+ }
}
- Array Buffer(0x100000);
+ Array Buffer(0x100000);
int64 CopySize=0;
while (true)
@@ -890,6 +1071,10 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *
int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
if (ReadSize==0)
break;
+ // Update only the current file progress in WinRAR, set the total to 0
+ // to keep it as is. It looks better for WinRAR.
+ uiExtractProgress(CopySize,UnpSize,0,0);
+
New.Write(&Buffer[0],ReadSize);
CopySize+=ReadSize;
}
@@ -900,6 +1085,16 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *
void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
{
+ if (Cmd->Test)
+ {
+ // Destination name conversion isn't needed for simple archive test.
+ // This check also allows to avoid issuing "Attempting to correct...
+ // Renaming..." messages in MakeNameCompatible() below for problematic
+ // names like aux.txt when testing an archive.
+ wcsncpyz(DestName,ArcFileName,DestSize);
+ return;
+ }
+
wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
if (*Cmd->ExtrPath!=0)
@@ -946,7 +1141,7 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De
{
size_t NameLength=wcslen(ArcFileName);
if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 &&
- (IsPathDiv(ArcPath[ArcPathLength-1]) ||
+ (IsPathDiv(ArcPath[ArcPathLength-1]) ||
IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0))
{
ArcFileName+=Min(ArcPathLength,NameLength);
@@ -1033,11 +1228,11 @@ bool CmdExtract::ExtrDllGetPassword()
#ifndef RARDLL
-bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
+bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd)
{
if (!Cmd->Password.IsSet())
{
- if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/)
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !Cmd->Password.IsSet()*/)
{
// Suppress "test is ok" message if user cancelled the password prompt.
uiMsg(UIERROR_INCERRCOUNT);
@@ -1055,7 +1250,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
case -1:
ErrHandler.Exit(RARX_USERBREAK);
case 2:
- if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password))
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd))
return false;
break;
case 3:
@@ -1131,6 +1326,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
if (!DirExist)
{
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames);
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
}
@@ -1212,6 +1409,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
MakeNameUsable(DestFileName,true);
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames);
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
{
@@ -1258,31 +1457,59 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
#ifndef SFX_MODULE
-// To speed up solid volumes extraction, try to find a non-first start volume,
-// which still allows to unpack all files. It is possible for independent
-// solid volumes with solid statistics reset in the beginning.
-bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
+// Find non-matched reference sources in solid and non-solid archives.
+// Detect the optimal start position for semi-solid archives
+// and optimal start volume for independent solid volumes.
+//
+// Alternatively we could collect references while extracting an archive
+// and perform the second extraction pass for references only.
+// But it would be slower for solid archives than scaning headers
+// in first pass and extracting everything in second, as implemented now.
+//
+void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering)
{
+ FreeAnalyzeData(); // If processing non-first archive in multiple archives set.
+
wchar *ArgName=Cmd->FileArgs.GetString();
Cmd->FileArgs.Rewind();
if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0))
- return false; // No need to check further for * and *.* masks.
+ return; // No need to check further for * and *.* masks.
- wchar StartName[NM];
- *StartName=0;
-
// Start search from first volume if all volumes preceding current are available.
wchar NextName[NM];
- GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName));
+ if (Volume)
+ GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName));
+ else
+ wcsncpyz(NextName,ArcName,ASIZE(NextName));
- bool Matched=false;
- while (!Matched)
+ bool MatchFound=false;
+ bool PrevMatched=false;
+ bool OpenNext=false;
+
+ bool FirstVolume=true;
+
+ // We shall set FirstFile once for all volumes and not for each volume.
+ // So we do not reuse the outdated Analyze->StartPos from previous volume
+ // if extracted file resides completely in the beginning of current one.
+ bool FirstFile=true;
+
+ while (true)
{
Archive Arc(Cmd);
- if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume)
+ if (!Arc.Open(NextName) || !Arc.IsArchive(false))
+ {
+ if (OpenNext)
+ {
+ // If we couldn't open trailing volumes, we can't set early exit
+ // parameters. It is possible that some volume are on removable media
+ // and will be provided by user when extracting.
+ *Analyze->EndName=0;
+ Analyze->EndPos=0;
+ }
break;
+ }
- bool OpenNext=false;
+ OpenNext=false;
while (Arc.ReadHeader()>0)
{
Wait();
@@ -1295,17 +1522,88 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
}
if (HeaderType==HEAD_FILE)
{
+ if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15)
+ {
+ // RAR versions earlier than 2.0 do not set per file solid flag.
+ // They have only the global archive solid flag, so we can't
+ // reliably analyze them here.
+ OpenNext=false;
+ break;
+ }
+
if (!Arc.FileHead.SplitBefore)
{
- if (!Arc.FileHead.Solid) // Can start extraction from here.
- wcsncpyz(StartName,NextName,ASIZE(StartName));
+ if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here.
+ {
+ // We would gain nothing and unnecessarily complicate extraction
+ // if we set StartName for first volume or StartPos for first
+ // archived file.
+ if (!FirstVolume)
+ wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName));
+
+ // We shall set FirstFile once for all volumes for this code
+ // to work properly. Alternatively we could append
+ // "|| Analyze->StartPos!=0" to the condition, so we do not reuse
+ // the outdated Analyze->StartPos value from previous volume.
+ if (!FirstFile)
+ Analyze->StartPos=Arc.CurBlockPos;
+ }
if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0)
{
- Matched=true; // First matched file found, must stop further scan.
- break;
+ MatchFound = true;
+ PrevMatched = true;
+
+ // Reset the previously set early exit position, if any, because
+ // we found a new matched file.
+ Analyze->EndPos=0;
+
+ // Matched file reference pointing at maybe non-matched source file.
+ // Even though we know RedirName, we can't check if source file
+ // is certainly non-matched, because it can be filtered out by
+ // date or attributes, which we do not know here.
+ if (Arc.FileHead.RedirType==FSREDIR_FILECOPY)
+ {
+ bool AlreadyAdded=false;
+ for (size_t I=0;IEndName,NextName,ASIZE(Analyze->EndName));
+ Analyze->EndPos=Arc.CurBlockPos;
+ }
+ PrevMatched=false;
}
}
+
+ FirstFile=false;
if (Arc.FileHead.SplitAfter)
{
OpenNext=true; // Allow open next volume.
@@ -1316,16 +1614,25 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
}
Arc.Close();
- if (!OpenNext)
- break;
+ if (Volume && OpenNext)
+ {
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ FirstVolume=false;
- NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ // Needed for multivolume archives. Added in case some 'break'
+ // will quit early from loop above, so we do not set it in the loop.
+ // Now it can happen for hypothetical archive without file records
+ // and with HEAD_ENDARC record.
+ FirstFile=false;
+ }
+ else
+ break;
}
- bool NewStartFound=wcscmp(VolName,StartName)!=0;
- if (NewStartFound) // Found a new volume to start extraction.
- wcsncpyz(ArcName,StartName,ASIZE(ArcName));
-
- return NewStartFound;
+
+ // If file references are present, we can't reliably skip in semi-solid
+ // archives, because reference source can be present in skipped data.
+ if (RefList.Size()!=0)
+ memset(Analyze,0,sizeof(*Analyze));
}
#endif
diff --git a/libclamunrar/extract.hpp b/libclamunrar/extract.hpp
index 159759b563..18396c5b93 100644
--- a/libclamunrar/extract.hpp
+++ b/libclamunrar/extract.hpp
@@ -6,13 +6,32 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
+ struct ExtractRef
+ {
+ wchar *RefName;
+ wchar *TmpName;
+ uint64 RefCount;
+ };
+ Array RefList;
+
+ struct AnalyzeData
+ {
+ wchar StartName[NM];
+ uint64 StartPos;
+ wchar EndName[NM];
+ uint64 EndPos;
+ } *Analyze;
+
+ bool ArcAnalyzed;
+
+ void FreeAnalyzeData();
EXTRACT_ARC_CODE ExtractArchive();
- bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+ bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize);
void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
#ifdef RARDLL
bool ExtrDllGetPassword();
#else
- bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
+ bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
@@ -21,7 +40,7 @@ class CmdExtract
bool ExtrCreateFile(Archive &Arc,File &CurFile);
bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
#ifndef SFX_MODULE
- bool DetectStartVolume(const wchar *VolName,bool NewNumbering);
+ void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering);
void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize);
#endif
@@ -52,6 +71,15 @@ class CmdExtract
bool PrevProcessed; // If previous file was successfully extracted or tested.
wchar DestFileName[NM];
bool PasswordCancelled;
+
+ // In Windows it is set to true if at least one symlink with ".."
+ // in target was extracted.
+ bool ConvertSymlinkPaths;
+
+ // Last path checked for symlinks. We use it to improve the performance,
+ // so we do not check recently checked folders again.
+ std::wstring LastCheckedSymlink;
+
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif
diff --git a/libclamunrar/filcreat.cpp b/libclamunrar/filcreat.cpp
index 8e7562cb6f..d58e4f6fe9 100644
--- a/libclamunrar/filcreat.cpp
+++ b/libclamunrar/filcreat.cpp
@@ -3,7 +3,7 @@
// If NewFile==NULL, we delete created file after user confirmation.
// It is useful if we need to overwrite an existing folder or file,
// but need user confirmation for that.
-bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
diff --git a/libclamunrar/filcreat.hpp b/libclamunrar/filcreat.hpp
index 44f801d4ee..456a4a4a13 100644
--- a/libclamunrar/filcreat.hpp
+++ b/libclamunrar/filcreat.hpp
@@ -1,7 +1,7 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
-bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool *UserReject,int64 FileSize=INT64NDF,
RarTime *FileTime=NULL,bool WriteOnly=false);
diff --git a/libclamunrar/file.cpp b/libclamunrar/file.cpp
index e7b584da6b..7bf60fd4ef 100644
--- a/libclamunrar/file.cpp
+++ b/libclamunrar/file.cpp
@@ -522,29 +522,35 @@ bool File::RawSeek(int64 Offset,int Method)
{
if (hFile==FILE_BAD_HANDLE)
return true;
- if (!IsSeekable())
+ if (!IsSeekable()) // To extract archives from stdin with -si.
{
- if (Method==SEEK_CUR)
- {
- Offset+=CurFilePos;
- Method=SEEK_SET;
- }
- if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward.
+ // We tried to dynamically allocate 32 KB buffer here, but it improved
+ // speed in Windows 10 by mere ~1.5%.
+ byte Buf[4096];
+ if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
{
- uint64 SkipSize=Offset-CurFilePos;
- while (SkipSize>0)
+ uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
+ while (SkipSize>0) // Reading to emulate seek forward.
{
- byte Buf[4096];
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
if (ReadSize<=0)
return false;
SkipSize-=ReadSize;
+ CurFilePos+=ReadSize;
}
- CurFilePos=Offset;
+ return true;
+ }
+ // May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking
+ // RAR 4.x archives without the end of archive block created with -en.
+ if (Method==SEEK_END)
+ {
+ int ReadSize;
+ while ((ReadSize=Read(Buf,ASIZE(Buf)))>0)
+ CurFilePos+=ReadSize;
return true;
}
- return false; // Backward or end of file seek on unseekable file.
+ return false; // Backward seek on unseekable file.
}
if (Offset<0 && Method!=SEEK_SET)
{
@@ -732,17 +738,40 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
}
-void File::GetOpenFileTime(RarTime *ft)
+#ifdef _UNIX
+void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_ALL
- FILETIME FileTime;
- GetFileTime(hFile,NULL,NULL,&FileTime);
- ft->SetWinFT(&FileTime);
+#ifdef UNIX_TIME_NS
+#if defined(_APPLE)
+ if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
+ if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
+ if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
+#else
+ if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
+ if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
+ if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
+#endif
+#else
+ if (ftm!=NULL) ftm->SetUnix(st.st_mtime);
+ if (ftc!=NULL) ftc->SetUnix(st.st_ctime);
+ if (fta!=NULL) fta->SetUnix(st.st_atime);
+#endif
+}
#endif
-#if defined(_UNIX) || defined(_EMX)
+
+
+void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
+{
+#ifdef _WIN_ALL
+ FILETIME ctime,atime,mtime;
+ GetFileTime(hFile,&ctime,&atime,&mtime);
+ if (ftm!=NULL) ftm->SetWinFT(&mtime);
+ if (ftc!=NULL) ftc->SetWinFT(&ctime);
+ if (fta!=NULL) fta->SetWinFT(&atime);
+#elif defined(_UNIX)
struct stat st;
fstat(GetFD(),&st);
- ft->SetUnix(st.st_mtime);
+ StatToRarTime(st,ftm,ftc,fta);
#endif
}
diff --git a/libclamunrar/file.hpp b/libclamunrar/file.hpp
index 1de5fe6df3..5f55de960e 100644
--- a/libclamunrar/file.hpp
+++ b/libclamunrar/file.hpp
@@ -14,8 +14,6 @@
#define FILE_BAD_HANDLE NULL
#endif
-class RAROptions;
-
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
@@ -88,6 +86,9 @@ class File
wchar FileName[NM];
FILE_ERRORTYPE ErrorType;
+
+ byte *SeekBuf; // To read instead of seek for stdin files.
+ static const size_t SeekBufSize=0x10000;
public:
File();
virtual ~File();
@@ -118,7 +119,10 @@ class File
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
- void GetOpenFileTime(RarTime *ft);
+#ifdef _UNIX
+ static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta);
+#endif
+ void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
int64 FileLength();
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
diff --git a/libclamunrar/filefn.cpp b/libclamunrar/filefn.cpp
index 883c26e2bc..aaef305b8e 100644
--- a/libclamunrar/filefn.cpp
+++ b/libclamunrar/filefn.cpp
@@ -320,7 +320,6 @@ bool SetFileAttr(const wchar *Name,uint Attr)
}
-#if 0
wchar *MkTemp(wchar *Name,size_t MaxSize)
{
size_t Length=wcslen(Name);
@@ -354,7 +353,6 @@ wchar *MkTemp(wchar *Name,size_t MaxSize)
}
return Name;
}
-#endif
#if !defined(SFX_MODULE)
@@ -399,7 +397,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
{
// Update only the current file progress in WinRAR, set the total to 0
- // to keep it as is. It looks better for WinRAR,
+ // to keep it as is. It looks better for WinRAR.
uiExtractProgress(TotalRead,FileLength,0,0);
}
else
diff --git a/libclamunrar/filefn.hpp b/libclamunrar/filefn.hpp
index 36dc703953..53d86653fe 100644
--- a/libclamunrar/filefn.hpp
+++ b/libclamunrar/filefn.hpp
@@ -27,9 +27,7 @@ bool IsDeleteAllowed(uint FileAttr);
void PrepareToDelete(const wchar *Name);
uint GetFileAttr(const wchar *Name);
bool SetFileAttr(const wchar *Name,uint Attr);
-#if 0
wchar* MkTemp(wchar *Name,size_t MaxSize);
-#endif
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
diff --git a/libclamunrar/find.cpp b/libclamunrar/find.cpp
index 04702ab87e..c9f2c5768c 100644
--- a/libclamunrar/find.cpp
+++ b/libclamunrar/find.cpp
@@ -117,7 +117,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
if (hFind==INVALID_HANDLE_VALUE)
return false;
FindClose(hFind);
-#else
+#elif defined(_UNIX)
char FindMaskA[NM];
WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
@@ -143,21 +143,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
fd->FileAttr=st.st_mode;
fd->Size=st.st_size;
-#ifdef UNIX_TIME_NS
-#if defined(_APPLE)
- fd->mtime.SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
- fd->atime.SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
- fd->ctime.SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
-#else
- fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
- fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
- fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
-#endif
-#else
- fd->mtime.SetUnix(st.st_mtime);
- fd->atime.SetUnix(st.st_atime);
- fd->ctime.SetUnix(st.st_ctime);
-#endif
+ File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
#endif
diff --git a/libclamunrar/getbits.cpp b/libclamunrar/getbits.cpp
index e4db2695fb..5d5ad2bb2c 100644
--- a/libclamunrar/getbits.cpp
+++ b/libclamunrar/getbits.cpp
@@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer)
ExternalBuffer=false;
if (AllocBuffer)
{
- // getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
- // So let's allocate 3 additional bytes for situation, when we need to
+ // getbits*() attempt to read data from InAddr, ... InAddr+4 positions.
+ // So let's allocate 4 additional bytes for situation, when we need to
// read only 1 byte from the last position of buffer and avoid a crash
- // from access to next 3 bytes, which contents we do not need.
- size_t BufSize=MAX_SIZE+3;
+ // from access to next 4 bytes, which contents we do not need.
+ size_t BufSize=MAX_SIZE+4;
InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area
diff --git a/libclamunrar/getbits.hpp b/libclamunrar/getbits.hpp
index 2e151da9a9..00acbea945 100644
--- a/libclamunrar/getbits.hpp
+++ b/libclamunrar/getbits.hpp
@@ -28,26 +28,38 @@ class BitInput
InAddr+=Bits>>3;
InBit=Bits&7;
}
-
+
// Return 16 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits()
{
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+ uint32 BitField=*(uint32*)(InBuf+InAddr);
+ BitField=ByteSwap32(BitField);
+ BitField >>= (16-InBit);
+#else
uint BitField=(uint)InBuf[InAddr] << 16;
BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit);
+#endif
return BitField & 0xffff;
}
+
// Return 32 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits32()
{
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+ uint32 BitField=*(uint32*)(InBuf+InAddr);
+ BitField=ByteSwap32(BitField);
+#else
uint BitField=(uint)InBuf[InAddr] << 24;
BitField|=(uint)InBuf[InAddr+1] << 16;
BitField|=(uint)InBuf[InAddr+2] << 8;
BitField|=(uint)InBuf[InAddr+3];
+#endif
BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
return BitField & 0xffffffff;
diff --git a/libclamunrar/hardlinks.cpp b/libclamunrar/hardlinks.cpp
index 40cc0aa495..171b5fa087 100644
--- a/libclamunrar/hardlinks.cpp
+++ b/libclamunrar/hardlinks.cpp
@@ -1,7 +1,5 @@
bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
{
- SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
-
if (!FileExist(NameExisting))
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
diff --git a/libclamunrar/hash.cpp b/libclamunrar/hash.cpp
index a4559e05c3..106cc60833 100644
--- a/libclamunrar/hash.cpp
+++ b/libclamunrar/hash.cpp
@@ -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;
diff --git a/libclamunrar/hash.hpp b/libclamunrar/hash.hpp
index b7d879f66a..6315680e78 100644
--- a/libclamunrar/hash.hpp
+++ b/libclamunrar/hash.hpp
@@ -6,8 +6,14 @@ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
struct HashValue
{
void Init(HASH_TYPE Type);
- bool operator == (const HashValue &cmp);
- bool operator != (const HashValue &cmp) {return !(*this==cmp);}
+
+ // Use the const member, so types on both sides of "==" match.
+ // Otherwise clang -std=c++20 issues "ambiguity is between a regular call
+ // to this operator and a call with the argument order reversed" warning.
+ bool operator == (const HashValue &cmp) const;
+
+ // Not actually used now. Const member for same reason as operator == above.
+ bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
HASH_TYPE Type;
union
diff --git a/libclamunrar/headers.cpp b/libclamunrar/headers.cpp
index b042dc3932..b441376c56 100644
--- a/libclamunrar/headers.cpp
+++ b/libclamunrar/headers.cpp
@@ -49,13 +49,5 @@ FileHeader& FileHeader::operator = (FileHeader &hd)
void MainHeader::Reset()
{
- HighPosAV=0;
- PosAV=0;
- CommentInHeader=false;
- PackComment=false;
- Locator=false;
- QOpenOffset=0;
- QOpenMaxSize=0;
- RROffset=0;
- RRMaxSize=0;
+ *this={};
}
diff --git a/libclamunrar/headers.hpp b/libclamunrar/headers.hpp
index 6af453a9d1..5984f99629 100644
--- a/libclamunrar/headers.hpp
+++ b/libclamunrar/headers.hpp
@@ -6,12 +6,11 @@
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
-#define SIZEOF_SHORTBLOCKHEAD 7
+#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size.
#define SIZEOF_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26
-#define SIZEOF_UOHEAD 18
#define SIZEOF_STREAMHEAD 26
#define VER_PACK 29U
@@ -162,12 +161,16 @@ struct MainHeader:BaseBlock
ushort HighPosAV;
uint PosAV;
bool CommentInHeader;
- bool PackComment; // For RAR 1.4 archive format only.
+ bool PackComment; // For RAR 1.4 archive format only.
bool Locator;
- uint64 QOpenOffset; // Offset of quick list record.
- uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
- uint64 RROffset; // Offset of recovery record.
- uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
+ uint64 QOpenOffset; // Offset of quick list record.
+ uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
+ uint64 RROffset; // Offset of recovery record.
+ uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
+ size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
+ std::wstring OrigName; // Original archive name.
+ RarTime OrigTime; // Original archive time.
+
void Reset();
};
@@ -230,7 +233,7 @@ struct FileHeader:BlockHeader
bool LargeFile;
// 'true' for HEAD_SERVICE block, which is a child of preceding file block.
- // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives.
+ // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
bool SubBlock;
HOST_SYSTEM_TYPE HSType;
@@ -321,16 +324,6 @@ struct ProtectHeader:BlockHeader
};
-struct UnixOwnersHeader:SubBlockHeader
-{
- ushort OwnerNameSize;
- ushort GroupNameSize;
-/* dummy */
- char OwnerName[256];
- char GroupName[256];
-};
-
-
struct EAHeader:SubBlockHeader
{
uint UnpSize;
diff --git a/libclamunrar/headers5.hpp b/libclamunrar/headers5.hpp
index 9ea8d979ae..50f5955d1d 100644
--- a/libclamunrar/headers5.hpp
+++ b/libclamunrar/headers5.hpp
@@ -59,11 +59,18 @@
// Main header extra field values.
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
+#define MHEXTRA_METADATA 0x02 // Archive metadata.
// Flags for MHEXTRA_LOCATOR.
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
+// Flags for MHEXTRA_METADATA.
+#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
+#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
+#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
+#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
+
// File and service header extra field values.
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
#define FHEXTRA_HASH 0x02 // File hash.
diff --git a/libclamunrar/isnt.cpp b/libclamunrar/isnt.cpp
index f85472c350..3cc876b990 100644
--- a/libclamunrar/isnt.cpp
+++ b/libclamunrar/isnt.cpp
@@ -40,7 +40,7 @@ static bool WMI_IsWindows10()
IWbemServices *pSvc = NULL;
- hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc);
+ hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc);
if (FAILED(hres))
{
diff --git a/libclamunrar/list.cpp b/libclamunrar/list.cpp
index e5052c0177..e4444e13ab 100644
--- a/libclamunrar/list.cpp
+++ b/libclamunrar/list.cpp
@@ -36,6 +36,7 @@ void ListArchive(CommandData *Cmd)
{
Arc.ViewComment();
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
+
mprintf(L"\n%s: ",St(MListDetails));
uint SetCount=0;
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
@@ -61,6 +62,16 @@ void ListArchive(CommandData *Cmd)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
if (Arc.Encrypted)
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead));
+
+ if (!Arc.MainHead.OrigName.empty())
+ mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str());
+ if (Arc.MainHead.OrigTime.IsSet())
+ {
+ wchar DateStr[50];
+ Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical);
+ mprintf(L"\n%s: %s",St(MOriginalTime),DateStr);
+ }
+
mprintf(L"\n");
}
diff --git a/libclamunrar/loclang.hpp b/libclamunrar/loclang.hpp
index ad7d0c9249..1e23ece76b 100644
--- a/libclamunrar/loclang.hpp
+++ b/libclamunrar/loclang.hpp
@@ -28,6 +28,7 @@
#define MRARTitle1 L"\nUsage: rar - - "
#define MUNRARTitle1 L"\nUsage: unrar - - "
#define MRARTitle2 L"\n <@listfiles...> "
+#define MFwrSlTitle2 L"\n <@listfiles...> "
#define MCHelpCmd L"\n\n"
#define MCHelpCmdA L"\n a Add files to archive"
#define MCHelpCmdC L"\n c Add archive comment"
@@ -44,7 +45,7 @@
#define MCHelpCmdR L"\n r Repair archive"
#define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
#define MCHelpCmdRN L"\n rn Rename archived files"
-#define MCHelpCmdRR L"\n rr[N] Add data recovery record"
+#define MCHelpCmdRR L"\n rr[N] Add the data recovery record"
#define MCHelpCmdRV L"\n rv[N] Create recovery volumes"
#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
#define MCHelpCmdT L"\n t Test archive files"
@@ -58,6 +59,7 @@
#define MCHelpSwAD L"\n ad[1,2] Alternate destination path"
#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
#define MCHelpSwAI L"\n ai Ignore file attributes"
+#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]"
#define MCHelpSwAO L"\n ao Add files with Archive attribute set"
#define MCHelpSwAP L"\n ap Set path inside archive"
#define MCHelpSwAS L"\n as Synchronize archive contents"
@@ -394,3 +396,6 @@
#define MAdjustValue L"\nAdjusting %s value to %s."
#define MOpFailed L"\nOperation failed"
#define MSkipEncArc L"\nSkipping the encrypted archive %s"
+#define MOrigName L"Original name"
+#define MOriginalTime L"Original time"
+#define MFileRenamed L"\n%s is renamed to %s"
diff --git a/libclamunrar/makefile b/libclamunrar/makefile
index ace156c7c6..55af49befe 100644
--- a/libclamunrar/makefile
+++ b/libclamunrar/makefile
@@ -126,7 +126,7 @@ OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filc
archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \
resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \
rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \
- list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o
+ list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o
.cpp.o:
$(COMPILE) -D$(WHAT) -c $<
@@ -142,20 +142,23 @@ clean:
@rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ)
@rm -f unrar libunrar.*
-unrar: clean $(OBJECTS) $(UNRAR_OBJ)
+# We removed 'clean' from dependencies, because it prevented parallel
+# 'make -Jn' builds.
+
+unrar: $(OBJECTS) $(UNRAR_OBJ)
@rm -f unrar
$(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
$(STRIP) unrar
sfx: WHAT=SFX_MODULE
-sfx: clean $(OBJECTS)
+sfx: $(OBJECTS)
@rm -f default.sfx
$(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS)
$(STRIP) default.sfx
lib: WHAT=RARDLL
lib: CXXFLAGS+=$(LIBFLAGS)
-lib: clean $(OBJECTS) $(LIB_OBJ)
+lib: $(OBJECTS) $(LIB_OBJ)
@rm -f libunrar.*
$(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
$(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)
diff --git a/libclamunrar/model.cpp b/libclamunrar/model.cpp
index 83391c5a45..e4f9e3c537 100644
--- a/libclamunrar/model.cpp
+++ b/libclamunrar/model.cpp
@@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
- pps--;
+
+ // 2022.12.02: we removed pps-- here and changed the code below to avoid
+ // "array subscript -1 is outside array bounds" warning in some compilers.
do
{
- pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount;
+ pps++;
} while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
diff --git a/libclamunrar/options.cpp b/libclamunrar/options.cpp
index 40323be828..22ae27ce21 100644
--- a/libclamunrar/options.cpp
+++ b/libclamunrar/options.cpp
@@ -6,14 +6,6 @@ RAROptions::RAROptions()
}
-RAROptions::~RAROptions()
-{
- // It is important for security reasons, so we do not have the unnecessary
- // password data left in memory.
- memset(this,0,sizeof(RAROptions));
-}
-
-
void RAROptions::Init()
{
memset(this,0,sizeof(RAROptions));
diff --git a/libclamunrar/options.hpp b/libclamunrar/options.hpp
index 113195828b..e249eb599d 100644
--- a/libclamunrar/options.hpp
+++ b/libclamunrar/options.hpp
@@ -45,6 +45,12 @@ enum OVERWRITE_MODE
OVERWRITE_FORCE_ASK
};
+enum ARC_METADATA
+{
+ ARCMETA_NONE=0,
+ ARCMETA_SAVE, // -ams
+ ARCMETA_RESTORE // -amr
+};
enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
@@ -84,11 +90,12 @@ struct FilterMode
#define MAX_GENERATE_MASK 128
+// Here we store simple data types, which we can clear and move all together
+// quickly. Rest of data types goes to CommandData.
class RAROptions
{
public:
RAROptions();
- ~RAROptions();
void Init();
uint ExclFileAttr;
@@ -118,7 +125,6 @@ class RAROptions
wchar ArcPath[NM]; // For -ap.
wchar ExclArcPath[NM]; // For -ep4 switch.
- SecPassword Password;
bool EncryptHeaders;
bool SkipEncrypted;
@@ -132,6 +138,7 @@ class RAROptions
HASH_TYPE HashType;
int Recovery;
int RecVolNumber;
+ ARC_METADATA ArcMetadata;
bool DisablePercentage;
bool DisableCopyright;
bool DisableDone;
@@ -147,7 +154,6 @@ class RAROptions
PATH_EXCL_MODE ExclPath;
RECURSE_MODE Recurse;
int64 VolSize;
- Array NextVolSizes;
uint CurVolNum;
bool AllYes;
bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.
diff --git a/libclamunrar/os.hpp b/libclamunrar/os.hpp
index 7df3c6c0da..4b21e49d76 100644
--- a/libclamunrar/os.hpp
+++ b/libclamunrar/os.hpp
@@ -13,6 +13,8 @@
#endif
#include
+#include
+#include
#if defined(_WIN_ALL) || defined(_EMX)
@@ -39,8 +41,15 @@
#define _UNICODE // Set _T() macro to convert from narrow to wide strings.
#endif
+#if 0
+// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR,
+// but still keep SFX modules XP compatible.
+#define WINVER _WIN32_WINNT_VISTA
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#else
#define WINVER _WIN32_WINNT_WINXP
#define _WIN32_WINNT _WIN32_WINNT_WINXP
+#endif
#if !defined(ZIPSFX)
#define RAR_SMP
@@ -72,9 +81,6 @@
#include
#endif
#ifdef _MSC_VER
- #if _MSC_VER<1500
- #define for if (0) ; else for
- #endif
#include
#include
@@ -98,7 +104,6 @@
#include
#include
-
#define SAVE_LINKS
#define ENABLE_ACCESS
diff --git a/libclamunrar/pathfn.cpp b/libclamunrar/pathfn.cpp
index 983bd74379..e959e9d65b 100644
--- a/libclamunrar/pathfn.cpp
+++ b/libclamunrar/pathfn.cpp
@@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
const wchar *s=DestPtr;
if (s[0]!=0 && IsDriveDiv(s[1]))
s+=2;
- if (s[0]=='\\' && s[1]=='\\')
+
+ // Skip UNC Windows \\server\share\ or Unix //server/share/
+ if (IsPathDiv(s[0]) && IsPathDiv(s[1]))
{
- const wchar *Slash=wcschr(s+2,'\\');
- if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL)
- s=Slash+1;
+ uint SlashCount=0;
+ for (const wchar *t=s+2;*t!=0;t++)
+ if (IsPathDiv(*t) && ++SlashCount==2)
+ {
+ s=t+1; // Found two more path separators after leading two.
+ break;
+ }
}
for (const wchar *t=s;*t!=0;t++)
if (IsPathDiv(*t))
@@ -422,50 +428,39 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
bool IsNameUsable(const wchar *Name)
{
-#ifndef _UNIX
- if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL)
+ // We were asked to apply Windows-like conversion in Linux in case
+ // files are unpacked to Windows share. This code is invoked only
+ // if file failed to be created, so it doesn't affect extraction
+ // of Unix compatible names to native Unix drives.
+#ifdef _UNIX
+ // Windows shares in Unix do not allow the drive letter,
+ // so unlike Windows version, we check all characters here.
+ if (wcschr(Name,':')!=NULL)
return false;
+#else
+ if (Name[0]!=0 && Name[1]!=0 && wcschr(Name+2,':')!=NULL)
+ return false;
+#endif
for (const wchar *s=Name;*s!=0;s++)
{
if ((uint)*s<32)
return false;
+
+ // It is for Windows shares in Unix. We can create such names in Windows.
+#ifdef _UNIX
+ // No spaces or dots before the path separator are allowed in Windows
+ // shares. But they are allowed and automtically removed at the end of
+ // file or folder name, so it is useless to replace them here.
+ // Since such files or folders are created successfully, a supposed
+ // conversion here would never be invoked.
if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
return false;
- }
#endif
+ }
return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL;
}
-void MakeNameUsable(char *Name,bool Extended)
-{
-#ifdef _WIN_ALL
- // In Windows we also need to convert characters not defined in current
- // code page. This double conversion changes them to '?', which is
- // catched by code below.
- size_t NameLength=strlen(Name);
- wchar NameW[NM];
- CharToWide(Name,NameW,ASIZE(NameW));
- WideToChar(NameW,Name,NameLength+1);
- Name[NameLength]=0;
-#endif
- for (char *s=Name;*s!=0;s=charnext(s))
- {
- if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32)
- *s='_';
-#ifdef _EMX
- if (*s=='=')
- *s='_';
-#endif
-#ifndef _UNIX
- if (s-Name>1 && *s==':')
- *s='_';
- // Remove ' ' and '.' before path separator, but allow .\ and ..\.
- if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1]))
- *s='_';
-#endif
- }
-}
void MakeNameUsable(wchar *Name,bool Extended)
@@ -474,7 +469,27 @@ void MakeNameUsable(wchar *Name,bool Extended)
{
if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32)
*s='_';
-#ifndef _UNIX
+#ifdef _UNIX
+ // We were asked to apply Windows-like conversion in Linux in case
+ // files are unpacked to Windows share. This code is invoked only
+ // if file failed to be created, so it doesn't affect extraction
+ // of Unix compatible names to native Unix drives.
+ if (Extended)
+ {
+ // Windows shares in Unix do not allow the drive letter,
+ // so unlike Windows version, we check all characters here.
+ if (*s==':')
+ *s='_';
+
+ // No spaces or dots before the path separator are allowed on Windows
+ // shares. But they are allowed and automatically removed at the end of
+ // file or folder name, so it is useless to replace them here.
+ // Since such files or folders are created successfully, a supposed
+ // conversion here would never be invoked.
+ if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
+ *s='_';
+ }
+#else
if (s-Name>1 && *s==':')
*s='_';
#if 0 // We already can create such files.
@@ -731,7 +746,7 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u
// Here we ensure that we have enough 'N' characters to fit all digits
// of archive number. We'll replace them by actual number later
// in this function.
- if (NCount=4)
CurWeek++;
- char Field[10][6];
+ char Field[10][11];
sprintf(Field[0],"%04u",rlt.Year);
sprintf(Field[1],"%02u",rlt.Month);
diff --git a/libclamunrar/pathfn.hpp b/libclamunrar/pathfn.hpp
index efb283301d..62cae0ad2c 100644
--- a/libclamunrar/pathfn.hpp
+++ b/libclamunrar/pathfn.hpp
@@ -29,7 +29,6 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx
wchar* GetVolNumPart(const wchar *ArcName);
void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering);
bool IsNameUsable(const wchar *Name);
-void MakeNameUsable(char *Name,bool Extended);
void MakeNameUsable(wchar *Name,bool Extended);
void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);
diff --git a/libclamunrar/qopen.cpp b/libclamunrar/qopen.cpp
index 43346b061a..d906d06bd3 100644
--- a/libclamunrar/qopen.cpp
+++ b/libclamunrar/qopen.cpp
@@ -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,
diff --git a/libclamunrar/rar.hpp b/libclamunrar/rar.hpp
index 20e32fb238..67edb6735d 100644
--- a/libclamunrar/rar.hpp
+++ b/libclamunrar/rar.hpp
@@ -12,6 +12,7 @@
#include "version.hpp"
#include "rardefs.hpp"
#include "rarlang.hpp"
+#include "rawint.hpp"
#include "unicode.hpp"
#include "errhnd.hpp"
#include "secpassword.hpp"
@@ -34,7 +35,6 @@
#endif
#include "file.hpp"
#include "crc.hpp"
-#include "ui.hpp"
#include "filefn.hpp"
#include "filestr.hpp"
#include "find.hpp"
@@ -47,11 +47,11 @@
#include "archive.hpp"
#include "match.hpp"
#include "cmddata.hpp"
+#include "ui.hpp"
#include "filcreat.hpp"
#include "consio.hpp"
#include "system.hpp"
#include "log.hpp"
-#include "rawint.hpp"
#include "rawread.hpp"
#include "encname.hpp"
#include "resource.hpp"
diff --git a/libclamunrar/rardefs.hpp b/libclamunrar/rardefs.hpp
index 095792a036..6858d39c13 100644
--- a/libclamunrar/rardefs.hpp
+++ b/libclamunrar/rardefs.hpp
@@ -9,9 +9,13 @@
#define ASIZE(x) (sizeof(x)/sizeof(x[0]))
-// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16)
-// for CryptProtectMemory in SecPassword.
-#define MAXPASSWORD 128
+// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of
+// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword.
+// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords
+// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR
+// to 128 for compatibility and because it is enough for AES-256.
+#define MAXPASSWORD 512
+#define MAXPASSWORD_RAR 128
#define MAXSFXSIZE 0x200000
diff --git a/libclamunrar/rawint.hpp b/libclamunrar/rawint.hpp
index 303798886b..c8cd86fc7a 100644
--- a/libclamunrar/rawint.hpp
+++ b/libclamunrar/rawint.hpp
@@ -84,7 +84,7 @@ inline uint32 RawGetBE4(const byte *m)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_ulong(*(uint32 *)m);
-#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
return __builtin_bswap32(*(uint32 *)m);
#else
return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3];
@@ -97,7 +97,7 @@ inline void RawPutBE4(uint32 i,byte *mem)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint32*)mem = _byteswap_ulong(i);
-#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
*(uint32*)mem = __builtin_bswap32(i);
#else
mem[0]=byte(i>>24);
@@ -112,7 +112,7 @@ inline uint32 ByteSwap32(uint32 i)
{
#ifdef _MSC_VER
return _byteswap_ulong(i);
-#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap32(i);
#else
return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);
diff --git a/libclamunrar/rdwrfn.cpp b/libclamunrar/rdwrfn.cpp
index 5e13ae6547..fa71376018 100644
--- a/libclamunrar/rdwrfn.cpp
+++ b/libclamunrar/rdwrfn.cpp
@@ -155,7 +155,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#ifdef RARDLL
- RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions();
+ CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
@@ -204,7 +204,7 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
- RAROptions *Cmd=SrcArc->GetRAROptions();
+ CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
diff --git a/libclamunrar/recvol.cpp b/libclamunrar/recvol.cpp
index adf5840443..b178207119 100644
--- a/libclamunrar/recvol.cpp
+++ b/libclamunrar/recvol.cpp
@@ -5,7 +5,7 @@
-bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent)
+bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent)
{
Archive Arc(Cmd);
if (!Arc.Open(Name))
@@ -42,7 +42,7 @@ bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent)
}
-void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name)
+void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name)
{
wchar RevName[NM];
*RevName=0;
diff --git a/libclamunrar/recvol.hpp b/libclamunrar/recvol.hpp
index 06510a211b..4a6d663ffe 100644
--- a/libclamunrar/recvol.hpp
+++ b/libclamunrar/recvol.hpp
@@ -14,11 +14,11 @@ class RecVolumes3
ThreadPool *RSThreadPool;
#endif
public:
- RecVolumes3(RAROptions *Cmd,bool TestOnly);
+ RecVolumes3(CommandData *Cmd,bool TestOnly);
~RecVolumes3();
- void Make(RAROptions *Cmd,wchar *ArcName);
- bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
- void Test(RAROptions *Cmd,const wchar *Name);
+ void Make(CommandData *Cmd,wchar *ArcName);
+ bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
+ void Test(CommandData *Cmd,const wchar *Name);
};
@@ -48,8 +48,8 @@ struct RecRSThreadData
class RecVolumes5
{
private:
- void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
- void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode);
+ void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
+ void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode);
uint ReadHeader(File *RecFile,bool FirstRev);
Array RecItems;
@@ -76,13 +76,13 @@ class RecVolumes5
public: // 'public' only because called from thread functions.
void ProcessAreaRS(RecRSThreadData *td);
public:
- RecVolumes5(RAROptions *Cmd,bool TestOnly);
+ RecVolumes5(CommandData *Cmd,bool TestOnly);
~RecVolumes5();
- bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
- void Test(RAROptions *Cmd,const wchar *Name);
+ bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
+ void Test(CommandData *Cmd,const wchar *Name);
};
-bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent);
-void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name);
+bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent);
+void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name);
#endif
diff --git a/libclamunrar/recvol3.cpp b/libclamunrar/recvol3.cpp
index 9fb846a28a..0138d0f3a9 100644
--- a/libclamunrar/recvol3.cpp
+++ b/libclamunrar/recvol3.cpp
@@ -36,7 +36,7 @@ THREAD_PROC(RSDecodeThread)
}
#endif
-RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
+RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly)
{
memset(SrcFile,0,sizeof(SrcFile));
if (TestOnly)
@@ -99,7 +99,7 @@ static bool IsNewStyleRev(const wchar *Name)
}
-bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
+bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent)
{
wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName));
@@ -226,7 +226,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
if (WrongParam)
continue;
}
- if (P[1]+P[2]>255)
+ if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255)
continue;
if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
{
@@ -238,7 +238,14 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
wcsncpyz(PrevName,CurName,ASIZE(PrevName));
File *NewFile=new File;
NewFile->TOpen(CurName);
- SrcFile[FileNumber+P[0]-1]=NewFile;
+
+ // This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255
+ // checks above. Still we keep it here for better clarity and security.
+ int SrcPos=FileNumber+P[0]-1;
+ if (SrcPos<0 || SrcPos>=ASIZE(SrcFile))
+ continue;
+ SrcFile[SrcPos]=NewFile;
+
FoundRecVolumes++;
if (RecFileSize==0)
@@ -497,7 +504,7 @@ void RSEncode::DecodeBuf()
}
-void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
+void RecVolumes3::Test(CommandData *Cmd,const wchar *Name)
{
if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
{
diff --git a/libclamunrar/recvol5.cpp b/libclamunrar/recvol5.cpp
index 596ea06f10..2d9c947110 100644
--- a/libclamunrar/recvol5.cpp
+++ b/libclamunrar/recvol5.cpp
@@ -4,7 +4,7 @@ static const uint MaxVolumes=65535;
// rev files by mistake.
#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files.
-RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly)
+RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly)
{
RealBuf=NULL;
RealReadBuffer=NULL;
@@ -70,7 +70,7 @@ THREAD_PROC(RecThreadRS)
#endif
-void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
+void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
{
/*
RSCoder16 RS;
@@ -141,7 +141,7 @@ void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
-bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
+bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent)
{
wchar ArcName[NM];
wcsncpyz(ArcName,Name,ASIZE(ArcName));
@@ -494,7 +494,7 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
}
-void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
+void RecVolumes5::Test(CommandData *Cmd,const wchar *Name)
{
wchar VolName[NM];
wcsncpyz(VolName,Name,ASIZE(VolName));
diff --git a/libclamunrar/rijndael.cpp b/libclamunrar/rijndael.cpp
index 48ac1723bc..02c4d147b2 100644
--- a/libclamunrar/rijndael.cpp
+++ b/libclamunrar/rijndael.cpp
@@ -90,18 +90,20 @@ Rijndael::Rijndael()
void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector)
{
-#ifdef USE_SSE
- // Check SSE here instead of constructor, so if object is a part of some
- // structure memset'ed before use, this variable is not lost.
+ // Check SIMD here instead of constructor, so if object is a part of some
+ // structure memset'ed before use, these variables are not lost.
+#if defined(USE_SSE)
int CPUInfo[4];
- __cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function.
- if ((CPUInfo[0] & 0x7fffffff)>=1)
+ __cpuid(CPUInfo, 0);
+ if (CPUInfo[0]>=1) // Check the maximum supported cpuid function.
{
__cpuid(CPUInfo, 1);
AES_NI=(CPUInfo[2] & 0x2000000)!=0;
}
else
AES_NI=false;
+#elif defined(USE_NEON)
+ AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0;
#endif
// Other developers asked us to initialize it to suppress "may be used
@@ -141,18 +143,25 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe
keyEncToDec();
}
+
void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer)
{
if (inputLen <= 0)
return;
size_t numBlocks = inputLen/16;
-#ifdef USE_SSE
+#if defined(USE_SSE)
if (AES_NI)
{
blockEncryptSSE(input,numBlocks,outBuffer);
return;
}
+#elif defined(USE_NEON)
+ if (AES_Neon)
+ {
+ blockEncryptNeon(input,numBlocks,outBuffer);
+ return;
+ }
#endif
byte *prevBlock = m_initVector;
@@ -239,6 +248,40 @@ void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffe
}
#endif
+
+#ifdef USE_NEON
+void Rijndael::blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer)
+{
+ byte *prevBlock = m_initVector;
+ while (numBlocks > 0)
+ {
+ byte block[16];
+ if (CBCMode)
+ vst1q_u8(block, veorq_u8(vld1q_u8(prevBlock), vld1q_u8(input)));
+ else
+ vst1q_u8(block, vld1q_u8(input));
+
+ uint8x16_t data = vld1q_u8(block);
+ for (uint i = 0; i < m_uRounds-1; i++)
+ {
+ data = vaeseq_u8(data, vld1q_u8((byte *)m_expandedKey[i]));
+ data = vaesmcq_u8(data);
+ }
+ data = vaeseq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds-1])));
+ data = veorq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds])));
+ vst1q_u8(outBuffer, data);
+
+ prevBlock=outBuffer;
+
+ outBuffer += 16;
+ input += 16;
+ numBlocks--;
+ }
+ vst1q_u8(m_initVector, vld1q_u8(prevBlock));
+ return;
+}
+#endif
+
void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer)
{
@@ -246,12 +289,18 @@ void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer)
return;
size_t numBlocks=inputLen/16;
-#ifdef USE_SSE
+#if defined(USE_SSE)
if (AES_NI)
{
blockDecryptSSE(input,numBlocks,outBuffer);
return;
}
+#elif defined(USE_NEON)
+ if (AES_Neon)
+ {
+ blockDecryptNeon(input,numBlocks,outBuffer);
+ return;
+ }
#endif
byte block[16], iv[4][4];
@@ -343,6 +392,41 @@ void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuf
#endif
+#ifdef USE_NEON
+void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer)
+{
+ byte iv[16];
+ memcpy(iv,m_initVector,16);
+
+ while (numBlocks > 0)
+ {
+ uint8x16_t data = vld1q_u8(input);
+
+ for (int i=m_uRounds-1; i>0; i--)
+ {
+ data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1]));
+ data = vaesimcq_u8(data);
+ }
+
+ data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1]));
+ data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0]));
+
+ if (CBCMode)
+ data = veorq_u8(data, vld1q_u8(iv));
+
+ vst1q_u8(iv, vld1q_u8(input));
+ vst1q_u8(outBuffer, data);
+
+ input += 16;
+ outBuffer += 16;
+ numBlocks--;
+ }
+
+ memcpy(m_initVector,iv,16);
+}
+#endif
+
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ALGORITHM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/libclamunrar/rijndael.hpp b/libclamunrar/rijndael.hpp
index 797899ffb7..96e1d0d14c 100644
--- a/libclamunrar/rijndael.hpp
+++ b/libclamunrar/rijndael.hpp
@@ -18,6 +18,16 @@ class Rijndael
bool AES_NI;
#endif
+#ifdef USE_NEON
+ // Set "crypto" attribute as replacement of -march=armv8-a+crypto switch.
+ __attribute__((target("crypto")))
+ void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer);
+ __attribute__((target("crypto")))
+ void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer);
+
+ bool AES_Neon;
+#endif
+
void keySched(byte key[_MAX_KEY_COLUMNS][4]);
void keyEncToDec();
void GenerateTables();
diff --git a/libclamunrar/scantree.cpp b/libclamunrar/scantree.cpp
index a13a3ebcef..dbaf1e4273 100644
--- a/libclamunrar/scantree.cpp
+++ b/libclamunrar/scantree.cpp
@@ -215,10 +215,21 @@ bool ScanTree::GetNextMask()
UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask));
#endif
- // We wish to scan entire disk if mask like c:\ is specified
- // regardless of recursion mode. Use c:\*.* mask when need to scan only
- // the root directory.
- ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
+ // We prefer to scan entire disk if mask like \\server\share\ or c:\
+ // is specified regardless of recursion mode. Use \\server\share\*.*
+ // or c:\*.* mask to scan only the root directory.
+ if (CurMask[0]=='\\' && CurMask[1]=='\\')
+ {
+ const wchar *Slash=wcschr(CurMask+2,'\\');
+ if (Slash!=NULL)
+ {
+ Slash=wcschr(Slash+1,'\\');
+ ScanEntireDisk=Slash!=NULL && *(Slash+1)==0;
+ }
+ }
+ else
+ ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
+
wchar *Name=PointToName(CurMask);
if (*Name==0)
diff --git a/libclamunrar/secpassword.cpp b/libclamunrar/secpassword.cpp
index b99e53af01..08da549721 100644
--- a/libclamunrar/secpassword.cpp
+++ b/libclamunrar/secpassword.cpp
@@ -56,7 +56,6 @@ static CryptLoader GlobalCryptLoader;
SecPassword::SecPassword()
{
- CrossProcess=false;
Set(L"");
}
@@ -70,7 +69,8 @@ SecPassword::~SecPassword()
void SecPassword::Clean()
{
PasswordSet=false;
- cleandata(Password,sizeof(Password));
+ if (Password.size()>0)
+ cleandata(&Password[0],Password.size()*sizeof(Password[0]));
}
@@ -104,7 +104,7 @@ void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstS
// Source string can be shorter than destination as in case when we process
// -p parameter, so we need to take into account both sizes.
memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst));
- SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess);
+ SecHideData(Dst,DstSize*sizeof(*Dst),Encode,false);
}
@@ -112,7 +112,7 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize)
{
if (PasswordSet)
{
- Process(Password,ASIZE(Password),Psw,MaxSize,false);
+ Process(&Password[0],Password.size(),Psw,MaxSize,false);
Psw[MaxSize-1]=0;
}
else
@@ -124,15 +124,14 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize)
void SecPassword::Set(const wchar *Psw)
{
- if (*Psw==0)
- {
- PasswordSet=false;
- memset(Password,0,sizeof(Password));
- }
- else
+ // Eliminate any traces of previously stored password for security reason
+ // in case it was longer than new one.
+ Clean();
+
+ if (*Psw!=0)
{
PasswordSet=true;
- Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true);
+ Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true);
}
}
@@ -142,7 +141,7 @@ size_t SecPassword::Length()
wchar Plain[MAXPASSWORD];
Get(Plain,ASIZE(Plain));
size_t Length=wcslen(Plain);
- cleandata(Plain,ASIZE(Plain));
+ cleandata(Plain,sizeof(Plain));
return Length;
}
@@ -157,12 +156,15 @@ bool SecPassword::operator == (SecPassword &psw)
Get(Plain1,ASIZE(Plain1));
psw.Get(Plain2,ASIZE(Plain2));
bool Result=wcscmp(Plain1,Plain2)==0;
- cleandata(Plain1,ASIZE(Plain1));
- cleandata(Plain2,ASIZE(Plain2));
+ cleandata(Plain1,sizeof(Plain1));
+ cleandata(Plain2,sizeof(Plain2));
return Result;
}
+// Set CrossProcess to true if we need to pass a password to another process.
+// We use CrossProcess when transferring parameters to UAC elevated WinRAR
+// and Windows GUI SFX modules.
void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
{
// CryptProtectMemory is not available in UWP and CryptProtectData
diff --git a/libclamunrar/secpassword.hpp b/libclamunrar/secpassword.hpp
index 375d3887a4..5284bce124 100644
--- a/libclamunrar/secpassword.hpp
+++ b/libclamunrar/secpassword.hpp
@@ -8,10 +8,7 @@ class SecPassword
private:
void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode);
- wchar Password[MAXPASSWORD];
-
- // It is important to have this 'bool' value, so if our object is cleaned
- // with memset as a part of larger structure, it is handled correctly.
+ std::vector Password = std::vector(MAXPASSWORD);
bool PasswordSet;
public:
SecPassword();
@@ -22,10 +19,6 @@ class SecPassword
bool IsSet() {return PasswordSet;}
size_t Length();
bool operator == (SecPassword &psw);
-
- // Set to true if we need to pass a password to another process.
- // We use it when transferring parameters to UAC elevated WinRAR.
- bool CrossProcess;
};
diff --git a/libclamunrar/strfn.cpp b/libclamunrar/strfn.cpp
index f53c826ace..7617f7a599 100644
--- a/libclamunrar/strfn.cpp
+++ b/libclamunrar/strfn.cpp
@@ -357,6 +357,32 @@ void itoa(int64 n,wchar *Str,size_t MaxSize)
}
+// Convert the number to string using thousand separators.
+void fmtitoa(int64 n,wchar *Str,size_t MaxSize)
+{
+ static wchar ThSep=0; // Thousands separator.
+#ifdef _WIN_ALL
+ wchar Info[10];
+ if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0)
+ ThSep=*Info;
+#elif defined(_UNIX)
+ ThSep=*localeconv()->thousands_sep;
+#endif
+ if (ThSep==0) // If failed to detect the actual separator value.
+ ThSep=' ';
+ wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int.
+ itoa(n,RawText,ASIZE(RawText));
+ uint S=0,D=0,L=wcslen(RawText)%3;
+ while (RawText[S]!=0 && D+1=7)
{
diff --git a/libclamunrar/threadmisc.cpp b/libclamunrar/threadmisc.cpp
index 742eda41d9..7a6ec7822e 100644
--- a/libclamunrar/threadmisc.cpp
+++ b/libclamunrar/threadmisc.cpp
@@ -149,3 +149,5 @@ uint GetNumberOfThreads()
return NumCPU;
}
+
+
diff --git a/libclamunrar/timefn.hpp b/libclamunrar/timefn.hpp
index 5271361644..49b61e85d6 100644
--- a/libclamunrar/timefn.hpp
+++ b/libclamunrar/timefn.hpp
@@ -22,6 +22,17 @@ class RarTime
// Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601.
// We use nanoseconds here to handle the high precision Unix time.
+ // It allows dates up to July 2185.
+ //
+ // If we'll ever need to extend the date range, we can define a lower
+ // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows
+ // versions can differ in least significant digits of "lt" time output
+ // for Unix archives.
+ // Alternatively we can introduce 'bool HighPrecision' set to true
+ // in SetUnixNS() and TicksPerSecond() instead of constant above.
+ // It might be more reliable than defining TicksPerSecond variable,
+ // which wouldn't survive memset of any structure hosting RarTime.
+ // We would need to eliminate all such memsets in the entire code first.
uint64 itime;
public:
// RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND.
diff --git a/libclamunrar/ui.hpp b/libclamunrar/ui.hpp
index 8fc76aa800..5def26df18 100644
--- a/libclamunrar/ui.hpp
+++ b/libclamunrar/ui.hpp
@@ -49,7 +49,7 @@ enum UIMESSAGE_CODE {
UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING,
UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING,
UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE,
- UIMSG_SKIPENCARC,
+ UIMSG_SKIPENCARC, UIMSG_FILERENAME,
UIWAIT_FIRST,
UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW,
@@ -77,7 +77,7 @@ enum UIASKREP_RESULT {
};
UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
-UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
+UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
void uiInit(SOUND_NOTIFY_MODE Sound);
@@ -88,7 +88,7 @@ void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64
void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize);
enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE};
-bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd);
bool uiIsGlobalPasswordSet();
enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION};
@@ -145,30 +145,31 @@ class uiMsgStore
// Templates recognize usual NULL as integer, not wchar*.
#define UINULL ((wchar *)NULL)
-inline void uiMsg(UIMESSAGE_CODE Code)
+inline void uiMsgBase(uiMsgStore &Store)
{
- uiMsgStore Store(Code);
- Store.Msg();
+ // Called last, when no parameters are left.
}
-template void uiMsg(UIMESSAGE_CODE Code,T1 a1)
+template void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN)
{
- uiMsgStore Store(Code);
+ // Process first parameter and pass the rest to same uiMsgBase.
Store< void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2)
-{
- uiMsgStore Store(Code);
- Store< void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3)
+// Use variadic templates.
+//
+// We must pass variable parameters by reference, so no temporary copies are
+// created for custom string objects like CStringBase in 7-Zip decompression
+// code. Such temporary copies would be destroyed inside of recursive
+// uiMsgBase calls, leaving us with Str[] items pointing at released memory.
+// Since we pass integer values as well, we can't use & references
+// and must resort to && rvalue references.
+template void uiMsg(UIMESSAGE_CODE Code,TN&&... aN)
{
- uiMsgStore Store(code);
- Store<Overwrite==OVERWRITE_NONE)
return UIASKREP_R_SKIP;
diff --git a/libclamunrar/uiconsole.cpp b/libclamunrar/uiconsole.cpp
index d713fac0b6..b524c25ff4 100644
--- a/libclamunrar/uiconsole.cpp
+++ b/libclamunrar/uiconsole.cpp
@@ -183,6 +183,7 @@ void uiMsgStore::Msg()
Log(NULL,St(MNeedAdmin));
break;
case UIERROR_ARCBROKEN:
+ mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN.
Log(Str[0],St(MErrBrokenArc));
break;
case UIERROR_HEADERBROKEN:
@@ -262,6 +263,7 @@ void uiMsgStore::Msg()
break;
case UIERROR_MISSINGVOL:
Log(Str[0],St(MAbsNextVol),Str[0]);
+ mprintf(L" "); // For progress percent.
break;
#ifndef SFX_MODULE
case UIERROR_NEEDPREVVOL:
@@ -395,7 +397,8 @@ void uiMsgStore::Msg()
}
-bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,
+ SecPassword *Password,CheckPassword *CheckPwd)
{
// Unlike GUI we cannot provide Cancel button here, so we use the empty
// password to abort. Otherwise user not knowing a password would need to
diff --git a/libclamunrar/uisilent.cpp b/libclamunrar/uisilent.cpp
index 1df0975665..8155885746 100644
--- a/libclamunrar/uisilent.cpp
+++ b/libclamunrar/uisilent.cpp
@@ -33,7 +33,8 @@ void uiMsgStore::Msg()
}
-bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,
+ SecPassword *Password,CheckPassword *CheckPwd)
{
return false;
}
diff --git a/libclamunrar/ulinks.cpp b/libclamunrar/ulinks.cpp
index af6ef36ff3..141a97fecc 100644
--- a/libclamunrar/ulinks.cpp
+++ b/libclamunrar/ulinks.cpp
@@ -70,7 +70,8 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize)
}
-bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,
+ const wchar *LinkName,bool &UpLink)
{
char Target[NM];
if (IsLink(Arc.FileHead.FileAttr))
@@ -100,13 +101,14 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW)))
return false;
+ UpLink=strstr(Target,"..")!=NULL;
return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
}
return false;
}
-bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
+static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
{
char Target[NM];
WideToChar(hd->RedirName,Target,ASIZE(Target));
@@ -127,8 +129,6 @@ bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
// Use hd->FileName instead of LinkName, since LinkName can include
// the destination path as a prefix, which can confuse
// IsRelativeSymlinkSafe algorithm.
- // 2022.05.04: Use TargetW instead of previously used hd->RedirName
- // for security reason.
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW)))
return false;
diff --git a/libclamunrar/unicode.cpp b/libclamunrar/unicode.cpp
index 5421923ff3..73b09bb220 100644
--- a/libclamunrar/unicode.cpp
+++ b/libclamunrar/unicode.cpp
@@ -229,10 +229,11 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
#endif
-// SrcSize is in wide characters, not in bytes.
-byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize)
+// SrcSize is source data size in wide characters, not in bytes.
+// DestSize is the maximum allowed destination size.
+byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize)
{
- for (size_t I=0;I>8);
diff --git a/libclamunrar/unicode.hpp b/libclamunrar/unicode.hpp
index 8d433c1b6b..9bfd9c5dcd 100644
--- a/libclamunrar/unicode.hpp
+++ b/libclamunrar/unicode.hpp
@@ -7,7 +7,7 @@
bool WideToChar(const wchar *Src,char *Dest,size_t DestSize);
bool CharToWide(const char *Src,wchar *Dest,size_t DestSize);
-byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize);
+byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize);
wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize);
void WideToUtf(const wchar *Src,char *Dest,size_t DestSize);
size_t WideToUtfSize(const wchar *Src);
diff --git a/libclamunrar/unpack.cpp b/libclamunrar/unpack.cpp
index 8dd2311901..f82bd4753b 100644
--- a/libclamunrar/unpack.cpp
+++ b/libclamunrar/unpack.cpp
@@ -315,7 +315,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
Dec->QuickBits=MAX_QUICK_DECODE_BITS;
break;
default:
- Dec->QuickBits=MAX_QUICK_DECODE_BITS-3;
+ Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0;
break;
}
diff --git a/libclamunrar/unpack.hpp b/libclamunrar/unpack.hpp
index 30a9a2ee77..737b31c2cc 100644
--- a/libclamunrar/unpack.hpp
+++ b/libclamunrar/unpack.hpp
@@ -93,17 +93,17 @@ struct UnpackBlockTables
#ifdef RAR_SMP
enum UNP_DEC_TYPE {
- UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER
+ UNPDT_LITERAL=0,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER
};
struct UnpackDecodedItem
{
- UNP_DEC_TYPE Type;
+ byte Type; // 'byte' instead of enum type to reduce memory use.
ushort Length;
union
{
uint Distance;
- byte Literal[4];
+ byte Literal[8]; // Store up to 8 chars here to speed up extraction.
};
};
diff --git a/libclamunrar/unpack30.cpp b/libclamunrar/unpack30.cpp
index 6a8efa23bc..7c2adfab28 100644
--- a/libclamunrar/unpack30.cpp
+++ b/libclamunrar/unpack30.cpp
@@ -55,7 +55,7 @@ void Unpack::Unpack29(bool Solid)
if (!UnpReadBuf30())
break;
}
- if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr)
+ if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr)
{
UnpWriteBuf30();
if (WrittenFileSize>DestUnpSize)
diff --git a/libclamunrar/unpack50.cpp b/libclamunrar/unpack50.cpp
index 99119507f9..e040907c27 100644
--- a/libclamunrar/unpack50.cpp
+++ b/libclamunrar/unpack50.cpp
@@ -42,7 +42,7 @@ void Unpack::Unpack5(bool Solid)
break;
}
- if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize)
@@ -93,7 +93,7 @@ void Unpack::Unpack5(bool Solid)
}
else
{
- Distance+=Inp.getbits32()>>(32-DBits);
+ Distance+=Inp.getbits()>>(16-DBits);
Inp.addbits(DBits);
}
}
diff --git a/libclamunrar/unpack50mt.cpp b/libclamunrar/unpack50mt.cpp
index 691ac8e993..82c9c4a8ce 100644
--- a/libclamunrar/unpack50mt.cpp
+++ b/libclamunrar/unpack50mt.cpp
@@ -345,7 +345,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
if (D.DecodedSize>1)
{
UnpackDecodedItem *PrevItem=CurItem-1;
- if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3)
+ if (PrevItem->Type==UNPDT_LITERAL && PrevItem->LengthLiteral)-1)
{
PrevItem->Length++;
PrevItem->Literal[PrevItem->Length]=(byte)MainSlot;
@@ -388,7 +388,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
}
else
{
- Distance+=D.Inp.getbits32()>>(32-DBits);
+ Distance+=D.Inp.getbits()>>(16-DBits);
D.Inp.addbits(DBits);
}
}
@@ -451,7 +451,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
while (ItemDestUnpSize)
@@ -461,10 +461,10 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
if (Item->Type==UNPDT_LITERAL)
{
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
- if (Item->Length==3 && UnpPtrLength==7 && UnpPtrLiteral;
- UnpPtr+=4;
+ *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal);
+ UnpPtr+=8;
}
else
#endif
@@ -559,7 +559,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D)
break;
}
}
- if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize)
diff --git a/libclamunrar/uowners.cpp b/libclamunrar/uowners.cpp
index 9f46308583..5eb1279027 100644
--- a/libclamunrar/uowners.cpp
+++ b/libclamunrar/uowners.cpp
@@ -1,56 +1,12 @@
-void ExtractUnixOwner20(Archive &Arc,const wchar *FileName)
+void ExtractUnixOwner30(Archive &Arc,const wchar *FileName)
{
char NameA[NM];
WideToChar(FileName,NameA,ASIZE(NameA));
- if (Arc.BrokenHeader)
- {
- uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName);
- ErrHandler.SetErrorCode(RARX_CRC);
+ if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL)
return;
- }
-
- struct passwd *pw;
- errno=0; // Required by getpwnam specification if we need to check errno.
- if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL)
- {
- uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName));
- ErrHandler.SysErrMsg();
- ErrHandler.SetErrorCode(RARX_WARNING);
- return;
- }
- uid_t OwnerID=pw->pw_uid;
-
- struct group *gr;
- errno=0; // Required by getgrnam specification if we need to check errno.
- if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL)
- {
- uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName));
- ErrHandler.SysErrMsg();
- ErrHandler.SetErrorCode(RARX_CRC);
- return;
- }
- uint Attr=GetFileAttr(FileName);
- gid_t GroupID=gr->gr_gid;
-#if defined(SAVE_LINKS) && !defined(_APPLE)
- if (lchown(NameA,OwnerID,GroupID)!=0)
-#else
- if (chown(NameA,OwnerID,GroupID)!=0)
-#endif
- {
- uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName);
- ErrHandler.SetErrorCode(RARX_CREATE);
- }
- SetFileAttr(FileName,Attr);
-}
-
-
-void ExtractUnixOwner30(Archive &Arc,const wchar *FileName)
-{
- char NameA[NM];
- WideToChar(FileName,NameA,ASIZE(NameA));
char *OwnerName=(char *)&Arc.SubHead.SubData[0];
int OwnerSize=strlen(OwnerName)+1;
diff --git a/libclamunrar/version.hpp b/libclamunrar/version.hpp
index 4807c9cb40..1f45c3a189 100644
--- a/libclamunrar/version.hpp
+++ b/libclamunrar/version.hpp
@@ -1,6 +1,6 @@
#define RARVER_MAJOR 6
-#define RARVER_MINOR 12
+#define RARVER_MINOR 23
#define RARVER_BETA 0
-#define RARVER_DAY 4
-#define RARVER_MONTH 5
-#define RARVER_YEAR 2022
+#define RARVER_DAY 1
+#define RARVER_MONTH 8
+#define RARVER_YEAR 2023
diff --git a/libclamunrar/volume.cpp b/libclamunrar/volume.cpp
index 917851de40..4924d8d0ea 100644
--- a/libclamunrar/volume.cpp
+++ b/libclamunrar/volume.cpp
@@ -1,15 +1,15 @@
#include "rar.hpp"
#ifdef RARDLL
-static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize);
-static bool DllVolNotify(RAROptions *Cmd,wchar *NextName);
+static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize);
+static bool DllVolNotify(CommandData *Cmd,wchar *NextName);
#endif
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command)
{
- RAROptions *Cmd=Arc.GetRAROptions();
+ CommandData *Cmd=Arc.GetCommandData();
HEADER_TYPE HeaderType=Arc.GetHeaderType();
FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead;
@@ -190,7 +190,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma
#ifdef RARDLL
-bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
+bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize)
{
bool DllVolChanged=false,DllVolAborted=false;
@@ -246,7 +246,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
#ifdef RARDLL
-bool DllVolNotify(RAROptions *Cmd,wchar *NextName)
+bool DllVolNotify(CommandData *Cmd,wchar *NextName)
{
char NextNameA[NM];
WideToChar(NextName,NextNameA,ASIZE(NextNameA));
diff --git a/libclamunrar/volume.hpp b/libclamunrar/volume.hpp
index 2d6a6d5c1e..4ada10910b 100644
--- a/libclamunrar/volume.hpp
+++ b/libclamunrar/volume.hpp
@@ -1,10 +1,7 @@
#ifndef _RAR_VOLUME_
#define _RAR_VOLUME_
-void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos,
- ComprDataIO *DataIO);
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,
wchar Command);
-void SetVolWrite(Archive &Dest,int64 VolSize);
#endif
diff --git a/libclamunrar/win32lnk.cpp b/libclamunrar/win32lnk.cpp
index 84ab63ff61..759c49003f 100644
--- a/libclamunrar/win32lnk.cpp
+++ b/libclamunrar/win32lnk.cpp
@@ -40,7 +40,7 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd)
PrivSet=true;
}
- const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024;
+ const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024;
Array Buf(BufSize);
REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0];
diff --git a/libclamunrar/win32stm.cpp b/libclamunrar/win32stm.cpp
index eaa43be2d9..9dab728bd4 100644
--- a/libclamunrar/win32stm.cpp
+++ b/libclamunrar/win32stm.cpp
@@ -39,6 +39,7 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName)
CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName));
ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1);
+
wcsncatz(StreamName,StoredName,ASIZE(StreamName));
FindData fd;
@@ -111,16 +112,23 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode)
wcsncatz(FullName,StreamName,ASIZE(FullName));
+
FindData fd;
- bool Found=FindFile::FastFind(FileName,&fd);
+ bool HostFound=FindFile::FastFind(FileName,&fd);
if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
File CurFile;
- if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile,false))
- CurFile.Close();
+
+ if (CurFile.WCreate(FullName))
+ {
+ if (Arc.ReadSubData(NULL,&CurFile,false))
+ CurFile.Close();
+ }
+
+ // Restoring original file timestamps.
File HostFile;
- if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE))
+ if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE))
SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime,
&fd.ftLastWriteTime);
diff --git a/libclamunrar_iface/CMakeLists.txt b/libclamunrar_iface/CMakeLists.txt
index 4918eb8a10..d7c4648e55 100644
--- a/libclamunrar_iface/CMakeLists.txt
+++ b/libclamunrar_iface/CMakeLists.txt
@@ -34,9 +34,11 @@ if(ENABLE_UNRAR)
if(ENABLE_SHARED_LIB)
# The clamunrar_iface SHARED library.
add_library( clamunrar_iface SHARED )
- set_target_properties(clamunrar_iface PROPERTIES
+ set_target_properties( clamunrar_iface PROPERTIES
VERSION ${LIBCLAMAV_VERSION}
- SOVERSION ${LIBCLAMAV_SOVERSION})
+ SOVERSION ${LIBCLAMAV_SOVERSION}
+ CXX_STANDARD 11
+ )
target_sources( clamunrar_iface
PRIVATE
@@ -84,6 +86,9 @@ if(ENABLE_UNRAR)
if(ENABLE_STATIC_LIB)
# The clamunrar_iface static library.
add_library( clamunrar_iface_static STATIC)
+ set_target_properties( clamunrar_iface_static PROPERTIES
+ CXX_STANDARD 11
+ )
target_sources( clamunrar_iface_static
PRIVATE
diff --git a/win32/res/common.rc b/win32/res/common.rc
index b5ee7de048..250ce57915 100644
--- a/win32/res/common.rc
+++ b/win32/res/common.rc
@@ -6,8 +6,8 @@
#define REPO_VERSION VERSION
#endif
-#define RES_VER_Q 1,0,2,0
-#define RES_VER_S "ClamAV 1.0.2"
+#define RES_VER_Q 1,0,3,0
+#define RES_VER_S "ClamAV 1.0.3"
VS_VERSION_INFO VERSIONINFO
FILEVERSION RES_VER_Q