diff --git a/UnRAR.vcxproj b/UnRAR.vcxproj
index 512bcf1..ac0878f 100644
--- a/UnRAR.vcxproj
+++ b/UnRAR.vcxproj
@@ -238,8 +238,10 @@
+
+
diff --git a/UnRARDll.vcxproj b/UnRARDll.vcxproj
index 72cecd8..17ea1b4 100644
--- a/UnRARDll.vcxproj
+++ b/UnRARDll.vcxproj
@@ -374,7 +374,9 @@
+
+
diff --git a/arccmt.cpp b/arccmt.cpp
index f23a7dd..a0fcd7b 100644
--- a/arccmt.cpp
+++ b/arccmt.cpp
@@ -36,7 +36,12 @@ bool Archive::DoGetComment(std::wstring &CmtData)
{
// Current (RAR 3.0+) version of archive comment.
Seek(GetStartPos(),SEEK_SET);
- return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
+ if (SearchSubBlock(SUBHEAD_TYPE_CMT)!=0)
+ if (ReadCommentData(CmtData))
+ return true;
+ else
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
#ifndef SFX_MODULE
// Old style (RAR 2.9) comment header embedded into the main
diff --git a/archive.cpp b/archive.cpp
index c2293de..f90b619 100644
--- a/archive.cpp
+++ b/archive.cpp
@@ -67,7 +67,7 @@ void Archive::CheckArc(bool EnableBroken)
// password is incorrect.
if (!FailedHeaderDecryption)
uiMsg(UIERROR_BADARCHIVE,FileName);
- ErrHandler.Exit(RARX_FATAL);
+ ErrHandler.Exit(RARX_BADARC);
}
}
@@ -109,9 +109,11 @@ RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
// We check the last signature byte, so we can return a sensible
// warning in case we'll want to change the archive format
// sometimes in the future.
+#ifndef SFX_MODULE
if (D[6]==0)
Type=RARFMT15;
else
+#endif
if (D[6]==1)
Type=RARFMT50;
else
diff --git a/archive.hpp b/archive.hpp
index 58bdafa..895f853 100644
--- a/archive.hpp
+++ b/archive.hpp
@@ -58,7 +58,7 @@ class Archive:public File
bool ProhibitQOpen;
#endif
public:
- Archive(CommandData *InitCmd=NULL);
+ Archive(CommandData *InitCmd=nullptr);
~Archive();
static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
@@ -147,6 +147,9 @@ class Archive:public File
bool NewArchive;
std::wstring FirstVolumeName;
+#ifdef PROPAGATE_MOTW
+ MarkOfTheWeb Motw;
+#endif
};
diff --git a/arcread.cpp b/arcread.cpp
index 825da72..ab80463 100644
--- a/arcread.cpp
+++ b/arcread.cpp
@@ -20,10 +20,10 @@ size_t Archive::ReadHeader()
case RARFMT14:
ReadSize=ReadHeader14();
break;
-#endif
case RARFMT15:
ReadSize=ReadHeader15();
break;
+#endif
case RARFMT50:
ReadSize=ReadHeader50();
break;
@@ -106,6 +106,9 @@ void Archive::UnexpEndArcMsg()
if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{
uiMsg(UIERROR_UNEXPEOF,FileName);
+ if (CurHeaderType!=HEAD_FILE)
+ uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName);
+
ErrHandler.SetErrorCode(RARX_WARNING);
}
}
@@ -137,6 +140,7 @@ inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
}
+#ifndef SFX_MODULE
size_t Archive::ReadHeader15()
{
RawRead Raw(this);
@@ -145,7 +149,7 @@ size_t Archive::ReadHeader15()
if (Decrypt)
{
-#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
+#ifdef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll.
return 0;
#else
RequestArcPassword(NULL);
@@ -545,6 +549,7 @@ size_t Archive::ReadHeader15()
return Raw.Size();
}
+#endif // #ifndef SFX_MODULE
size_t Archive::ReadHeader50()
@@ -740,10 +745,15 @@ size_t Archive::ReadHeader50()
byte csum[SIZE_PSWCHECK_CSUM];
Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
+// Exclude this code for rarext.dll, Setup.SFX and unrar_nocrypt.dll linked
+// without sha256. But still set Encrypted=true for rarext.dll here,
+// so it can recognize encrypted header archives in archive properties.
+#ifndef RAR_NOCRYPT
byte Digest[SHA256_DIGEST_SIZE];
sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest);
CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+#endif
}
Encrypted=true;
}
@@ -1036,22 +1046,30 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
FileHeader *hd=(FileHeader *)bb;
switch(FieldType)
{
+#ifndef RAR_NOCRYPT // Except rarext.dll, Setup.SFX and unrar_nocrypt.dll.
case FHEXTRA_CRYPT:
{
FileHeader *hd=(FileHeader *)bb;
uint EncVersion=(uint)Raw->GetV();
if (EncVersion>CRYPT_VERSION)
+ {
UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion));
+ hd->CryptMethod=CRYPT_UNKNOWN;
+ }
else
{
uint Flags=(uint)Raw->GetV();
- hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
- hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
hd->Lg2Count=Raw->Get1();
if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count));
+ hd->CryptMethod=CRYPT_UNKNOWN;
+ }
else
{
+ hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
+ hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
+
Raw->GetB(hd->Salt,SIZE_SALT50);
Raw->GetB(hd->InitV,SIZE_INITV);
if (hd->UsePswCheck)
@@ -1085,6 +1103,7 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
}
}
break;
+#endif
case FHEXTRA_HASH:
{
FileHeader *hd=(FileHeader *)bb;
diff --git a/blake2s.hpp b/blake2s.hpp
index 06e396a..90b7885 100644
--- a/blake2s.hpp
+++ b/blake2s.hpp
@@ -5,12 +5,9 @@
#define BLAKE2_DIGEST_SIZE 32
#define BLAKE2_THREADS_NUMBER 8
-enum blake2s_constant
-{
- BLAKE2S_BLOCKBYTES = 64,
- BLAKE2S_OUTBYTES = 32
-};
-
+// Use constexpr instead of enums for -std=c++20 compatibility.
+constexpr size_t BLAKE2S_BLOCKBYTES = 64;
+constexpr size_t BLAKE2S_OUTBYTES = 32;
// Alignment to 64 improves performance of both SSE and non-SSE versions.
// Alignment to n*16 is required for SSE version, so we selected 64.
diff --git a/cmddata.cpp b/cmddata.cpp
index 37bcf5b..0a1852d 100644
--- a/cmddata.cpp
+++ b/cmddata.cpp
@@ -38,6 +38,9 @@ void CommandData::Init()
InclArgs.Reset();
ArcNames.Reset();
StoreArgs.Reset();
+#ifdef PROPAGATE_MOTW
+ MotwList.Reset();
+#endif
Password.Clean();
NextVolSizes.clear();
#ifdef RARDLL
@@ -189,7 +192,7 @@ void CommandData::ParseDone()
#if !defined(SFX_MODULE)
void CommandData::ParseEnvVar()
{
- char *EnvVar=getenv("RAR");
+ char *EnvVar=getenv("RARINISWITCHES");
if (EnvVar!=NULL)
{
std::wstring EnvStr;
@@ -293,6 +296,9 @@ void CommandData::ProcessSwitchesString(const std::wstring &Str)
void CommandData::ProcessSwitch(const wchar *Switch)
{
+ if (LargePageAlloc::ProcessSwitch(this,Switch))
+ return;
+
switch(toupperw(Switch[0]))
{
case '@':
@@ -619,8 +625,6 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
}
break;
- case 'M':
- break;
case 'D':
{
bool SetDictLimit=toupperw(Switch[2])=='X';
@@ -665,33 +669,30 @@ void CommandData::ProcessSwitch(const wchar *Switch)
if (toupperw(Switch[2])=='S' && Switch[3]==0)
SkipEncrypted=true;
break;
- case 'S':
+ case 'L':
+ if (toupperw(Switch[2])=='P')
{
- std::wstring StoreNames=(Switch[2]==0 ? DefaultStoreList:Switch+2);
- size_t Pos=0;
- while (PosMaxPoolThreads || Threads<1)
BadSwitch(Switch);
- else
- {
- }
break;
#endif
default:
@@ -752,6 +753,18 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
break;
#endif
+#ifdef PROPAGATE_MOTW
+ case 'M':
+ {
+ MotwAllFields=Switch[2]=='1';
+ const wchar *Sep=wcschr(Switch+2,'=');
+ if (Switch[2]=='-')
+ MotwList.Reset();
+ else
+ GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,MotwList);
+ }
+ break;
+#endif
#ifdef _WIN_ALL
case 'N':
if (toupperw(Switch[2])=='I')
@@ -1033,6 +1046,11 @@ void CommandData::ProcessCommand()
#ifndef SFX_MODULE
const wchar *SingleCharCommands=L"FUADPXETK";
+
+ // RAR -mlp command is the legitimate way to assign the required privilege.
+ if (Command.empty() && UseLargePages || SetupComplete)
+ return;
+
if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || ArcName.empty())
OutHelp(Command.empty() ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
@@ -1231,5 +1249,25 @@ int64 CommandData::GetVolSize(const wchar *S,uint DefMultiplier)
}
+// Treat the list like rar;zip as *.rar;*.zip for -ms and similar switches.
+void CommandData::GetBriefMaskList(const std::wstring &Masks,StringList &Args)
+{
+ size_t Pos=0;
+ while (Pos Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
+ DWORD SizeToRead=(DWORD)Buf.size()-1;
+
+ // ReadConsole fails in Windows 7 for requested input exceeding 30 KB.
+ // Not certain about Windows 8, so check for Windows 10 here.
+ if (WinNT()<=WNT_W10)
+ SizeToRead=Min(SizeToRead,0x4000);
+
DWORD ReadSize=0;
- if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],(DWORD)Buf.size()-1,&ReadSize,NULL)==0)
- return false;
+ if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0)
+ ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort.
Buf[ReadSize]=0;
str=Buf.data();
}
#else
std::vector Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
if (fgetws(&Buf[0],Buf.size(),stdin)==NULL)
- ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
+ ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop.
str=Buf.data();
#endif
RemoveLF(str);
- return true;
}
#endif
@@ -324,22 +330,22 @@ int Ask(const wchar *AskStr)
{
uiAlarm(UIALARM_QUESTION);
- const int MaxItems=10;
+ const uint MaxItems=10;
wchar Item[MaxItems][40];
- int ItemKeyPos[MaxItems],NumItems=0;
+ uint ItemKeyPos[MaxItems],NumItems=0;
- for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
+ for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_'))
{
wchar *CurItem=Item[NumItems];
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
wchar *EndItem=wcschr(CurItem,'_');
- if (EndItem!=NULL)
+ if (EndItem!=nullptr)
*EndItem=0;
- int KeyPos=0,CurKey;
+ uint KeyPos=0,CurKey;
while ((CurKey=CurItem[KeyPos])!=0)
{
bool Found=false;
- for (int I=0;I3 ? L"\n":L" "):L", ");
- int KeyPos=ItemKeyPos[I];
- for (int J=0;JArcName;
rx.OpenMode=r->OpenMode;
rx.CmtBuf=r->CmtBuf;
@@ -32,7 +31,7 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
- DataSet *Data=NULL;
+ DataSet *Data=nullptr;
try
{
ErrHandler.Clean();
@@ -74,7 +73,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
r->OpenResult=ERAR_EOPEN;
delete Data;
- return NULL;
+ return nullptr;
}
if (!Data->Arc.IsArchive(true))
{
@@ -89,7 +88,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
r->OpenResult=ERAR_BAD_ARCHIVE;
}
delete Data;
- return NULL;
+ return nullptr;
}
r->Flags=0;
@@ -115,7 +114,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
std::wstring CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW))
{
- if (r->CmtBufW!=NULL)
+ if (r->CmtBufW!=nullptr)
{
// CmtDataW.push_back(0);
size_t Size=wcslen(CmtDataW.data())+1;
@@ -141,6 +140,18 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
else
r->CmtState=r->CmtSize=0;
+#ifdef PROPAGATE_MOTW
+ if (r->MarkOfTheWeb!=nullptr)
+ {
+ Data->Cmd.MotwAllFields=r->MarkOfTheWeb[0]=='1';
+ const wchar *Sep=wcschr(r->MarkOfTheWeb,'=');
+ if (r->MarkOfTheWeb[0]=='-')
+ Data->Cmd.MotwList.Reset();
+ else
+ Data->Cmd.GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,Data->Cmd.MotwList);
+ }
+#endif
+
Data->Extract.ExtractArchiveInit(Data->Arc);
return (HANDLE)Data;
}
@@ -494,6 +505,8 @@ static int RarErrorToDll(RAR_EXIT ErrCode)
return ERAR_BAD_PASSWORD;
case RARX_SUCCESS:
return ERAR_SUCCESS; // 0.
+ case RARX_BADARC:
+ return ERAR_BAD_ARCHIVE;
default:
return ERAR_UNKNOWN;
}
diff --git a/dll.hpp b/dll.hpp
index ac8d53f..3a8ded9 100644
--- a/dll.hpp
+++ b/dll.hpp
@@ -157,7 +157,8 @@ struct RAROpenArchiveDataEx
LPARAM UserData;
unsigned int OpFlags;
wchar_t *CmtBufW;
- unsigned int Reserved[25];
+ wchar_t *MarkOfTheWeb;
+ unsigned int Reserved[23];
};
enum UNRARCALLBACK_MESSAGES {
diff --git a/dll.rc b/dll.rc
index c959c86..47182ee 100644
--- a/dll.rc
+++ b/dll.rc
@@ -2,8 +2,8 @@
#include
VS_VERSION_INFO VERSIONINFO
-FILEVERSION 7, 1, 100, 1230
-PRODUCTVERSION 7, 1, 100, 1230
+FILEVERSION 7, 10, 2, 1436
+PRODUCTVERSION 7, 10, 2, 1436
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
@@ -14,8 +14,8 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
- VALUE "FileVersion", "7.1.0\0"
- VALUE "ProductVersion", "7.1.0\0"
+ VALUE "FileVersion", "7.10.2\0"
+ VALUE "ProductVersion", "7.10.2\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
diff --git a/errhnd.hpp b/errhnd.hpp
index 3b558b1..065350f 100644
--- a/errhnd.hpp
+++ b/errhnd.hpp
@@ -16,6 +16,7 @@ enum RAR_EXIT // RAR exit code.
RARX_NOFILES = 10,
RARX_BADPWD = 11,
RARX_READ = 12,
+ RARX_BADARC = 13,
RARX_USERBREAK = 255
};
diff --git a/extinfo.hpp b/extinfo.hpp
index 11b89fe..4d0967a 100644
--- a/extinfo.hpp
+++ b/extinfo.hpp
@@ -11,10 +11,6 @@ bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wst
std::wstring GetStreamNameNTFS(Archive &Arc);
-#ifdef _WIN_ALL
-bool SetPrivilege(LPCTSTR PrivName);
-#endif
-
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
diff --git a/extract.cpp b/extract.cpp
index f799e6a..d9f37c4 100644
--- a/extract.cpp
+++ b/extract.cpp
@@ -25,6 +25,7 @@ CmdExtract::CmdExtract(CommandData *Cmd)
#ifdef RAR_SMP
Unp->SetThreads(Cmd->Threads);
#endif
+ Unp->AllowLargePages(Cmd->UseLargePages);
}
@@ -112,6 +113,15 @@ void CmdExtract::DoExtract()
void CmdExtract::ExtractArchiveInit(Archive &Arc)
{
+ if (Cmd->Command[0]=='T' || Cmd->Command[0]=='I')
+ Cmd->Test=true;
+
+#ifdef PROPAGATE_MOTW
+ // Invoke here, so it is also supported by unrar.dll.
+ if (!Cmd->Test && Cmd->MotwList.ItemsCount()>0)
+ Arc.Motw.ReadZoneIdStream(Arc.FileName,Cmd->MotwAllFields);
+#endif
+
DataIO.AdjustTotalArcSize(&Arc);
FileCount=0;
@@ -178,12 +188,18 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
}
#endif
- mprintf(St(MNotRAR),ArcName.c_str());
-
+ bool RarExt=false;
#ifndef SFX_MODULE
- if (CmpExt(ArcName,L"rar"))
+ RarExt=CmpExt(ArcName,L"rar");
#endif
- ErrHandler.SetErrorCode(RARX_WARNING);
+
+ if (RarExt)
+ uiMsg(UIERROR_BADARCHIVE,ArcName); // Non-archive .rar file.
+ else
+ mprintf(St(MNotRAR),ArcName.c_str()); // Non-archive not .rar file, likely in "rar x *.*".
+
+ if (RarExt)
+ ErrHandler.SetErrorCode(RARX_BADARC);
return EXTRACT_ARC_NEXT;
}
@@ -254,9 +270,6 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
ExtractArchiveInit(Arc);
- if (Cmd->Command[0]=='T' || Cmd->Command[0]=='I')
- Cmd->Test=true;
-
if (Cmd->Command[0]=='I')
{
@@ -494,7 +507,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
DestFileName=!Cmd->TempPath.empty() ? Cmd->TempPath:Cmd->ExtrPath;
AddEndSlash(DestFileName);
DestFileName+=L"__tmp_reference_source_";
- MkTemp(DestFileName);
+ MkTemp(DestFileName,nullptr);
MatchedRef.TmpName=DestFileName;
}
RefTarget=true; // Need it even for 't' to test the reference source.
@@ -553,6 +566,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
return !Arc.Solid; // Can try extracting next file only in non-solid archive.
}
+#ifndef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll.
if (Arc.FileHead.Encrypted)
{
RarCheckPassword CheckPwd;
@@ -579,9 +593,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
// 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)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
ConvertDosPassword(Arc,FilePassword);
- #endif
+#endif
byte PswCheck[SIZE_PSWCHECK];
bool EncSet=DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,
@@ -609,16 +623,16 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
// Avoid new requests for unrar.dll to prevent the infinite loop
// if app always returns the same password.
- #ifndef RARDLL
+#ifndef RARDLL
continue; // Request a password again.
- #endif
+#endif
}
- #ifdef RARDLL
+#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
+#endif
ErrHandler.SetErrorCode(RARX_BADPWD);
ExtrFile=false;
}
@@ -627,6 +641,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
}
else
DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL);
+#endif // RAR_NOCRYPT
// Per file symlink conversion flag. Can be turned off in unrar.dll.
bool CurConvertSymlinkPaths=ConvertSymlinkPaths;
@@ -688,7 +703,29 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
// any overwrite prompts.
if (!CheckWinLimit(Arc,ArcFileName))
return false;
- ExtrFile=ExtrCreateFile(Arc,CurFile);
+
+ // Read+write mode is required to set "Compressed" attribute.
+ // Other than that prefer the write only mode to avoid
+ // OpenIndiana NAS problem with SetFileTime and read+write files.
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ bool Compressed=Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0;
+ bool WriteOnly=!Compressed;
+#else
+ bool WriteOnly=true;
+#endif
+
+ ExtrFile=ExtrCreateFile(Arc,CurFile,WriteOnly);
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ // 2024.03.12: Set early to compress written data immediately.
+ // For 10 GB text file it was ~1.5x faster than when set after close.
+
+ if (ExtrFile && Compressed)
+ SetFileCompression(CurFile.GetHandle(),true);
+
+#endif
+
}
if (!ExtrFile && Arc.Solid)
@@ -968,6 +1005,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
CurFile.Truncate();
+#ifdef PROPAGATE_MOTW
+ Arc.Motw.CreateZoneIdStream(DestFileName,Cmd->MotwList);
+#endif
CurFile.SetOpenFileTime(
Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
@@ -987,9 +1027,6 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (SetAttr)
{
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
- if (Cmd->SetCompressedAttr &&
- (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
- SetFileCompression(DestFileName,true);
if (Cmd->ClearArc)
Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
#endif
@@ -1406,7 +1443,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName)
}
-bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
+bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly)
{
bool Success=true;
wchar Command=Cmd->Command[0];
@@ -1417,9 +1454,7 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
if ((Command=='E' || Command=='X') && !Cmd->Test)
{
bool UserReject;
- // Specify "write only" mode to avoid OpenIndiana NAS problems
- // with SetFileTime and read+write files.
- if (!FileCreate(Cmd,&CurFile,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ if (!FileCreate(Cmd,&CurFile,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,WriteOnly))
{
Success=false;
if (!UserReject)
@@ -1479,10 +1514,16 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName)
if (Arc.FileHead.Method==0)
WrongVer=false;
+ // Can't unpack the unknown encryption even for stored files.
+ if (Arc.FileHead.CryptMethod==CRYPT_UNKNOWN)
+ WrongVer=true;
+
if (WrongVer)
{
ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
- uiMsg(UIERROR_NEWERRAR,Arc.FileName);
+ // No need to suggest a new version if it is just a broken archive.
+ if (!Arc.BrokenHeader)
+ uiMsg(UIERROR_NEWERRAR,Arc.FileName);
}
return !WrongVer;
}
diff --git a/extract.hpp b/extract.hpp
index 4400057..18d8f8c 100644
--- a/extract.hpp
+++ b/extract.hpp
@@ -37,7 +37,7 @@ class CmdExtract
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
#endif
void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName);
- bool ExtrCreateFile(Archive &Arc,File &CurFile);
+ bool ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly);
bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName);
#ifndef SFX_MODULE
void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering);
diff --git a/filefn.cpp b/filefn.cpp
index 3cb2589..d23f4f7 100644
--- a/filefn.cpp
+++ b/filefn.cpp
@@ -129,6 +129,8 @@ void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
}
+
+
bool IsRemovable(const std::wstring &Name)
{
#if defined(_WIN_ALL)
@@ -320,10 +322,10 @@ bool SetFileAttr(const std::wstring &Name,uint Attr)
}
-wchar* MkTemp(wchar *Name,size_t MaxSize)
+// Ext is the extension with the leading dot, like L".bat", or nullptr to use
+// the default extension.
+bool MkTemp(std::wstring &Name,const wchar *Ext)
{
- size_t Length=wcslen(Name);
-
RarTime CurTime;
CurTime.SetCurrentTime();
@@ -342,47 +344,20 @@ wchar* MkTemp(wchar *Name,size_t MaxSize)
for (uint Attempt=0;;Attempt++)
{
- uint Ext=Random%50000+Attempt;
- wchar RndText[50];
+ uint RandomExt=Random%50000+Attempt;
+ if (Attempt==1000)
+ return false;
+
// User asked to specify the single extension for all temporary files,
// so it can be added to server ransomware protection exceptions.
// He wrote, this protection blocks temporary files when adding
- // a file to RAR archive with drag and drop.
- swprintf(RndText,ASIZE(RndText),L"%u.%03u.rartemp",PID,Ext);
- if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
- return NULL;
- wcsncpyz(Name+Length,RndText,MaxSize-Length);
- if (!FileExist(Name))
- break;
- }
- return Name;
-}
+ // a file to RAR archive with drag and drop. So unless a calling code
+ // requires a specific extension, like .bat file when uninstalling,
+ // we set the uniform extension here.
+ if (Ext==nullptr)
+ Ext=L".rartemp";
-
-bool MkTemp(std::wstring &Name)
-{
- RarTime CurTime;
- CurTime.SetCurrentTime();
-
- // We cannot use CurTime.GetWin() as is, because its lowest bits can
- // have low informational value, like being a zero or few fixed numbers.
- uint Random=(uint)(CurTime.GetWin()/100000);
-
- // Using PID we guarantee that different RAR copies use different temp names
- // even if started in exactly the same time.
- uint PID=0;
-#ifdef _WIN_ALL
- PID=(uint)GetCurrentProcessId();
-#elif defined(_UNIX)
- PID=(uint)getpid();
-#endif
-
- for (uint Attempt=0;;Attempt++)
- {
- uint Ext=Random%50000+Attempt;
- if (Attempt==1000)
- return false;
- std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(Ext) + L".rartemp";
+ std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext;
if (!FileExist(NewName))
{
Name=NewName;
@@ -545,14 +520,21 @@ bool SetFileCompression(const std::wstring &Name,bool State)
hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
+ if (hFile==INVALID_HANDLE_VALUE)
+ return false;
}
- if (hFile==INVALID_HANDLE_VALUE)
- return false;
+ bool Success=SetFileCompression(hFile,State);
+ CloseHandle(hFile);
+ return Success;
+}
+
+
+bool SetFileCompression(HANDLE hFile,bool State)
+{
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
DWORD Result;
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
sizeof(NewState),NULL,0,&Result,NULL);
- CloseHandle(hFile);
return RetCode!=0;
}
diff --git a/filefn.hpp b/filefn.hpp
index 4e06197..8ac7c10 100644
--- a/filefn.hpp
+++ b/filefn.hpp
@@ -6,7 +6,10 @@ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr);
bool CreateDir(const std::wstring &Name);
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent);
+
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
+
+
bool IsRemovable(const std::wstring &Name);
#ifndef SFX_MODULE
@@ -28,8 +31,7 @@ bool IsDeleteAllowed(uint FileAttr);
void PrepareToDelete(const std::wstring &Name);
uint GetFileAttr(const std::wstring &Name);
bool SetFileAttr(const std::wstring &Name,uint Attr);
-wchar* MkTemp(wchar *Name,size_t MaxSize);
-bool MkTemp(std::wstring &Name);
+bool MkTemp(std::wstring &Name,const wchar *Ext);
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
@@ -41,6 +43,7 @@ bool DelDir(const std::wstring &Name);
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool SetFileCompression(const std::wstring &Name,bool State);
+bool SetFileCompression(HANDLE hFile,bool State);
void ResetFileCache(const std::wstring &Name);
#endif
diff --git a/hash.cpp b/hash.cpp
index cde3db9..d07b2d2 100644
--- a/hash.cpp
+++ b/hash.cpp
@@ -275,7 +275,9 @@ bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
{
HashValue Final;
Result(&Final);
- if (Key!=NULL)
+#ifndef RAR_NOCRYPT
+ if (Key!=nullptr)
ConvertHashToMAC(&Final,Key);
+#endif
return Final==*CmpValue;
}
diff --git a/hash.hpp b/hash.hpp
index 0189113..99488e6 100644
--- a/hash.hpp
+++ b/hash.hpp
@@ -13,6 +13,8 @@ struct HashValue
bool operator == (const HashValue &cmp) const;
// Not actually used now. Const member for same reason as operator == above.
+ // Can be removed after switching to C++20, which automatically provides "!="
+ // if operator == is defined.
bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
HASH_TYPE Type;
diff --git a/largepage.cpp b/largepage.cpp
new file mode 100644
index 0000000..7b574c5
--- /dev/null
+++ b/largepage.cpp
@@ -0,0 +1,201 @@
+#include "rar.hpp"
+
+/*
+To enable, disable or check Large Memory pages manually:
+- open "Local Security Policy" from "Start Menu";
+- open "Lock Pages in Memory" in "Local Policies\User Rights Assignment";
+- add or remove the user and sign out and sign in or restart Windows.
+*/
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
+#define ALLOW_LARGE_PAGES
+#endif
+
+LargePageAlloc::LargePageAlloc()
+{
+ UseLargePages=false;
+#ifdef ALLOW_LARGE_PAGES
+ PageSize=0;
+#endif
+}
+
+
+void LargePageAlloc::AllowLargePages(bool Allow)
+{
+#ifdef ALLOW_LARGE_PAGES
+ if (Allow && PageSize==0)
+ {
+ HMODULE hKernel=GetModuleHandle(L"kernel32.dll");
+ if (hKernel!=nullptr)
+ {
+ typedef SIZE_T (*GETLARGEPAGEMINIMUM)();
+ GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum");
+ if (pGetLargePageMinimum!=nullptr)
+ PageSize=pGetLargePageMinimum();
+ }
+ if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME))
+ {
+ UseLargePages=false;
+ return;
+ }
+ }
+
+ UseLargePages=Allow;
+#endif
+}
+
+
+bool LargePageAlloc::IsPrivilegeAssigned()
+{
+#ifdef ALLOW_LARGE_PAGES
+ return SetPrivilege(SE_LOCK_MEMORY_NAME);
+#else
+ return true;
+#endif
+}
+
+
+bool LargePageAlloc::AssignPrivilege()
+{
+#ifdef ALLOW_LARGE_PAGES
+ HANDLE hToken = NULL;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ return false;
+
+ // Get the required buffer size.
+ DWORD BufSize=0;
+ GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize);
+ if (BufSize==0 || BufSize>1000000) // Sanity check for returned value.
+ {
+ CloseHandle(hToken);
+ return false;
+ }
+
+ TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize);
+
+ // Get the current user token information.
+ if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0)
+ {
+ CloseHandle(hToken);
+ return false;
+ }
+
+ // Get SID string for the current user.
+ LPWSTR ApiSidStr;
+ ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr);
+
+ // Convert SID to C++ string and release API based buffer.
+ std::wstring SidStr=ApiSidStr;
+ LocalFree(ApiSidStr);
+ CloseHandle(hToken);
+
+ if (IsUserAdmin())
+ AssignPrivilegeBySid(SidStr);
+ else
+ {
+ // Define here, so they survive until ShellExecuteEx call.
+ std::wstring ExeName=GetModuleFileStr();
+ std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr;
+
+ SHELLEXECUTEINFO shExecInfo{};
+ shExecInfo.cbSize = sizeof(shExecInfo);
+
+ shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode.
+ shExecInfo.lpVerb = L"runas";
+ shExecInfo.lpFile = ExeName.c_str();
+ shExecInfo.lpParameters = Param.c_str();
+ shExecInfo.nShow = SW_SHOWNORMAL;
+ BOOL Result=ShellExecuteEx(&shExecInfo);
+ }
+#endif
+
+ return true;
+}
+
+
+bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid)
+{
+#ifdef ALLOW_LARGE_PAGES
+ LSA_HANDLE PolicyHandle;
+ LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it.
+
+#ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR.
+ // We define STATUS_SUCCESS here instead of including ntstatus.h to avoid
+ // macro redefinition warnings. We tried UMDF_USING_NTSTATUS define
+ // and other workarounds, but it didn't help.
+ const uint STATUS_SUCCESS=0;
+#endif
+
+ if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT|
+ POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS)
+ return false;
+
+ PSID UserSid;
+ ConvertStringSidToSid(Sid.c_str(),&UserSid);
+
+ LSA_UNICODE_STRING LsaString;
+ LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME;
+ // It must be in bytes, so multiple it to sizeof(wchar_t).
+ LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]);
+ LsaString.MaximumLength=LsaString.Length;
+
+ bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS;
+
+ LocalFree(UserSid);
+ LsaClose(PolicyHandle);
+
+ mprintf(St(MPrivilegeAssigned));
+ if (Ask(St(MYesNo)) == 1)
+ Shutdown(POWERMODE_RESTART);
+
+ return Success;
+#else
+ return true;
+#endif
+}
+
+
+bool LargePageAlloc::AssignConfirmation()
+{
+#ifdef ALLOW_LARGE_PAGES
+ mprintf(St(MLockInMemoryNeeded));
+ return Ask(St(MYesNo)) == 1;
+#else
+ return false;
+#endif
+}
+
+
+void* LargePageAlloc::new_large(size_t Size)
+{
+ void *Allocated=nullptr;
+
+#ifdef ALLOW_LARGE_PAGES
+ if (UseLargePages && Size>=PageSize)
+ {
+ // VirtualAlloc fails if allocation size isn't multiple of page size.
+ SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize;
+ Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE);
+ if (Allocated!=nullptr)
+ LargeAlloc.push_back(Allocated);
+ }
+#endif
+ return Allocated;
+}
+
+
+bool LargePageAlloc::delete_large(void *Addr)
+{
+#ifdef ALLOW_LARGE_PAGES
+ if (Addr!=nullptr)
+ for (size_t I=0;I LargeAlloc;
+ SIZE_T PageSize;
+#endif
+ bool UseLargePages;
+ public:
+ LargePageAlloc();
+ void AllowLargePages(bool Allow);
+ static bool IsPrivilegeAssigned();
+ static bool AssignPrivilege();
+ static bool AssignPrivilegeBySid(const std::wstring &Sid);
+ static bool AssignConfirmation();
+
+ static bool ProcessSwitch(CommandData *Cmd,const wchar *Switch)
+ {
+ if (Switch[0]==LOCKMEM_SWITCH[0])
+ {
+ size_t Length=wcslen(LOCKMEM_SWITCH);
+ if (wcsncmp(Switch,LOCKMEM_SWITCH,Length)==0)
+ {
+ LargePageAlloc::AssignPrivilegeBySid(Switch+Length);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template T* new_l(size_t Size,bool Clear=false)
+ {
+ T *Allocated=(T*)new_large(Size*sizeof(T));
+ if (Allocated==nullptr)
+ Allocated=Clear ? new T[Size]{} : new T[Size];
+ return Allocated;
+ }
+
+ template void delete_l(T *Addr)
+ {
+ if (!delete_large(Addr))
+ delete[] Addr;
+ }
+};
+
+
+#endif
diff --git a/list.cpp b/list.cpp
index 3eae4de..15aa73a 100644
--- a/list.cpp
+++ b/list.cpp
@@ -37,7 +37,7 @@ void ListArchive(CommandData *Cmd)
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName.c_str());
mprintf(L"\n%s: ",St(MListDetails));
- const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
+ const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 1.5":L"RAR 5");
mprintf(L"%s", Fmt);
if (Arc.Solid)
mprintf(L", %s", St(MListSolid));
diff --git a/loclang.hpp b/loclang.hpp
index df94a8e..3ee42f7 100644
--- a/loclang.hpp
+++ b/loclang.hpp
@@ -97,6 +97,7 @@
#define MCHelpSwMC L"\n mc Set advanced compression parameters"
#define MCHelpSwMD L"\n md[x][kmg] Dictionary size in KB, MB or GB"
#define MCHelpSwME L"\n me[par] Set encryption parameters"
+#define MCHelpSwMLP L"\n mlp Use large memory pages"
#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store"
#define MCHelpSwMT L"\n mt Set the number of threads"
#define MCHelpSwN L"\n n Additionally filter included files"
@@ -107,6 +108,7 @@
#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file"
#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references"
#define MCHelpSwOL L"\n ol[a,-] Process symbolic links as the link [absolute paths, skip]"
+#define MCHelpSwOM L"\n om[-|1][=lst] Propagate Mark of the Web"
#define MCHelpSwONI L"\n oni Allow potentially incompatible names"
#define MCHelpSwOP L"\n op Set the output path for extracted files"
#define MCHelpSwOR L"\n or Rename files automatically"
@@ -232,8 +234,8 @@
#define MArcComment L"\nArchive comment"
#define MReadStdinCmt L"\nReading comment from stdin\n"
#define MReadCommFrom L"\nReading comment from %s"
-#define MDelComment L"\nDeleting comment from %s"
-#define MAddComment L"\nAdding comment to %s"
+#define MDelComment L"\nDeleting a comment from %s"
+#define MAddComment L"\nAdding a comment to %s"
#define MLogCommBrk L"\nThe archive comment is corrupt"
#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:"
#define MWriteCommTo L"\nWrite comment to %s"
@@ -302,7 +304,7 @@
#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged"
#define MRecCorrected L" - data recovered"
#define MRecFailed L" - cannot recover data"
-#define MAddRecRec L"\nAdding data recovery record"
+#define MAddRecRec L"\nAdding the data recovery record"
#define MEraseForVolume L"\n\nErasing contents of drive %c:\n"
#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n"
#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n"
@@ -329,7 +331,6 @@
#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored"
#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored"
#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block"
-#define MSubHeadType L"\nData header type: %s"
#define MScanError L"\nCannot read contents of %s"
#define MNotVolume L"\n%s is not volume"
#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets"
@@ -384,7 +385,7 @@
#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary."
#define MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u GB compression dictionary."
#define MSuggest64bit L"\n64-bit RAR version is necessary."
-#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file."
+#define MOpenErrAtime L"\nYou may need to remove -tsp switch or run RAR as administrator to open this file."
#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort."
#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n"
#define MErrReadCount L"\n%u files are archived incompletely because of read errors."
@@ -402,3 +403,8 @@
#define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data."
#define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary."
#define MSkipUnsafeLink L"\nSkipping the potentially unsafe %s -> %s link. For archives from a trustworthy source use -ola to extract it anyway."
+#define MTruncService L"\nTruncated at the service block: %s"
+#define MHeaderQO L"quick open information"
+#define MHeaderRR L"recovery record"
+#define MLockInMemoryNeeded L"-mlp switch requires ""Lock pages in memory"" privilege. Do you wish to assign it to the current user account?"
+#define MPrivilegeAssigned L"User privilege has been successfully assigned and will be activated after Windows restart. Restart now?"
diff --git a/makefile b/makefile
index ce54a02..f7fe61a 100644
--- a/makefile
+++ b/makefile
@@ -2,8 +2,11 @@
# Makefile for UNIX - unrar
# Linux using GCC
+# 2024.08.19: -march=native isn't recognized on some platforms such as RISCV64.
+# Thus we removed it. Clang ARM users can add -march=armv8-a+crypto to enable
+# ARM NEON crypto.
CXX=c++
-CXXFLAGS=-march=native -O2 -std=c++11 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
+CXXFLAGS=-O2 -std=c++11 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
LIBFLAGS=-fPIC
DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
STRIP=strip
@@ -25,7 +28,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 largepage.o
.cpp.o:
$(COMPILE) -D$(WHAT) -c $<
diff --git a/motw.cpp b/motw.cpp
new file mode 100644
index 0000000..25698fe
--- /dev/null
+++ b/motw.cpp
@@ -0,0 +1,131 @@
+#include "rar.hpp"
+
+/* Zone.Identifier stream can include the text like:
+
+[ZoneTransfer]
+ZoneId=3
+HostUrl=https://site/path/file.ext
+ReferrerUrl=d:\path\archive.ext
+
+Where ZoneId can be:
+
+ 0 = My Computer
+ 1 = Local intranet
+ 2 = Trusted sites
+ 3 = Internet
+ 4 = Restricted sites
+*/
+
+MarkOfTheWeb::MarkOfTheWeb()
+{
+ ZoneIdValue=-1; // -1 indicates the missing MOTW.
+ AllFields=false;
+}
+
+
+void MarkOfTheWeb::Clear()
+{
+ ZoneIdValue=-1;
+}
+
+
+void MarkOfTheWeb::ReadZoneIdStream(const std::wstring &FileName,bool AllFields)
+{
+ MarkOfTheWeb::AllFields=AllFields;
+ ZoneIdValue=-1;
+ ZoneIdStream.clear();
+
+ std::wstring StreamName=FileName+MOTW_STREAM_NAME;
+
+ File SrcFile;
+ if (SrcFile.Open(StreamName))
+ {
+ ZoneIdStream.resize(MOTW_STREAM_MAX_SIZE);
+ int BufSize=SrcFile.Read(&ZoneIdStream[0],ZoneIdStream.size());
+ ZoneIdStream.resize(BufSize<0 ? 0:BufSize);
+
+ if (BufSize<=0)
+ return;
+
+ ZoneIdValue=ParseZoneIdStream(ZoneIdStream);
+ }
+}
+
+
+// 'Stream' contains the raw "Zone.Identifier" NTFS stream data on input
+// and either raw or cleaned stream data on output.
+int MarkOfTheWeb::ParseZoneIdStream(std::string &Stream)
+{
+ if (Stream.rfind("[ZoneTransfer]",0)==std::string::npos)
+ return -1; // Not a valid Mark of the Web. Prefer the archive MOTW if any.
+
+ std::string::size_type ZoneId=Stream.find("ZoneId=",0);
+ if (ZoneId==std::string::npos || !IsDigit(Stream[ZoneId+7]))
+ return -1; // Not a valid Mark of the Web.
+ int ZoneIdValue=atoi(&Stream[ZoneId+7]);
+ if (ZoneIdValue<0 || ZoneIdValue>4)
+ return -1; // Not a valid Mark of the Web.
+
+ if (!AllFields)
+ Stream="[ZoneTransfer]\r\nZoneId=" + std::to_string(ZoneIdValue) + "\r\n";
+
+ return ZoneIdValue;
+}
+
+
+void MarkOfTheWeb::CreateZoneIdStream(const std::wstring &Name,StringList &MotwList)
+{
+ if (ZoneIdValue==-1)
+ return;
+
+ size_t ExtPos=GetExtPos(Name);
+ const wchar *Ext=ExtPos==std::wstring::npos ? L"":&Name[ExtPos+1];
+
+ bool Matched=false;
+ wchar *CurMask;
+ MotwList.Rewind();
+ while ((CurMask=MotwList.GetString())!=nullptr)
+ {
+ // Perform the fast extension comparison for simple *.ext masks.
+ // When extracting 100000 files with "Exe and office" masks set,
+ // this loop spends 1.14s without this optimization and 0.24s with it.
+ bool FastCmp=CurMask[0]=='*' && CurMask[1]=='.' && !IsWildcard(CurMask+2);
+ if (FastCmp && wcsicomp(Ext,CurMask+2)==0 || !FastCmp && CmpName(CurMask,Name,MATCH_NAMES))
+ {
+ Matched=true;
+ break;
+ }
+ }
+
+ if (!Matched)
+ return;
+
+ std::wstring StreamName=Name+MOTW_STREAM_NAME;
+
+ File StreamFile;
+ if (StreamFile.Create(StreamName)) // Can fail on FAT.
+ {
+ // We got a report that write to stream failed on Synology 2411+ NAS drive.
+ // So we handle it silently instead of aborting.
+ StreamFile.SetExceptions(false);
+ if (StreamFile.Write(&ZoneIdStream[0],ZoneIdStream.size()))
+ StreamFile.Close();
+ }
+}
+
+
+bool MarkOfTheWeb::IsNameConflicting(const std::wstring &StreamName)
+{
+ // We must use the case insensitive comparison for L":Zone.Identifier"
+ // to catch specially crafted archived streams like L":zone.identifier".
+ return wcsicomp(StreamName,MOTW_STREAM_NAME)==0 && ZoneIdValue!=-1;
+}
+
+
+// Return true and prepare the file stream to write if its ZoneId is stricter
+// than archive ZoneId. If it is missing, less or equally strict, return false.
+bool MarkOfTheWeb::IsFileStreamMoreSecure(std::string &FileStream)
+{
+ int StreamZone=ParseZoneIdStream(FileStream);
+ return StreamZone>ZoneIdValue;
+}
diff --git a/motw.hpp b/motw.hpp
new file mode 100644
index 0000000..e24feea
--- /dev/null
+++ b/motw.hpp
@@ -0,0 +1,26 @@
+#ifndef _RAR_MOTW_
+#define _RAR_MOTW_
+
+class MarkOfTheWeb
+{
+ private:
+ const size_t MOTW_STREAM_MAX_SIZE=1024;
+ const wchar* MOTW_STREAM_NAME=L":Zone.Identifier"; // Must start from ':'.
+
+ int ParseZoneIdStream(std::string &Stream);
+
+ std::string ZoneIdStream; // Store archive ":Zone.Identifier" NTFS stream data.
+ int ZoneIdValue; // -1 if missing.
+ bool AllFields; // Copy all MOTW fields or ZoneId only.
+ public:
+ MarkOfTheWeb();
+ void Clear();
+ void ReadZoneIdStream(const std::wstring &FileName,bool AllFields);
+ void CreateZoneIdStream(const std::wstring &Name,StringList &MotwList);
+ bool IsNameConflicting(const std::wstring &StreamName);
+ bool IsFileStreamMoreSecure(std::string &FileStream);
+};
+
+#endif
+
+
diff --git a/options.hpp b/options.hpp
index 1061bd0..4fa6841 100644
--- a/options.hpp
+++ b/options.hpp
@@ -52,7 +52,7 @@ enum ARC_METADATA
ARCMETA_RESTORE // -amr
};
-enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
+enum QOPEN_MODE { QOPEN_NONE=0, QOPEN_AUTO, QOPEN_ALWAYS };
enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 };
@@ -168,6 +168,14 @@ class RAROptions
bool SkipSymLinks;
int Priority;
int SleepTime;
+
+ bool UseLargePages;
+
+ // Quit after processing some system integration related switch,
+ // like enabling the large memory pages privilege.
+ // menu for non-admin user and quit.
+ bool SetupComplete;
+
bool KeepBroken;
bool OpenShared;
bool DeleteFiles;
@@ -185,6 +193,9 @@ class RAROptions
bool SyncFiles;
bool ProcessEA;
bool SaveStreams;
+#ifdef PROPAGATE_MOTW
+ bool MotwAllFields;
+#endif
bool SetCompressedAttr;
bool IgnoreGeneralAttr;
RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore;
diff --git a/os.hpp b/os.hpp
index b671846..f7e841b 100644
--- a/os.hpp
+++ b/os.hpp
@@ -8,12 +8,13 @@
#define SILENT
#endif
+
#include
#include
#include
#include
#include // For automatic pointers.
-
+#include
#ifdef _WIN_ALL
@@ -57,6 +58,9 @@
#include
#include
#include
+#include
+#include
+
// For WMI requests.
#include
diff --git a/rar.hpp b/rar.hpp
index 38cd744..d31ecd8 100644
--- a/rar.hpp
+++ b/rar.hpp
@@ -32,6 +32,9 @@
#ifdef _WIN_ALL
#include "isnt.hpp"
#endif
+#ifdef PROPAGATE_MOTW
+#include "motw.hpp"
+#endif
#include "file.hpp"
#include "crc.hpp"
#include "filefn.hpp"
@@ -61,6 +64,8 @@
#include "threadpool.hpp"
+#include "largepage.hpp"
+
#include "unpack.hpp"
diff --git a/rardefs.hpp b/rardefs.hpp
index 433010f..378ccc7 100644
--- a/rardefs.hpp
+++ b/rardefs.hpp
@@ -21,7 +21,7 @@
// the excessive memory allocation for dynamically allocated strings.
#define MAXPATHSIZE 0x10000
-#define MAXSFXSIZE 0x200000
+#define MAXSFXSIZE 0x400000
#define MAXCMTSIZE 0x40000
@@ -47,4 +47,8 @@
// Produce the value, which is equal or larger than 'v' and aligned to 'a'.
#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) )
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+#define PROPAGATE_MOTW // Propagate the archive Mark of the Web.
+#endif
+
#endif
diff --git a/smallfn.cpp b/smallfn.cpp
index 81259d0..924e195 100644
--- a/smallfn.cpp
+++ b/smallfn.cpp
@@ -15,5 +15,3 @@ int ToPercentUnlim(int64 N1,int64 N2)
return 0;
return (int)(N1*100/N2);
}
-
-
diff --git a/smallfn.hpp b/smallfn.hpp
index f53daa8..1e706a5 100644
--- a/smallfn.hpp
+++ b/smallfn.hpp
@@ -3,6 +3,5 @@
int ToPercent(int64 N1,int64 N2);
int ToPercentUnlim(int64 N1,int64 N2);
-void RARInitData();
#endif
diff --git a/system.cpp b/system.cpp
index cf9d183..b8f96cb 100644
--- a/system.cpp
+++ b/system.cpp
@@ -101,19 +101,35 @@ void Wait()
-#if defined(_WIN_ALL) && !defined(SFX_MODULE)
-void Shutdown(POWER_MODE Mode)
+#ifdef _WIN_ALL
+bool SetPrivilege(LPCTSTR PrivName)
{
+ bool Success=false;
+
HANDLE hToken;
- TOKEN_PRIVILEGES tkp;
- if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
- LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
- tkp.PrivilegeCount = 1;
- tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ TOKEN_PRIVILEGES tp;
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
+ if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) &&
+ AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
+ GetLastError() == ERROR_SUCCESS)
+ Success=true;
+
+ CloseHandle(hToken);
}
+
+ return Success;
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void Shutdown(POWER_MODE Mode)
+{
+ SetPrivilege(SE_SHUTDOWN_NAME);
if (Mode==POWERMODE_OFF)
ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED);
if (Mode==POWERMODE_SLEEP)
diff --git a/system.hpp b/system.hpp
index de4c14a..ff6a669 100644
--- a/system.hpp
+++ b/system.hpp
@@ -22,6 +22,9 @@ void SetPriority(int Priority);
clock_t MonoClock();
void Wait();
bool EmailFile(const std::wstring &FileName,std::wstring MailToW);
+#ifdef _WIN_ALL
+bool SetPrivilege(LPCTSTR PrivName);
+#endif
void Shutdown(POWER_MODE Mode);
bool ShutdownCheckAnother(bool Open);
diff --git a/timefn.hpp b/timefn.hpp
index 6c265ec..5b11e86 100644
--- a/timefn.hpp
+++ b/timefn.hpp
@@ -40,12 +40,12 @@ class RarTime
static const uint REMINDER_PRECISION = TICKS_PER_SECOND;
public:
RarTime() {Reset();}
- bool operator == (RarTime &rt) {return itime==rt.itime;}
- bool operator != (RarTime &rt) {return itime!=rt.itime;}
- bool operator < (RarTime &rt) {return itime (RarTime &rt) {return itime>rt.itime;}
- bool operator >= (RarTime &rt) {return itime>rt.itime || itime==rt.itime;}
+ bool operator == (const RarTime &rt) const {return itime==rt.itime;}
+ bool operator != (const RarTime &rt) const {return itime!=rt.itime;}
+ bool operator < (const RarTime &rt) const {return itime (const RarTime &rt) const {return itime>rt.itime;}
+ bool operator >= (const RarTime &rt) const {return itime>rt.itime || itime==rt.itime;}
void GetLocal(RarLocalTime *lt);
void SetLocal(RarLocalTime *lt);
diff --git a/ui.hpp b/ui.hpp
index f37001b..7f8dac7 100644
--- a/ui.hpp
+++ b/ui.hpp
@@ -26,21 +26,21 @@ enum UIMESSAGE_CODE {
UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS,
UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING,
UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF,
- UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME,
- UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED,
- UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED,
- UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT,
- UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA,
- UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX,
- UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_NEEEDSFX64,
- UIERROR_EMAIL, UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN,
- UIERROR_ACLSET, UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN,
- UIERROR_INCOMPATSWITCH, UIERROR_PATHTOOLONG, UIERROR_DIRSCAN,
- UIERROR_UOWNERGET, UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID,
- UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET, UIERROR_ULINKREAD,
- UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED,
- UIERROR_READERRCOUNT, UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW,
- UIERROR_ADJUSTVALUE, UIERROR_SKIPUNSAFELINK,
+ UIERROR_TRUNCSERVICE, UIERROR_BADARCHIVE, UIERROR_CMTBROKEN,
+ UIERROR_INVALIDNAME, UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED,
+ UIERROR_ENCRNOTSUPPORTED, UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT,
+ UIERROR_NOFILESREPAIRED, UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE,
+ UIERROR_NOFILESTOEXTRACT, UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL,
+ UIERROR_UNKNOWNEXTRA, UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED,
+ UIERROR_ZIPVOLSFX, UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX,
+ UIERROR_NEEEDSFX64, UIERROR_EMAIL, UIERROR_ACLGET, UIERROR_ACLBROKEN,
+ UIERROR_ACLUNKNOWN, UIERROR_ACLSET, UIERROR_STREAMBROKEN,
+ UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, UIERROR_PATHTOOLONG,
+ UIERROR_DIRSCAN, UIERROR_UOWNERGET, UIERROR_UOWNERBROKEN,
+ UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET,
+ UIERROR_ULINKREAD, UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME,
+ UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, UIERROR_DIRNAMEEXISTS,
+ UIERROR_TRUNCPSW, UIERROR_ADJUSTVALUE, UIERROR_SKIPUNSAFELINK,
UIMSG_FIRST,
UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA,
@@ -50,7 +50,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_FILERENAME,
+ UIMSG_SKIPENCARC, UIMSG_FILERENAME,
UIWAIT_FIRST,
UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW,
diff --git a/uiconsole.cpp b/uiconsole.cpp
index cc1c72d..71559f6 100644
--- a/uiconsole.cpp
+++ b/uiconsole.cpp
@@ -45,10 +45,8 @@ UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime
if (AllowRename && Choice==5)
{
mprintf(St(MAskNewName));
- if (getwstr(Name))
- return UIASKREP_R_RENAME;
- else
- return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'.
+ getwstr(Name);
+ return UIASKREP_R_RENAME;
}
return UIASKREP_R_CANCEL;
}
@@ -245,6 +243,18 @@ void uiMsgStore::Msg()
case UIERROR_UNEXPEOF:
Log(Str[0],St(MLogUnexpEOF));
break;
+ case UIERROR_TRUNCSERVICE:
+ {
+ const wchar *Type=nullptr;
+ if (wcscmp(Str[1],SUBHEAD_TYPE_QOPEN)==0)
+ Type=St(MHeaderQO);
+ else
+ if (wcscmp(Str[1],SUBHEAD_TYPE_RR)==0)
+ Type=St(MHeaderRR);
+ if (Type!=nullptr)
+ Log(Str[0],St(MTruncService),Type);
+ }
+ break;
case UIERROR_BADARCHIVE:
Log(Str[0],St(MBadArc),Str[0]);
break;
diff --git a/unicode.cpp b/unicode.cpp
index 812e35b..e6d4977 100644
--- a/unicode.cpp
+++ b/unicode.cpp
@@ -272,8 +272,12 @@ void WideToRaw(const std::wstring &Src,std::vector &Dest)
Dest.push_back((byte)C);
Dest.push_back((byte)(C>>8));
}
- Dest.push_back(0); // 2 bytes of trailing UTF-16 zero.
- Dest.push_back(0);
+ // In STL version of this function we do not add the trailing zero.
+ // Otherwise we would need to remove it when restoring std::wstring
+ // from raw data.
+
+ // Dest.push_back(0); // 2 bytes of trailing UTF-16 zero.
+ // Dest.push_back(0);
}
diff --git a/unpack.cpp b/unpack.cpp
index 99c6f15..1b13293 100644
--- a/unpack.cpp
+++ b/unpack.cpp
@@ -53,7 +53,7 @@ Unpack::~Unpack()
InitFilters30(false);
#endif
- free(Window);
+ Alloc.delete_l(Window); // delete Window;
#ifdef RAR_SMP
delete UnpThreadPool;
delete[] ReadBufMT;
@@ -123,11 +123,19 @@ void Unpack::Init(uint64 WinSize,bool Solid)
if (Solid && (Window!=NULL || Fragmented && WinSize>FragWindow.GetWinSize()))
throw std::bad_alloc();
- free(Window);
+ Alloc.delete_l(Window); // delete Window;
+ Window=nullptr;
- Window=Fragmented ? NULL : (byte *)malloc((size_t)WinSize);
+ try
+ {
+ if (!Fragmented)
+ Window=Alloc.new_l((size_t)WinSize,false); // Window=new byte[(size_t)WinSize];
+ }
+ catch (std::bad_alloc) // Use the fragmented window in this case.
+ {
+ }
- if (Window==NULL)
+ if (Window==nullptr)
if (WinSize<0x1000000 || sizeof(size_t)>4)
throw std::bad_alloc(); // Exclude RAR4, small dictionaries and 64-bit.
else
diff --git a/unpack.hpp b/unpack.hpp
index 5ed904a..8fa287a 100644
--- a/unpack.hpp
+++ b/unpack.hpp
@@ -55,7 +55,7 @@ struct DecodeTable:PackDef
// Translates compressed bits (up to QuickBits length)
// to position in alphabet in quick mode.
// 'ushort' saves some memory and even provides a little speed gain
- // comparting to 'uint' here.
+ // comparing to 'uint' here.
ushort QuickNum[1< FilterSrcMemory;
std::vector FilterDstMemory;
@@ -382,6 +384,7 @@ class Unpack:PackDef
Unpack(ComprDataIO *DataIO);
~Unpack();
void Init(uint64 WinSize,bool Solid);
+ void AllowLargePages(bool Allow) {Alloc.AllowLargePages(Allow);}
void DoUnpack(uint Method,bool Solid);
bool IsFileExtracted() {return FileExtracted;}
void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;}
diff --git a/version.hpp b/version.hpp
index 6987a9a..1fa5327 100644
--- a/version.hpp
+++ b/version.hpp
@@ -1,6 +1,6 @@
#define RARVER_MAJOR 7
-#define RARVER_MINOR 1
-#define RARVER_BETA 0
-#define RARVER_DAY 12
-#define RARVER_MONTH 5
+#define RARVER_MINOR 10
+#define RARVER_BETA 2
+#define RARVER_DAY 4
+#define RARVER_MONTH 12
#define RARVER_YEAR 2024
diff --git a/volume.cpp b/volume.cpp
index e171515..e0b5243 100644
--- a/volume.cpp
+++ b/volume.cpp
@@ -142,7 +142,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma
// replacing an encrypted header volume to unencrypted and adding
// unexpected files by third party to encrypted extraction.
uiMsg(UIERROR_BADARCHIVE,Arc.FileName);
- ErrHandler.Exit(RARX_FATAL);
+ ErrHandler.Exit(RARX_BADARC);
}
if (SplitHeader)
diff --git a/win32acl.cpp b/win32acl.cpp
index 36cb852..091838f 100644
--- a/win32acl.cpp
+++ b/win32acl.cpp
@@ -110,26 +110,3 @@ void SetACLPrivileges()
InitDone=true;
}
-
-
-bool SetPrivilege(LPCTSTR PrivName)
-{
- bool Success=false;
-
- HANDLE hToken;
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- {
- TOKEN_PRIVILEGES tp;
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) &&
- AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
- GetLastError() == ERROR_SUCCESS)
- Success=true;
-
- CloseHandle(hToken);
- }
-
- return Success;
-}
diff --git a/win32stm.cpp b/win32stm.cpp
index 0c9c72a..0c7e88a 100644
--- a/win32stm.cpp
+++ b/win32stm.cpp
@@ -2,8 +2,21 @@
#ifdef _WIN_ALL
// StreamName must include the leading ':'.
-static bool IsNtfsReservedStream(const std::wstring &StreamName)
+static bool IsNtfsProhibitedStream(const std::wstring &StreamName)
{
+ // 2024.03.14: We replaced the predefined names check with simpler
+ // "no more than a single colon" check. Second colon could be used to
+ // define the type of alternate stream, but RAR archives work only with
+ // data streams and do not store :$DATA type in archive. It is assumed.
+ // So there is no legitimate use for stream type inside of archive,
+ // but it can be abused to hide the actual file data in file::$DATA
+ // or hide the actual MOTW data in Zone.Identifier:$DATA.
+ uint ColonCount=0;
+ for (wchar Ch:StreamName)
+ if (Ch==':' && ++ColonCount>1)
+ return true;
+ return false;
+/*
const wchar *Reserved[]{
L"::$ATTRIBUTE_LIST",L"::$BITMAP",L"::$DATA",L"::$EA",L"::$EA_INFORMATION",
L"::$FILE_NAME",L"::$INDEX_ALLOCATION",L":$I30:$INDEX_ALLOCATION",
@@ -14,6 +27,7 @@ static bool IsNtfsReservedStream(const std::wstring &StreamName)
if (wcsicomp(StreamName,Name)==0)
return true;
return false;
+*/
}
#endif
@@ -36,41 +50,45 @@ void ExtractStreams20(Archive &Arc,const std::wstring &FileName)
}
std::wstring StreamName;
- if (FileName.size()==1)
- {
- // Convert single character names like f:stream to .\f:stream to
- // resolve the ambiguity with drive letters.
- StreamName=L".\\"+FileName;
- }
- else
- StreamName=FileName;
- if (Arc.StreamHead.StreamName[0]!=':')
+ CharToWide(Arc.StreamHead.StreamName,StreamName);
+
+ if (StreamName[0]!=':')
{
uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName);
ErrHandler.SetErrorCode(RARX_CRC);
return;
}
- std::wstring StoredName;
- // "substr(1)" to exclude ':', so we can use ConvertPath() below.
- CharToWide(Arc.StreamHead.StreamName.substr(1),StoredName);
- ConvertPath(&StoredName,&StoredName);
-
+ // Convert single character names like f:stream to .\f:stream to
+ // resolve the ambiguity with drive letters.
+ std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName;
+ FullName+=StreamName;
- StoredName=L":"+StoredName;
- if (IsNtfsReservedStream(StoredName))
+#ifdef PROPAGATE_MOTW
+ // 2022.10.31: Can't easily read RAR 2.0 stream data here, so if we already
+ // propagated the archive Zone.Identifier stream, also known as Mark of
+ // the Web, to extracted file, we do not overwrite it here.
+ if (Arc.Motw.IsNameConflicting(StreamName))
return;
- StreamName+=StoredName;
+ // 2024.02.03: Prevent using Zone.Identifier:$DATA to overwrite Zone.Identifier
+ // according to ZDI-CAN-23156 Trend Micro report.
+ // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream(().
+ // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0)
+ // return;
+#endif
+
+ if (IsNtfsProhibitedStream(StreamName))
+ return;
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(StreamName))
+ if (CurFile.WCreate(FullName))
{
ComprDataIO DataIO;
Unpack Unpack(&DataIO);
@@ -91,10 +109,15 @@ void ExtractStreams20(Archive &Arc,const std::wstring &FileName)
else
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);
+
+ // Restoring original file attributes.
+ // Important if file was read only or did not have "Archive" attribute.
if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
SetFileAttr(FileName,FD.FileAttr);
}
@@ -104,16 +127,6 @@ void ExtractStreams20(Archive &Arc,const std::wstring &FileName)
#ifdef _WIN_ALL
void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode)
{
- std::wstring FullName;
- if (FileName[0]!=0 && FileName[1]==0)
- {
- // Convert single character names like f:stream to .\f:stream to
- // resolve the ambiguity with drive letters.
- FullName=L".\\"+FileName;
- }
- else
- FullName=FileName;
-
std::wstring StreamName=GetStreamNameNTFS(Arc);
if (StreamName[0]!=':')
{
@@ -125,14 +138,43 @@ void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode)
if (TestMode)
{
File CurFile;
- Arc.ReadSubData(NULL,&CurFile,true);
+ Arc.ReadSubData(nullptr,&CurFile,true);
return;
}
+ // Convert single character names like f:stream to .\f:stream to
+ // resolve the ambiguity with drive letters.
+ std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName;
FullName+=StreamName;
+#ifdef PROPAGATE_MOTW
+ // 2022.10.31: If we already propagated the archive Zone.Identifier stream,
+ // also known as Mark of the Web, to extracted file, we overwrite it here
+ // only if file zone is stricter. Received a user request for such behavior.
- if (IsNtfsReservedStream(StreamName))
+ std::string ParsedMotw;
+ if (Arc.Motw.IsNameConflicting(StreamName))
+ {
+ // Do not worry about excessive memory allocation, ReadSubData prevents it.
+ std::vector FileMotw;
+ if (!Arc.ReadSubData(&FileMotw,nullptr,false))
+ return;
+ ParsedMotw.assign(FileMotw.begin(),FileMotw.end());
+
+ // We already set the archive stream. If file stream value isn't more
+ // restricted, we do not want to write it over the existing archive stream.
+ if (!Arc.Motw.IsFileStreamMoreSecure(ParsedMotw))
+ return;
+ }
+
+ // 2024.02.03: Prevent using :Zone.Identifier:$DATA to overwrite :Zone.Identifier
+ // according to ZDI-CAN-23156 Trend Micro report.
+ // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream(().
+ // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0)
+ // return;
+#endif
+
+ if (IsNtfsProhibitedStream(StreamName))
return;
FindData FD;
@@ -144,7 +186,17 @@ void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode)
if (CurFile.WCreate(FullName))
{
- if (Arc.ReadSubData(NULL,&CurFile,false))
+#ifdef PROPAGATE_MOTW
+ if (!ParsedMotw.empty())
+ {
+ // The archive propagated security zone is either missing
+ // or less strict than file one. Write the file security zone here.
+ CurFile.Write(ParsedMotw.data(),ParsedMotw.size());
+ CurFile.Close();
+ }
+ else
+#endif
+ if (Arc.ReadSubData(nullptr,&CurFile,false))
CurFile.Close();
}
@@ -154,9 +206,10 @@ void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode)
SetFileTime(HostFile.GetHandle(),&FD.ftCreationTime,&FD.ftLastAccessTime,
&FD.ftLastWriteTime);
- // Restoring original file attributes. Important if file was read only
- // or did not have "Archive" attribute
- SetFileAttr(FileName,FD.FileAttr);
+ // Restoring original file attributes.
+ // Important if file was read only or did not have "Archive" attribute.
+ if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
+ SetFileAttr(FileName,FD.FileAttr);
}
#endif
@@ -168,9 +221,8 @@ std::wstring GetStreamNameNTFS(Archive &Arc)
Dest=RawToWide(Arc.SubHead.SubData);
else
{
- std::vector Src=Arc.SubHead.SubData;
- Src.push_back(0); // Needed for our UtfToWide.
- UtfToWide((char *)Src.data(),Dest);
+ std::string Src(Arc.SubHead.SubData.begin(),Arc.SubHead.SubData.end());
+ UtfToWide(Src.data(),Dest);
}
return Dest;
}