diff --git a/acknow.txt b/acknow.txt index a68b672..60a772f 100644 --- a/acknow.txt +++ b/acknow.txt @@ -51,7 +51,7 @@ * RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code is available here: - http://sourceforge.net/projects/slicing-by-8/ + https://sourceforge.net/projects/slicing-by-8/ Original Intel Slicing-by-8 code is licensed under BSD License available at http://www.opensource.org/licenses/bsd-license.html diff --git a/arccmt.cpp b/arccmt.cpp index 92a3896..8b7e498 100644 --- a/arccmt.cpp +++ b/arccmt.cpp @@ -4,8 +4,15 @@ bool Archive::GetComment(Array *CmtData) { if (!MainComment) return false; - SaveFilePos SavePos(*this); + int64 SavePos=Tell(); + bool Success=DoGetComment(CmtData); + Seek(SavePos,SEEK_SET); + return Success; +} + +bool Archive::DoGetComment(Array *CmtData) +{ #ifndef SFX_MODULE uint CmtLength; if (Format==RARFMT14) @@ -136,7 +143,7 @@ bool Archive::GetComment(Array *CmtData) bool Archive::ReadCommentData(Array *CmtData) { Array CmtRaw; - if (!ReadSubData(&CmtRaw,NULL)) + if (!ReadSubData(&CmtRaw,NULL,false)) return false; size_t CmtSize=CmtRaw.Size(); CmtRaw.Push(0); diff --git a/archive.cpp b/archive.cpp index f07ed05..8c5a1da 100644 --- a/archive.cpp +++ b/archive.cpp @@ -208,8 +208,8 @@ bool Archive::IsArchive(bool EnableBroken) break; } - // This check allows to make RS based recovery even if password is incorrect. - // But we should not do it for EnableBroken or we'll get 'not RAR archive' + + // We should not do it for EnableBroken or we'll get 'not RAR archive' // messages when extracting encrypted archives with wrong password. if (FailedHeaderDecryption && !EnableBroken) return false; @@ -233,7 +233,7 @@ bool Archive::IsArchive(bool EnableBroken) // immediately after IsArchive call. if (HeadersLeft && (!SilentOpen || !Encrypted)) { - SaveFilePos SavePos(*this); + int64 SavePos=Tell(); int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; HEADER_TYPE SaveCurHeaderType=CurHeaderType; @@ -262,6 +262,7 @@ bool Archive::IsArchive(bool EnableBroken) CurBlockPos=SaveCurBlockPos; NextBlockPos=SaveNextBlockPos; CurHeaderType=SaveCurHeaderType; + Seek(SavePos,SEEK_SET); } if (!Volume || FirstVolume) wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName)); diff --git a/archive.hpp b/archive.hpp index 4edb481..d9518f1 100644 --- a/archive.hpp +++ b/archive.hpp @@ -29,7 +29,6 @@ class Archive:public File void UpdateLatestTime(FileHeader *CurBlock); void ConvertNameCase(wchar *Name); void ConvertFileHeader(FileHeader *hd); - void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite); size_t ReadHeader14(); size_t ReadHeader15(); size_t ReadHeader50(); @@ -38,6 +37,7 @@ class Archive:public File void UnexpEndArcMsg(); void BrokenHeaderMsg(); void UnkEncVerMsg(const wchar *Name,const wchar *Info); + bool DoGetComment(Array *CmtData); bool ReadCommentData(Array *CmtData); #if !defined(RAR_NOCRYPT) @@ -65,8 +65,6 @@ class Archive:public File size_t SearchBlock(HEADER_TYPE HeaderType); size_t SearchSubBlock(const wchar *Type); size_t SearchRR(); - void WriteBlock(HEADER_TYPE HeaderType,BaseBlock *wb=NULL,bool OnlySetSize=false,bool NonFinalWrite=false); - void SetBlockSize(HEADER_TYPE HeaderType,BaseBlock *wb=NULL) {WriteBlock(HeaderType,wb,true);} size_t ReadHeader(); void CheckArc(bool EnableBroken); void CheckOpen(const wchar *Name); @@ -83,7 +81,7 @@ class Archive:public File int64 GetStartPos(); void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, const wchar *Name,uint Flags); - bool ReadSubData(Array *UnpData,File *DestFile); + bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode); HEADER_TYPE GetHeaderType() {return CurHeaderType;} RAROptions* GetRAROptions() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} diff --git a/arcread.cpp b/arcread.cpp index c299d3d..d1df6c0 100644 --- a/arcread.cpp +++ b/arcread.cpp @@ -268,14 +268,14 @@ size_t Archive::ReadHeader15() uint FileTime=Raw.Get4(); hd->UnpVer=Raw.Get1(); - // RAR15 did not use the special dictionary size to mark dirs. - if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) - hd->Dir=true; - hd->Method=Raw.Get1()-0x30; size_t NameSize=Raw.Get2(); hd->FileAttr=Raw.Get4(); + // RAR15 did not use the special dictionary size to mark dirs. + if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) + hd->Dir=true; + hd->CryptMethod=CRYPT_NONE; if (hd->Encrypted) switch(hd->UnpVer) @@ -402,8 +402,8 @@ size_t Archive::ReadHeader15() if (rmode & 4) rlt.Second++; rlt.Reminder=0; - int count=rmode&3; - for (int J=0;JPassword.Clean(); } @@ -720,6 +719,7 @@ size_t Archive::ReadHeader50() UnkEncVerMsg(FileName,Info); return 0; } + Raw.GetB(CryptHead.Salt,SIZE_SALT50); if (CryptHead.UsePswCheck) { @@ -785,7 +785,7 @@ size_t Archive::ReadHeader50() case HEAD_SERVICE: { FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; - hd->Reset(); + hd->Reset(); // Clear hash, time fields and other stuff like flags. *(BaseBlock *)hd=ShortBlock; bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; @@ -1256,11 +1256,13 @@ size_t Archive::ReadHeader14() Raw.Read(NameSize); char FileName[NM]; - Raw.GetB((byte *)FileName,Min(NameSize,ASIZE(FileName))); - FileName[NameSize]=0; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; IntToExt(FileName,FileName,ASIZE(FileName)); CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); ConvertNameCase(FileHead.FileName); + ConvertFileHeader(&FileHead); if (Raw.Size()!=0) NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; @@ -1414,7 +1416,7 @@ int64 Archive::GetStartPos() } -bool Archive::ReadSubData(Array *UnpData,File *DestFile) +bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) { if (BrokenHeader) { @@ -1462,6 +1464,7 @@ bool Archive::ReadSubData(Array *UnpData,File *DestFile) SubDataIO.SetPackedSizeToRead(SubHead.PackSize); SubDataIO.EnableShowProgress(false); SubDataIO.SetFiles(this,DestFile); + SubDataIO.SetTestMode(TestMode); SubDataIO.UnpVolume=SubHead.SplitAfter; SubDataIO.SetSubHeader(&SubHead,NULL); Unpack.SetDestSize(SubHead.UnpSize); diff --git a/blake2s.hpp b/blake2s.hpp index 7dd7157..f88ef37 100644 --- a/blake2s.hpp +++ b/blake2s.hpp @@ -3,6 +3,7 @@ #define _RAR_BLAKE2_ #define BLAKE2_DIGEST_SIZE 32 +#define BLAKE2_THREADS_NUMBER 8 enum blake2s_constant { diff --git a/cmddata.cpp b/cmddata.cpp index 7b92686..455a07b 100644 --- a/cmddata.cpp +++ b/cmddata.cpp @@ -56,7 +56,6 @@ void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) // In Windows we may prefer to implement our own command line parser // to avoid replacing \" by " in standard parser. Such replacing corrupts // destination paths like "dest path\" in extraction commands. - // Also our own parser is Unicode compatible. const wchar *CmdLine=GetCommandLine(); wchar *Par; @@ -123,6 +122,7 @@ void CommandData::ParseArg(wchar *Arg) wchar CmdChar=toupperw(*Command); bool Add=wcschr(L"AFUM",CmdChar)!=NULL; bool Extract=CmdChar=='X' || CmdChar=='E'; + bool Repair=CmdChar=='R' && Command[1]==0; if (EndSeparator && !Add) wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); else @@ -140,8 +140,8 @@ void CommandData::ParseArg(wchar *Arg) ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true); } - else - if (Found && FileData.IsDir && Extract && *ExtrPath==0) + else // We use 'destpath\' when extracting and reparing. + if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0) { wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); AddEndSlash(ExtrPath,ASIZE(ExtrPath)); @@ -287,17 +287,23 @@ void CommandData::ProcessSwitch(const wchar *Switch) AppendArcNameToPath=APPENDARCNAME_DESTPATH; else if (Switch[2]=='1') - AppendArcNameToPath=APPENDARCNAME_OWNDIR; + AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR; + else + if (Switch[2]=='2') + AppendArcNameToPath=APPENDARCNAME_OWNDIR; break; #ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else - { - GenerateArcName=true; - wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); - } + if (toupperw(Switch[2])=='F') + wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask)); + else + { + GenerateArcName=true; + wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } break; #endif case 'I': @@ -372,11 +378,11 @@ void CommandData::ProcessSwitch(const wchar *Switch) default: if (Switch[1]=='+') { - InclFileAttr|=GetExclAttr(Switch+2); + InclFileAttr|=GetExclAttr(Switch+2,InclDir); InclAttrSet=true; } else - ExclFileAttr|=GetExclAttr(Switch+1); + ExclFileAttr|=GetExclAttr(Switch+1,ExclDir); break; } break; @@ -432,9 +438,9 @@ void CommandData::ProcessSwitch(const wchar *Switch) wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); break; } - if (wcsicomp(Switch+1,L"M")==0) + if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv. { - MoreInfo=true; + VerboseOutput=true; break; } if (wcsicomp(Switch+1,L"NUL")==0) @@ -461,6 +467,12 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'P': DisablePercentage=true; break; + case 'N': + DisableNames=true; + break; + case 'V': + VerboseOutput=true; + break; } break; } @@ -535,7 +547,6 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'D': Type=FILTER_DELTA; break; case 'A': Type=FILTER_AUDIO; break; case 'C': Type=FILTER_RGB; break; - case 'I': Type=FILTER_ITANIUM; break; case 'R': Type=FILTER_ARM; break; } if (*Str=='+' || *Str=='-') @@ -824,39 +835,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) SetTimeFilters(Switch+2,false,false); break; case 'S': - { - EXTTIME_MODE Mode=EXTTIME_HIGH3; - bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; - if (CommonMode) - Mode=(EXTTIME_MODE)(Switch[2]-'0'); - if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. - Mode=EXTTIME_HIGH3; - if (Switch[2]=='-') - Mode=EXTTIME_NONE; - if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) - xmtime=xctime=xatime=Mode; - else - { - if (Switch[3]>='0' && Switch[3]<='4') - Mode=(EXTTIME_MODE)(Switch[3]-'0'); - if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. - Mode=EXTTIME_HIGH3; - if (Switch[3]=='-') - Mode=EXTTIME_NONE; - switch(toupperw(Switch[2])) - { - case 'M': - xmtime=Mode; - break; - case 'C': - xctime=Mode; - break; - case 'A': - xatime=Mode; - break; - } - } - } + SetStoreTimeMode(Switch+2); break; case '-': Test=false; @@ -959,7 +938,10 @@ void CommandData::ProcessCommand() if (wcschr(L"AFUMD",*Command)==NULL) { if (GenerateArcName) - GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false); + { + const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask; + GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false); + } StringList ArcMasks; ArcMasks.AddString(ArcName); @@ -978,7 +960,6 @@ void CommandData::ProcessCommand() case 'X': case 'E': case 'T': - case 'I': { CmdExtract Extract(this); Extract.DoExtract(); @@ -1021,7 +1002,7 @@ bool CommandData::IsSwitch(int Ch) #ifndef SFX_MODULE -uint CommandData::GetExclAttr(const wchar *Str) +uint CommandData::GetExclAttr(const wchar *Str,bool &Dir) { if (IsDigit(*Str)) return wcstol(Str,NULL,0); @@ -1031,10 +1012,10 @@ uint CommandData::GetExclAttr(const wchar *Str) { switch(toupperw(*Str)) { -#ifdef _UNIX case 'D': - Attr|=S_IFDIR; + Dir=true; break; +#ifdef _UNIX case 'V': Attr|=S_IFCHR; break; @@ -1048,9 +1029,6 @@ uint CommandData::GetExclAttr(const wchar *Str) case 'S': Attr|=0x4; break; - case 'D': - Attr|=0x10; - break; case 'A': Attr|=0x20; break; diff --git a/cmddata.hpp b/cmddata.hpp index bf0efa6..719b400 100644 --- a/cmddata.hpp +++ b/cmddata.hpp @@ -11,12 +11,12 @@ enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1}; class CommandData:public RAROptions { private: - void ProcessSwitchesString(const wchar *Str); void ProcessSwitch(const wchar *Switch); void BadSwitch(const wchar *Switch); - uint GetExclAttr(const wchar *Str); + uint GetExclAttr(const wchar *Str,bool &Dir); #if !defined(SFX_MODULE) void SetTimeFilters(const wchar *Mod,bool Before,bool Age); + void SetStoreTimeMode(const wchar *S); #endif bool FileLists; @@ -33,6 +33,7 @@ class CommandData:public RAROptions void ParseEnvVar(); void ReadConfig(); void PreprocessArg(const wchar *Arg); + void ProcessSwitchesString(const wchar *Str); void OutTitle(); void OutHelp(RAR_EXIT ExitCode); bool IsSwitch(int Ch); diff --git a/cmdfilter.cpp b/cmdfilter.cpp index 39d5e66..d6517ce 100644 --- a/cmdfilter.cpp +++ b/cmdfilter.cpp @@ -285,7 +285,10 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy #ifndef SFX_MODULE if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime)) return 0; - if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0) + if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) + return 0; + if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || + FileHead.Dir && !InclDir)) return 0; if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; @@ -303,3 +306,47 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy } return 0; } + + +#if !defined(SFX_MODULE) +void CommandData::SetStoreTimeMode(const wchar *S) +{ + if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+') + { + // Apply -ts, -ts1, -ts-, -ts+ to all 3 times. + // Handle obsolete -ts[2,3,4] as ts+. + EXTTIME_MODE Mode=EXTTIME_MAX; + if (*S=='-') + Mode=EXTTIME_NONE; + if (*S=='1') + Mode=EXTTIME_1S; + xmtime=xctime=xatime=Mode; + S++; + } + + while (*S!=0) + { + EXTTIME_MODE Mode=EXTTIME_MAX; + if (S[1]=='-') + Mode=EXTTIME_NONE; + if (S[1]=='1') + Mode=EXTTIME_1S; + switch(toupperw(*S)) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + case 'P': + PreserveAtime=true; + break; + } + S++; + } +} +#endif diff --git a/consio.cpp b/consio.cpp index 196066e..fedd5c0 100644 --- a/consio.cpp +++ b/consio.cpp @@ -164,7 +164,7 @@ static void GetPasswordText(wchar *Str,uint MaxLength) SetConsoleMode(hConIn,ConInMode); SetConsoleMode(hConOut,ConOutMode); #else - char StrA[MAXPASSWORD]; + char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters. #if defined(_EMX) || defined (__VMS) fgets(StrA,ASIZE(StrA)-1,stdin); #elif defined(__sun) @@ -248,6 +248,12 @@ bool getwstr(wchar *str,size_t n) ErrHandler.Exit(RARX_USERBREAK); } StrA[ReadSize]=0; + + // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR, + // such as send passwords, we get OEM encoding by default, unless we + // use "chcp" in console. But we avoid OEM to ANSI conversion, + // because we also want to handle ANSI files redirection correctly, + // like "rar ... < ansifile.txt". CharToWide(&StrA[0],str,n); cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords. } @@ -305,7 +311,7 @@ int Ask(const wchar *AskStr) for (int I=0;I4 ? L"\n":L" "):L", "); + eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", "); int KeyPos=ItemKeyPos[I]; for (int J=0;J> 16)] ^ crc_tables[4][(byte)(StartCRC >> 24)] ^ crc_tables[3][(byte) NextData ] ^ - crc_tables[2][(byte)(NextData >>8 ) ] ^ + crc_tables[2][(byte)(NextData >> 8) ] ^ crc_tables[1][(byte)(NextData >> 16)] ^ crc_tables[0][(byte)(NextData >> 24)]; } diff --git a/dll.cpp b/dll.cpp index 5f4d00e..31818e4 100644 --- a/dll.cpp +++ b/dll.cpp @@ -95,23 +95,23 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) r->Flags=0; if (Data->Arc.Volume) - r->Flags|=0x01; + r->Flags|=ROADF_VOLUME; if (Data->Arc.MainComment) - r->Flags|=0x02; + r->Flags|=ROADF_COMMENT; if (Data->Arc.Locked) - r->Flags|=0x04; + r->Flags|=ROADF_LOCK; if (Data->Arc.Solid) - r->Flags|=0x08; + r->Flags|=ROADF_SOLID; if (Data->Arc.NewNumbering) - r->Flags|=0x10; + r->Flags|=ROADF_NEWNUMBERING; if (Data->Arc.Signed) - r->Flags|=0x20; + r->Flags|=ROADF_SIGNED; if (Data->Arc.Protected) - r->Flags|=0x40; + r->Flags|=ROADF_RECOVERY; if (Data->Arc.Encrypted) - r->Flags|=0x80; + r->Flags|=ROADF_ENCHEADERS; if (Data->Arc.FirstVolume) - r->Flags|=0x100; + r->Flags|=ROADF_FIRSTVOLUME; Array CmtDataW; if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) @@ -474,6 +474,7 @@ static int RarErrorToDll(RAR_EXIT ErrCode) switch(ErrCode) { case RARX_FATAL: + case RARX_READ: return ERAR_EREAD; case RARX_CRC: return ERAR_BAD_DATA; diff --git a/dll.rc b/dll.rc index 97d915b..e12a927 100644 --- a/dll.rc +++ b/dll.rc @@ -1,28 +1,28 @@ -#include -#include - -VS_VERSION_INFO VERSIONINFO -FILEVERSION 5, 71, 1, 3019 -PRODUCTVERSION 5, 71, 1, 3019 -FILEOS VOS__WINDOWS32 -FILETYPE VFT_APP -{ - BLOCK "StringFileInfo" - { - BLOCK "040904E4" - { - VALUE "CompanyName", "Alexander Roshal\0" - VALUE "ProductName", "RAR decompression library\0" - VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "5.71.1\0" - VALUE "ProductVersion", "5.71.1\0" - VALUE "LegalCopyright", "Copyright � Alexander Roshal 1993-2019\0" - VALUE "OriginalFilename", "Unrar.dll\0" - } - } - BLOCK "VarFileInfo" - { - VALUE "Translation", 0x0409, 0x04E4 - } -} - +#include +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 6, 0, 100, 3629 +PRODUCTVERSION 6, 0, 100, 3629 +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Alexander Roshal\0" + VALUE "ProductName", "RAR decompression library\0" + VALUE "FileDescription", "RAR decompression library\0" + VALUE "FileVersion", "6.0.0\0" + VALUE "ProductVersion", "6.0.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0" + VALUE "OriginalFilename", "Unrar.dll\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04E4 + } +} + diff --git a/errhnd.cpp b/errhnd.cpp index c867f1f..18e9197 100644 --- a/errhnd.cpp +++ b/errhnd.cpp @@ -15,6 +15,7 @@ void ErrorHandler::Clean() UserBreak=false; MainExit=false; DisableShutdown=false; + ReadErrIgnoreAll=false; } @@ -56,24 +57,34 @@ void ErrorHandler::ReadError(const wchar *FileName) ReadErrorMsg(FileName); #endif #if !defined(SILENT) || defined(RARDLL) - Exit(RARX_FATAL); + Exit(RARX_READ); #endif } -bool ErrorHandler::AskRepeatRead(const wchar *FileName) +void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit) { + SetErrorCode(RARX_READ); #if !defined(SILENT) && !defined(SFX_MODULE) if (!Silent) { + uiMsg(UIERROR_FILEREAD,UINULL,FileName); SysErrMsg(); - bool Repeat=uiAskRepeatRead(FileName); - if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog. - DisableShutdown=true; - return Repeat; + if (ReadErrIgnoreAll) + Ignore=true; + else + { + bool All=false; + uiAskRepeatRead(FileName,Ignore,All,Retry,Quit); + if (All) + ReadErrIgnoreAll=Ignore=true; + if (Quit) // Disable shutdown if user select Quit in read error prompt. + DisableShutdown=true; + } + return; } #endif - return false; + Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice. } @@ -158,6 +169,7 @@ 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); @@ -188,7 +200,7 @@ void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_FILEREAD,ArcName,FileName); SysErrMsg(); - SetErrorCode(RARX_FATAL); + SetErrorCode(RARX_READ); } @@ -328,7 +340,7 @@ void ErrorHandler::Throw(RAR_EXIT Code) bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) { -#if !defined(SFX_MODULE) && !defined(SILENT) +#ifndef SILENT #ifdef _WIN_ALL int ErrType=GetLastError(); if (ErrType!=0) @@ -361,7 +373,7 @@ void ErrorHandler::SysErrMsg() return; #ifdef _WIN_ALL wchar *CurMsg=Msg; - while (CurMsg!=NULL) + while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines. { while (*CurMsg=='\r' || *CurMsg=='\n') CurMsg++; diff --git a/errhnd.hpp b/errhnd.hpp index 3455dac..06f4f61 100644 --- a/errhnd.hpp +++ b/errhnd.hpp @@ -15,9 +15,11 @@ enum RAR_EXIT // RAR exit code. RARX_CREATE = 9, RARX_NOFILES = 10, RARX_BADPWD = 11, + RARX_READ = 12, RARX_USERBREAK = 255 }; + class ErrorHandler { private: @@ -26,6 +28,7 @@ class ErrorHandler bool EnableBreak; bool Silent; bool DisableShutdown; // Shutdown is not suitable after last error. + bool ReadErrIgnoreAll; public: ErrorHandler(); void Clean(); @@ -33,7 +36,7 @@ class ErrorHandler void OpenError(const wchar *FileName); void CloseError(const wchar *FileName); void ReadError(const wchar *FileName); - bool AskRepeatRead(const wchar *FileName); + void AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit); void WriteError(const wchar *ArcName,const wchar *FileName); void WriteErrorFAT(const wchar *FileName); bool AskRepeatWrite(const wchar *FileName,bool DiskFull); diff --git a/extract.cpp b/extract.cpp index 41fd031..abcd3c3 100644 --- a/extract.cpp +++ b/extract.cpp @@ -40,6 +40,9 @@ void CmdExtract::DoExtract() { 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) { EXTRACT_ARC_CODE Code=ExtractArchive(); @@ -93,7 +96,6 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc) PrevProcessed=false; AllMatchesExact=true; - ReconstructDone=false; AnySolidDataUnpackedWell=false; StartTime.SetCurrentTime(); @@ -139,7 +141,7 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE - if (Arc.Volume && !Arc.FirstVolume) + if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName) { wchar FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); @@ -157,6 +159,16 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() 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)) + { + UseExactVolName=true; + return EXTRACT_ARC_REPEAT; + } +#endif + // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. @@ -261,15 +273,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (HeaderType==HEAD_ENDARC) if (Arc.EndArcHead.NextVolume) { -#ifndef NOVOLUME +#ifdef NOVOLUME + return false; +#else if (!MergeArchive(Arc,&DataIO,false,Command)) { ErrHandler.SetErrorCode(RARX_WARNING); return false; } -#endif Arc.Seek(Arc.CurBlockPos,SEEK_SET); return true; +#endif } else return false; @@ -315,11 +329,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) Arc.ConvertAttributes(); #if !defined(SFX_MODULE) && !defined(RARDLL) - if (Arc.FileHead.SplitBefore && FirstFile) + if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName) { wchar CurVolName[NM]; wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); - VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering); + GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName)); if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) { @@ -477,13 +491,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { // This message is used by Android GUI to reset cached passwords. // Update appropriate code if changed. - uiMsg(UIERROR_BADPSW,ArcFileName); + 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,ArcFileName); + uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); Cmd->Password.Clean(); // Avoid new requests for unrar.dll to prevent the infinite loop @@ -572,7 +586,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) TotalFileCount++; } FileCount++; - if (Command!='I') + if (Command!='I' && !Cmd->DisableNames) if (SkipSolid) mprintf(St(MExtrSkipFile),ArcFileName); else @@ -591,8 +605,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) mprintf(St(MExtrFile),DestFileName); break; } - if (!Cmd->DisablePercentage) + if (!Cmd->DisablePercentage && !Cmd->DisableNames) mprintf(L" "); + if (Cmd->DisableNames) + uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode. DataIO.CurUnpRead=0; DataIO.CurUnpWrite=0; @@ -622,7 +638,6 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) CurFile.Prealloc(Arc.FileHead.UnpSize); Preallocated=Arc.FileHead.UnpSize; } - CurFile.SetAllowDelete(!Cmd->KeepBroken); bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; @@ -709,7 +724,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { if (ValidCRC) { - if (Command!='P' && Command!='I') + if (Command!='P' && Command!='I' && !Cmd->DisableNames) mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); } @@ -734,38 +749,58 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) else mprintf(L"\b\b\b\b\b "); + // If we successfully unpacked a hard link, we wish to set its file + // attributes. Hard link shares file metadata with link target, + // so we do not need to set link time or owner. But when we overwrite + // an existing link, we can call PrepareToDelete(), which affects + // link target attributes as well. So we set link attributes to restore + // both target and link attributes if PrepareToDelete() changed them. + bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess; + if (!TestMode && (Command=='X' || Command=='E') && - (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && + (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && (!BrokenFile || Cmd->KeepBroken)) { - // We could preallocate more space that really written to broken file - // or file with crafted header. - if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) - CurFile.Truncate(); + // Below we use DestFileName instead of CurFile.FileName, + // so we can set file attributes also for hard links, which do not + // have the open CurFile. These strings are the same for other items. -#if defined(_WIN_ALL) || defined(_EMX) - if (Cmd->ClearArc) - Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; -#endif + if (!SetAttrOnly) + { + // We could preallocate more space that really written to broken file + // or file with crafted header. + if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) + CurFile.Truncate(); + + + CurFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + CurFile.Close(); + SetFileHeaderExtra(Cmd,Arc,DestFileName); - CurFile.SetOpenFileTime( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, - Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); - CurFile.Close(); + CurFile.SetCloseFileTime( + 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) - SetFileCompression(CurFile.FileName,true); + SetFileCompression(DestFileName,true); + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; #endif - SetFileHeaderExtra(Cmd,Arc,CurFile.FileName); - - CurFile.SetCloseFileTime( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); - if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr)) - uiMsg(UIERROR_FILEATTR,Arc.FileName,CurFile.FileName); + if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr)) + { + uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName); + // Android cannot set file attributes and while UIERROR_FILEATTR + // above is handled by Android RAR silently, this call would cause + // "Operation not permitted" message for every unpacked file. + ErrHandler.SysErrMsg(); + } PrevProcessed=true; } @@ -858,11 +893,21 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De #ifndef SFX_MODULE if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE) { - if (Cmd->AppendArcNameToPath==APPENDARCNAME_DESTPATH) - wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); - else - wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); // To archive own dir. - SetExt(DestName,NULL,DestSize); + switch(Cmd->AppendArcNameToPath) + { + case APPENDARCNAME_DESTPATH: // To subdir of destination path. + wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNDIR: // To archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + RemoveNameFromPath(DestName); + break; + } AddEndSlash(DestName,DestSize); } #endif @@ -1032,8 +1077,11 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) { if (Cmd->Test) { - mprintf(St(MExtrTestFile),ArcFileName); - mprintf(L" %s",St(MOk)); + if (!Cmd->DisableNames) + { + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(L" %s",St(MOk)); + } return; } @@ -1070,8 +1118,11 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) } if (MDCode==MKDIR_SUCCESS) { - mprintf(St(MCreatDir),DestFileName); - mprintf(L" %s",St(MOk)); + if (!Cmd->DisableNames) + { + mprintf(St(MCreatDir),DestFileName); + mprintf(L" %s",St(MOk)); + } PrevProcessed=true; } else @@ -1125,6 +1176,9 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) if (!UserReject) { ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName))) + uiMsg(UIERROR_DIRNAMEEXISTS); + #ifdef RARDLL Cmd->DllError=ERAR_ECREATE; #endif @@ -1180,3 +1234,104 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) } return !WrongVer; } + + +#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) +{ + 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. + + 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)); + + bool Matched=false; + while (!Matched) + { + Archive Arc(Cmd); + if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume) + break; + + bool OpenNext=false; + while (Arc.ReadHeader()>0) + { + Wait(); + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { + OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume. + break; + } + if (HeaderType==HEAD_FILE) + { + if (!Arc.FileHead.SplitBefore) + { + if (!Arc.FileHead.Solid) // Can start extraction from here. + wcsncpyz(StartName,NextName,ASIZE(StartName)); + + if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) + { + Matched=true; // First matched file found, must stop further scan. + break; + } + } + if (Arc.FileHead.SplitAfter) + { + OpenNext=true; // Allow open next volume. + break; + } + } + Arc.SeekToNext(); + } + Arc.Close(); + + if (!OpenNext) + break; + + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + } + bool NewStartFound=wcscmp(VolName,StartName)!=0; + if (NewStartFound) // Found a new volume to start extraction. + wcsncpyz(ArcName,StartName,ASIZE(ArcName)); + + return NewStartFound; +} +#endif + + +#ifndef SFX_MODULE +// Return the first volume name if all volumes preceding the specified +// are available. Otherwise return the specified volume name. +void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize) +{ + wchar FirstVolName[NM]; + VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering); + wchar NextName[NM]; + wcsncpyz(NextName,FirstVolName,ASIZE(NextName)); + wchar ResultName[NM]; + wcsncpyz(ResultName,SrcName,ASIZE(ResultName)); + while (true) + { + if (wcscmp(SrcName,NextName)==0) + { + wcsncpyz(ResultName,FirstVolName,DestSize); + break; + } + if (!FileExist(NextName)) + break; + NextVolumeName(NextName,ASIZE(NextName),!NewNumbering); + } + wcsncpyz(DestName,ResultName,DestSize); +} + +#endif \ No newline at end of file diff --git a/extract.hpp b/extract.hpp index 325928d..159759b 100644 --- a/extract.hpp +++ b/extract.hpp @@ -20,8 +20,12 @@ class CmdExtract void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); bool ExtrCreateFile(Archive &Arc,File &CurFile); bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); +#ifndef SFX_MODULE + bool DetectStartVolume(const wchar *VolName,bool NewNumbering); + void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); +#endif - RarTime StartTime; // time when extraction started + RarTime StartTime; // Time when extraction started. CommandData *Cmd; @@ -34,6 +38,7 @@ class CmdExtract bool FirstFile; bool AllMatchesExact; bool ReconstructDone; + bool UseExactVolName; // If any non-zero solid file was successfully unpacked before current. // If true and if current encrypted file is broken, obviously diff --git a/file.cpp b/file.cpp index fb9594d..5a8099e 100644 --- a/file.cpp +++ b/file.cpp @@ -8,15 +8,17 @@ File::File() LastWrite=false; HandleType=FILE_HANDLENORMAL; SkipClose=false; - IgnoreReadErrors=false; ErrorType=FILE_SUCCESS; OpenShared=false; AllowDelete=true; AllowExceptions=true; + PreserveAtime=false; #ifdef _WIN_ALL NoSequentialRead=false; CreateMode=FMF_UNDEFINED; #endif + ReadErrorMode=FREM_ASK; + TruncatedAfterReadError=false; } @@ -36,6 +38,7 @@ void File::operator = (File &SrcFile) NewFile=SrcFile.NewFile; LastWrite=SrcFile.LastWrite; HandleType=SrcFile.HandleType; + TruncatedAfterReadError=SrcFile.TruncatedAfterReadError; wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); SrcFile.SkipClose=true; } @@ -56,6 +59,9 @@ bool File::Open(const wchar *Name,uint Mode) if (OpenShared) ShareMode|=FILE_SHARE_WRITE; uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; + FindData FD; + if (PreserveAtime) + Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); DWORD LastError; @@ -86,6 +92,11 @@ bool File::Open(const wchar *Name,uint Mode) } if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) ErrorType=FILE_NOTFOUND; + if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE) + { + FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification. + SetFileTime(hNewFile,NULL,&ft,NULL); + } #else int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); @@ -94,6 +105,11 @@ bool File::Open(const wchar *Name,uint Mode) #if defined(_AIX) && defined(_LARGE_FILE_API) flags|=O_LARGEFILE; #endif +#endif + // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+. +#if defined(O_NOATIME) + if (PreserveAtime) + flags|=O_NOATIME; #endif char NameA[NM]; WideToChar(Name,NameA,ASIZE(NameA)); @@ -104,12 +120,12 @@ bool File::Open(const wchar *Name,uint Mode) #ifdef _OSF_SOURCE extern "C" int flock(int, int); #endif - if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) { close(handle); return false; } + #endif if (handle==-1) hNewFile=FILE_BAD_HANDLE; @@ -132,6 +148,7 @@ bool File::Open(const wchar *Name,uint Mode) { hFile=hNewFile; wcsncpyz(FileName,Name,ASIZE(FileName)); + TruncatedAfterReadError=false; } return Success; } @@ -230,7 +247,7 @@ bool File::Close() { #ifdef _WIN_ALL // We use the standard system handle for stdout in Windows - // and it must not be closed here. + // and it must not be closed here. if (HandleType==FILE_HANDLENORMAL) Success=CloseHandle(hFile)==TRUE; #else @@ -355,9 +372,12 @@ bool File::Write(const void *Data,size_t Size) int File::Read(void *Data,size_t Size) { + if (TruncatedAfterReadError) + return 0; + int64 FilePos=0; // Initialized only to suppress some compilers warning. - if (IgnoreReadErrors) + if (ReadErrorMode==FREM_IGNORE) FilePos=Tell(); int ReadSize; while (true) @@ -367,7 +387,7 @@ int File::Read(void *Data,size_t Size) { ErrorType=FILE_READERROR; if (AllowExceptions) - if (IgnoreReadErrors) + if (ReadErrorMode==FREM_IGNORE) { ReadSize=0; for (size_t I=0;ITell(); #ifndef SILENT int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; #endif @@ -415,6 +415,8 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, if (Size!=INT64NDF) Size-=ReadSize; } + SrcFile->Seek(SavePos,SEEK_SET); + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) uiMsg(UIEVENT_FILESUMEND); diff --git a/find.cpp b/find.cpp index 98cf771..b22f82d 100644 --- a/find.cpp +++ b/find.cpp @@ -63,12 +63,12 @@ bool FindFile::Next(FindData *fd,bool GetSymLink) } while (1) { + wchar Name[NM]; struct dirent *ent=readdir(dirp); if (ent==NULL) return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; - wchar Name[NM]; if (!CharToWide(ent->d_name,Name,ASIZE(Name))) uiMsg(UIERROR_INVALIDNAME,UINULL,Name); diff --git a/hash.cpp b/hash.cpp index 42791f4..a4559e0 100644 --- a/hash.cpp +++ b/hash.cpp @@ -53,7 +53,7 @@ DataHash::DataHash() DataHash::~DataHash() { #ifdef RAR_SMP - DestroyThreadPool(ThPool); + delete ThPool; #endif cleandata(&CurCRC32, sizeof(CurCRC32)); if (blake2ctx!=NULL) @@ -94,7 +94,7 @@ void DataHash::Update(const void *Data,size_t DataSize) { #ifdef RAR_SMP if (MaxThreads>1 && ThPool==NULL) - ThPool=CreateThreadPool(); + ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER); blake2ctx->ThPool=ThPool; blake2ctx->MaxThreads=MaxThreads; #endif diff --git a/list.cpp b/list.cpp index d558ffa..476fd3c 100644 --- a/list.cpp +++ b/list.cpp @@ -28,7 +28,7 @@ void ListArchive(CommandData *Cmd) if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; - while (1) + while (true) { int64 TotalPackSize=0,TotalUnpSize=0; uint FileCount=0; @@ -69,7 +69,7 @@ void ListArchive(CommandData *Cmd) wchar VolNumText[50]; *VolNumText=0; - while(Arc.ReadHeader()>0) + while (Arc.ReadHeader()>0) { Wait(); // Allow quit listing with Ctrl+C. HEADER_TYPE HeaderType=Arc.GetHeaderType(); @@ -345,7 +345,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS); mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo), - Format==RARFMT15 ? L"3.0":L"5.0", + Format==RARFMT15 ? L"1.5":L"5.0", hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method, hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, hd.WinSize>=0x100000 ? L"M":L"K"); diff --git a/loclang.hpp b/loclang.hpp index 6d264be..99108c9 100644 --- a/loclang.hpp +++ b/loclang.hpp @@ -4,6 +4,7 @@ #define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit" #define MContinueQuit L"_Continue_Quit" #define MRetryAbort L"_Retry_Abort" +#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit" #define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" #define MRegTo L"\nRegistered to %s\n" #define MShare L"\nTrial version Type 'rar -?' for help\n" @@ -54,7 +55,7 @@ #define MCHelpSwm L"\n - Stop switches scanning" #define MCHelpSwAT L"\n @[+] Disable [enable] file lists" #define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction" -#define MCHelpSwAD L"\n ad Append archive name to destination path" +#define MCHelpSwAD L"\n ad[1,2] Alternate destination path" #define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" #define MCHelpSwAI L"\n ai Ignore file attributes" #define MCHelpSwAO L"\n ao Add files with Archive attribute set" @@ -79,7 +80,7 @@ #define MCHelpSwF L"\n f Freshen files" #define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" #define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" -#define MCHelpSwIDP L"\n id[c,d,p,q] Disable messages" +#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages" #define MCHelpSwIEML L"\n ieml[addr] Send archive by email" #define MCHelpSwIERR L"\n ierr Send all messages to stderr" #define MCHelpSwILOG L"\n ilog[name] Log errors to file" @@ -131,7 +132,7 @@ #define MCHelpSwTO L"\n to[mcao] Process files older than time" #define MCHelpSwTA L"\n ta[mcao] Process files modified after YYYYMMDDHHMMSS date" #define MCHelpSwTB L"\n tb[mcao] Process files modified before YYYYMMDDHHMMSS date" -#define MCHelpSwTS L"\n ts[m,c,a] Save or restore file time (modification, creation, access)" +#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)" #define MCHelpSwU L"\n u Update files" #define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes" #define MCHelpSwVUnr L"\n v List all volumes" @@ -203,7 +204,6 @@ #define MErrOpenFile L"file" #define MAddNoFiles L"\nWARNING: No files" #define MMdfEncrSol L"\n%s: encrypted" -#define MCannotMdfEncrSol L"\nCannot modify solid archive containing encrypted files" #define MAddAnalyze L"\nAnalyzing archived files: " #define MRepacking L"\nRepacking archived files: " #define MCRCFailed L"\n%-20s - checksum error" @@ -342,7 +342,7 @@ #define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files" #define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s" #define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives" -#define MCannotEncName L"\nCannot encrypt archive already containing encrypted files" +#define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive" #define MCannotEmail L"\nCannot email the file %s" #define MCopyrightS L"\nRAR SFX archive" #define MSHelpCmd L"\n\n" @@ -352,7 +352,7 @@ #define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d" #define MVolumeNumber L"volume %d" #define MCannotDelete L"\nCannot delete %s" -#define MRecycleFailed L"\nCannot move some files and folders to Recycle Bin" +#define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin" #define MCalcCRC L"\nCalculating the checksum" #define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB." #define MCalcCRCAllVol L"\nCalculating checksums of all volumes." @@ -360,6 +360,7 @@ #define MNewerRAR L"\nYou may need a newer version of RAR." #define MUnkEncMethod L"\nUnknown encryption method in %s" #define MWrongPassword L"\nThe specified password is incorrect." +#define MWrongFilePassword L"\nIncorrect password for %s" #define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x" #define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated" #define MRRDamaged L"\nRecovery record is corrupt." @@ -379,3 +380,8 @@ #define MNeedAdmin L"\nYou may need to run RAR as administrator" #define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB." #define MUseSmalllerDict L"\nPlease use a smaller compression dictionary." +#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file." +#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort." +#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n" +#define MErrReadCount L"\n%u files are archived incompletely because of read errors." +#define MDirNameExists L"\nDirectory with such name already exists" diff --git a/makefile b/makefile index f70755f..214f87e 100644 --- a/makefile +++ b/makefile @@ -138,7 +138,9 @@ install: install-unrar uninstall: uninstall-unrar clean: - @rm -f *.o *.bak *~ + @rm -f *.bak *~ + @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) + @rm -f unrar libunrar.* unrar: clean $(OBJECTS) $(UNRAR_OBJ) @rm -f unrar @@ -154,8 +156,7 @@ sfx: clean $(OBJECTS) lib: WHAT=RARDLL lib: CXXFLAGS+=$(LIBFLAGS) lib: clean $(OBJECTS) $(LIB_OBJ) - @rm -f libunrar.so - @rm -f libunrar.a + @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) diff --git a/options.cpp b/options.cpp index b53edc6..40323be 100644 --- a/options.cpp +++ b/options.cpp @@ -22,7 +22,7 @@ void RAROptions::Init() Method=3; MsgStream=MSG_STDOUT; ConvertNames=NAMES_ORIGINALCASE; - xmtime=EXTTIME_HIGH3; + xmtime=EXTTIME_MAX; FileSizeLess=INT64NDF; FileSizeMore=INT64NDF; HashType=HASH_CRC32; diff --git a/options.hpp b/options.hpp index 7c33a03..993b219 100644 --- a/options.hpp +++ b/options.hpp @@ -21,7 +21,7 @@ enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4, enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST}; enum EXTTIME_MODE { - EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_HIGH1,EXTTIME_HIGH2,EXTTIME_HIGH3 + EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX }; enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE}; @@ -61,7 +61,8 @@ enum SAVECOPY_MODE { enum APPENDARCNAME_MODE { - APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNDIR + APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR, + APPENDARCNAME_OWNDIR }; enum POWER_MODE { @@ -92,6 +93,12 @@ class RAROptions uint ExclFileAttr; uint InclFileAttr; + + // We handle -ed and -e+d with special flags instead of attribute mask, + // so it works with both Windows and Unix archives. + bool ExclDir; + bool InclDir; + bool InclAttrSet; size_t WinSize; wchar TempPath[NM]; @@ -126,6 +133,7 @@ class RAROptions bool DisablePercentage; bool DisableCopyright; bool DisableDone; + bool DisableNames; bool PrintVersion; int Solid; int SolidCount; @@ -140,7 +148,7 @@ class RAROptions Array NextVolSizes; uint CurVolNum; bool AllYes; - bool MoreInfo; // -im, show more information, used only in "WinRAR t" now. + bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. bool DisableSortSolid; int ArcTime; int ConvertNames; @@ -162,6 +170,7 @@ class RAROptions #ifndef SFX_MODULE bool GenerateArcName; wchar GenerateMask[MAX_GENERATE_MASK]; + wchar DefGenerateMask[MAX_GENERATE_MASK]; #endif bool SyncFiles; bool ProcessEA; @@ -185,6 +194,7 @@ class RAROptions EXTTIME_MODE xmtime; // Extended time modes (time precision to store). EXTTIME_MODE xctime; EXTTIME_MODE xatime; + bool PreserveAtime; wchar CompressStdin[NM]; uint Threads; // We use it to init hash even if RAR_SMP is not defined. diff --git a/os.hpp b/os.hpp index 3913520..b69f348 100644 --- a/os.hpp +++ b/os.hpp @@ -133,7 +133,7 @@ #ifdef _UNIX -#define NM 2048 +#define NM 2048 #include #include diff --git a/pathfn.cpp b/pathfn.cpp index cb1d7ed..41594bf 100644 --- a/pathfn.cpp +++ b/pathfn.cpp @@ -170,14 +170,17 @@ int GetPathDisk(const wchar *Path) void AddEndSlash(wchar *Path,size_t MaxLength) { size_t Length=wcslen(Path); - if (Length>0 && Path[Length-1]!=CPATHDIVIDER) - wcsncatz(Path,SPATHDIVIDER,MaxLength); + if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1Tell(); UnsyncSeekPos=false; - SaveFilePos SavePos(*Arc); + int64 SavePos=SeekPos; Arc->Seek(BlockPos,SEEK_SET); // If BlockPos points to original main header, we'll have the infinite @@ -83,10 +83,14 @@ void QuickOpen::Load(uint64 BlockPos) if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) + { + Arc->Seek(SavePos,SEEK_SET); return; + } QOHeaderPos=Arc->CurBlockPos; RawDataStart=Arc->Tell(); RawDataSize=Arc->SubHead.UnpSize; + Arc->Seek(SavePos,SEEK_SET); Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader. } @@ -201,22 +205,28 @@ bool QuickOpen::Tell(int64 *Pos) uint QuickOpen::ReadBuffer() { - SaveFilePos SavePos(*Arc); + int64 SavePos=Arc->Tell(); Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET); size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize); if (Arc->SubHead.Encrypted) SizeToRead &= ~CRYPT_BLOCK_MASK; - if (SizeToRead==0) - return 0; - int ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); - if (ReadSize<=0) - return 0; + int ReadSize=0; + if (SizeToRead!=0) + { + ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); + if (ReadSize<=0) + ReadSize=0; + else + { #ifndef RAR_NOCRYPT - if (Arc->SubHead.Encrypted) - Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); + if (Arc->SubHead.Encrypted) + Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); #endif - RawDataPos+=ReadSize; - ReadBufSize+=ReadSize; + RawDataPos+=ReadSize; + ReadBufSize+=ReadSize; + } + } + Arc->Seek(SavePos,SEEK_SET); return ReadSize; } diff --git a/rar.hpp b/rar.hpp index 65e0496..3f7414c 100644 --- a/rar.hpp +++ b/rar.hpp @@ -39,7 +39,6 @@ #include "filestr.hpp" #include "find.hpp" #include "scantree.hpp" -#include "savepos.hpp" #include "getbits.hpp" #include "rdwrfn.hpp" #ifdef USE_QOPEN @@ -77,6 +76,9 @@ #include "rs.hpp" #include "rs16.hpp" + + + #include "recvol.hpp" #include "volume.hpp" #include "smallfn.hpp" diff --git a/rartypes.hpp b/rartypes.hpp index 1ec97c9..3d3111b 100644 --- a/rartypes.hpp +++ b/rartypes.hpp @@ -21,15 +21,12 @@ typedef wchar_t wchar; // Unicode character // Maximum int64 value. #define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff)) -// Special int64 value, large enough to never be found in real life. +// Special int64 value, large enough to never be found in real life +// and small enough to fit to both signed and unsigned 64-bit ints. // We use it in situations, when we need to indicate that parameter // is not defined and probably should be calculated inside of function. // Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it -// compatible with 32 bit int64. +// compatible with 32 bit int64 if 64 bit type is not supported. #define INT64NDF INT32TO64(0x7fffffff,0x7fffffff) -// Maximum uint64 value. -#define MAX_UINT64 INT32TO64(0xffffffff,0xffffffff) -#define UINT64NDF MAX_UINT64 - #endif diff --git a/rdwrfn.hpp b/rdwrfn.hpp index 070010e..fc38fd3 100644 --- a/rdwrfn.hpp +++ b/rdwrfn.hpp @@ -3,6 +3,7 @@ class CmdAdd; class Unpack; +class ArcFileSearch; #if 0 // We use external i/o calls for Benchmark command. diff --git a/recvol.cpp b/recvol.cpp index 166eef4..adf5840 100644 --- a/recvol.cpp +++ b/recvol.cpp @@ -31,12 +31,12 @@ bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) // handling exceptions. So it can close and delete files on Cancel. if (Fmt==RARFMT15) { - RecVolumes3 RecVol(false); + RecVolumes3 RecVol(Cmd,false); return RecVol.Restore(Cmd,Name,Silent); } else { - RecVolumes5 RecVol(false); + RecVolumes5 RecVol(Cmd,false); return RecVol.Restore(Cmd,Name,Silent); } } @@ -100,12 +100,12 @@ void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) RevFile.Close(); if (Rev5) { - RecVolumes5 RecVol(true); + RecVolumes5 RecVol(Cmd,true); RecVol.Test(Cmd,Name); } else { - RecVolumes3 RecVol(true); + RecVolumes3 RecVol(Cmd,true); RecVol.Test(Cmd,Name); } } diff --git a/recvol.hpp b/recvol.hpp index 7f2f1ad..06510a2 100644 --- a/recvol.hpp +++ b/recvol.hpp @@ -14,7 +14,7 @@ class RecVolumes3 ThreadPool *RSThreadPool; #endif public: - RecVolumes3(bool TestOnly); + RecVolumes3(RAROptions *Cmd,bool TestOnly); ~RecVolumes3(); void Make(RAROptions *Cmd,wchar *ArcName); bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); @@ -71,11 +71,12 @@ class RecVolumes5 #ifdef RAR_SMP ThreadPool *RecThreadPool; #endif - RecRSThreadData ThreadData[MaxPoolThreads]; // Store thread parameters. + uint MaxUserThreads; // Maximum number of threads defined by user. + RecRSThreadData *ThreadData; // Array to store thread parameters. public: // 'public' only because called from thread functions. void ProcessAreaRS(RecRSThreadData *td); public: - RecVolumes5(bool TestOnly); + RecVolumes5(RAROptions *Cmd,bool TestOnly); ~RecVolumes5(); bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); void Test(RAROptions *Cmd,const wchar *Name); diff --git a/recvol3.cpp b/recvol3.cpp index 51776d4..9fb846a 100644 --- a/recvol3.cpp +++ b/recvol3.cpp @@ -36,7 +36,7 @@ THREAD_PROC(RSDecodeThread) } #endif -RecVolumes3::RecVolumes3(bool TestOnly) +RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly) { memset(SrcFile,0,sizeof(SrcFile)); if (TestOnly) @@ -50,7 +50,7 @@ RecVolumes3::RecVolumes3(bool TestOnly) Buf.Alloc(TotalBufferSize); memset(SrcFile,0,sizeof(SrcFile)); #ifdef RAR_SMP - RSThreadPool=CreateThreadPool(); + RSThreadPool=new ThreadPool(Cmd->Threads); #endif } } @@ -61,7 +61,7 @@ RecVolumes3::~RecVolumes3() for (size_t I=0;IThreads; - RSEncode rse[MaxPoolThreads]; #else uint ThreadNumber=1; - RSEncode rse[1]; #endif + RSEncode *rse=new RSEncode[ThreadNumber]; for (uint I=0;IWrite(&Buf[I*RecBufferSize],MaxRead); } + delete[] rse; + for (int I=0;IThreads; +#else + MaxUserThreads=1; +#endif + + ThreadData=new RecRSThreadData[MaxUserThreads]; + for (uint I=0;IThreads; -#else - uint ThreadNumber=1; -#endif + uint ThreadNumber=MaxUserThreads; const uint MinThreadBlock=0x1000; ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock); @@ -238,7 +242,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) uiMsg(UIMSG_STRING,Item->Name); uint RevCRC; - CalcFileSum(Item->f,&RevCRC,NULL,Cmd->Threads,INT64NDF,CALCFSUM_CURPOS); + CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS); Item->Valid=RevCRC==Item->CRC; if (!Item->Valid) { diff --git a/resource.cpp b/resource.cpp index 2eec79c..dadd072 100644 --- a/resource.cpp +++ b/resource.cpp @@ -2,8 +2,10 @@ + + #ifndef RARDLL -const wchar *St(MSGID StringId) +const wchar* St(MSGID StringId) { return StringId; } diff --git a/rijndael.cpp b/rijndael.cpp index ba29c91..dd19750 100644 --- a/rijndael.cpp +++ b/rijndael.cpp @@ -75,8 +75,14 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe // Check SSE here instead of constructor, so if object is a part of some // structure memset'ed before use, this variable is not lost. int CPUInfo[4]; - __cpuid(CPUInfo, 1); - AES_NI=(CPUInfo[2] & 0x2000000)!=0; + __cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function. + if ((CPUInfo[0] & 0x7fffffff)>=1) + { + __cpuid(CPUInfo, 1); + AES_NI=(CPUInfo[2] & 0x2000000)!=0; + } + else + AES_NI=0; #endif // Other developers asked us to initialize it to suppress "may be used diff --git a/savepos.hpp b/savepos.hpp index 10a0664..1f8353f 100644 --- a/savepos.hpp +++ b/savepos.hpp @@ -14,13 +14,26 @@ class SaveFilePos } ~SaveFilePos() { - // If file is already closed by current exception processing, - // we would get uneeded error messages and an exception inside of - // exception and terminate if we try to seek without checking - // if file is still opened. We should not also restore the position - // if external code closed the file on purpose. + // Unless the file is already closed either by current exception + // processing or intentionally by external code. if (SaveFile->IsOpened()) - SaveFile->Seek(SavePos,SEEK_SET); + { + try + { + SaveFile->Seek(SavePos,SEEK_SET); + } + catch(RAR_EXIT) + { + // Seek() can throw an exception and it terminates process + // if we are already processing another exception. Also in C++ 11 + // an exception in destructor always terminates process unless + // we mark destructor with noexcept(false). So we do not want to + // throw here. To prevent data loss we do not want to continue + // execution after seek error, so we close the file. + // Any next access to this file will return an error. + SaveFile->Close(); + } + } } }; diff --git a/secpassword.cpp b/secpassword.cpp index 780d032..4865b3f 100644 --- a/secpassword.cpp +++ b/secpassword.cpp @@ -46,17 +46,12 @@ class CryptLoader } } - static CryptLoader& GetInstance() - { - static CryptLoader cryptLoader; - return cryptLoader; - } - CRYPTPROTECTMEMORY pCryptProtectMemory; CRYPTUNPROTECTMEMORY pCryptUnprotectMemory; }; // We need to call FreeLibrary when RAR is exiting. +CryptLoader GlobalCryptLoader; #endif SecPassword::SecPassword() @@ -174,15 +169,15 @@ void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) // increases data size not allowing in place conversion. #if defined(_WIN_ALL) // Try to utilize the secure Crypt[Un]ProtectMemory if possible. - if (CryptLoader::GetInstance().pCryptProtectMemory==NULL) - CryptLoader::GetInstance().Load(); + if (GlobalCryptLoader.pCryptProtectMemory==NULL) + GlobalCryptLoader.Load(); size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE; DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS; if (Encode) { - if (CryptLoader::GetInstance().pCryptProtectMemory!=NULL) + if (GlobalCryptLoader.pCryptProtectMemory!=NULL) { - if (!CryptLoader::GetInstance().pCryptProtectMemory(Data,DWORD(Aligned),Flags)) + if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags)) { ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed"); ErrHandler.SysErrMsg(); @@ -193,9 +188,9 @@ void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) } else { - if (CryptLoader::GetInstance().pCryptUnprotectMemory!=NULL) + if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL) { - if (!CryptLoader::GetInstance().pCryptUnprotectMemory(Data,DWORD(Aligned),Flags)) + if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags)) { ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed"); ErrHandler.SysErrMsg(); diff --git a/strfn.cpp b/strfn.cpp index 6545f0a..8904b90 100644 --- a/strfn.cpp +++ b/strfn.cpp @@ -241,8 +241,8 @@ uint GetDigits(uint Number) bool LowAscii(const char *Str) { - for (int I=0;Str[I]!=0;I++) - if ((byte)Str[I]<32 || (byte)Str[I]>127) + for (size_t I=0;Str[I]!=0;I++) + if (/*(byte)Str[I]<32 || */(byte)Str[I]>127) return false; return true; } @@ -250,11 +250,11 @@ bool LowAscii(const char *Str) bool LowAscii(const wchar *Str) { - for (int I=0;Str[I]!=0;I++) + for (size_t I=0;Str[I]!=0;I++) { // We convert wchar_t to uint just in case if some compiler // uses signed wchar_t. - if ((uint)Str[I]<32 || (uint)Str[I]>127) + if (/*(uint)Str[I]<32 || */(uint)Str[I]>127) return false; } return true; diff --git a/system.cpp b/system.cpp index c13c81e..4ae2b89 100644 --- a/system.cpp +++ b/system.cpp @@ -187,18 +187,29 @@ SSE_VERSION _SSE_Version=GetSSEVersion(); SSE_VERSION GetSSEVersion() { int CPUInfo[4]; - __cpuid(CPUInfo, 7); - if ((CPUInfo[1] & 0x20)!=0) - return SSE_AVX2; - __cpuid(CPUInfo, 1); - if ((CPUInfo[2] & 0x80000)!=0) - return SSE_SSE41; - if ((CPUInfo[2] & 0x200)!=0) - return SSE_SSSE3; - if ((CPUInfo[3] & 0x4000000)!=0) - return SSE_SSE2; - if ((CPUInfo[3] & 0x2000000)!=0) - return SSE_SSE; + __cpuid(CPUInfo, 0x80000000); + + // Maximum supported cpuid function. For example, Pentium M 755 returns 4 here. + uint MaxSupported=CPUInfo[0] & 0x7fffffff; + + if (MaxSupported>=7) + { + __cpuid(CPUInfo, 7); + if ((CPUInfo[1] & 0x20)!=0) + return SSE_AVX2; + } + if (MaxSupported>=1) + { + __cpuid(CPUInfo, 1); + if ((CPUInfo[2] & 0x80000)!=0) + return SSE_SSE41; + if ((CPUInfo[2] & 0x200)!=0) + return SSE_SSSE3; + if ((CPUInfo[3] & 0x4000000)!=0) + return SSE_SSE2; + if ((CPUInfo[3] & 0x2000000)!=0) + return SSE_SSE; + } return SSE_NONE; } #endif diff --git a/threadmisc.cpp b/threadmisc.cpp index 4020711..742eda4 100644 --- a/threadmisc.cpp +++ b/threadmisc.cpp @@ -1,7 +1,3 @@ -// Typically we use the same global thread pool for all RAR modules. -static ThreadPool *GlobalPool=NULL; -static uint GlobalPoolUseCount=0; - static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection) { #ifdef _WIN_ALL @@ -43,53 +39,6 @@ static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection) } -static struct GlobalPoolCreateSync -{ - CRITSECT_HANDLE CritSection; - GlobalPoolCreateSync() { CriticalSectionCreate(&CritSection); } - ~GlobalPoolCreateSync() { CriticalSectionDelete(&CritSection); } -} PoolCreateSync; - - -ThreadPool* CreateThreadPool() -{ -#ifdef RARDLL - // We use a simple thread pool, which does not allow to add tasks from - // different functions and threads in the same time. It is ok for RAR, - // but UnRAR.dll can be used in multithreaded environment. So we return - // a new pool for UnRAR.dll every time. - return new ThreadPool(MaxPoolThreads); -#else - // Reuse the existing pool for RAR. - CriticalSectionStart(&PoolCreateSync.CritSection); - - if (GlobalPoolUseCount++ == 0) - GlobalPool=new ThreadPool(MaxPoolThreads); - - CriticalSectionEnd(&PoolCreateSync.CritSection); - return GlobalPool; -#endif -} - - -void DestroyThreadPool(ThreadPool *Pool) -{ - if (Pool!=NULL) - { -#ifdef RARDLL - delete Pool; -#else - CriticalSectionStart(&PoolCreateSync.CritSection); - - if (Pool==GlobalPool && GlobalPoolUseCount > 0 && --GlobalPoolUseCount == 0) - delete GlobalPool; - - CriticalSectionEnd(&PoolCreateSync.CritSection); -#endif - } -} - - static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data) { #ifdef _UNIX diff --git a/threadpool.cpp b/threadpool.cpp index 732dd75..8c63a8b 100644 --- a/threadpool.cpp +++ b/threadpool.cpp @@ -170,12 +170,13 @@ void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) CreateThreads(); // If queue is full, wait until it is empty. - if ((QueueTop + 1) % ASIZE(TaskQueue) == QueueBottom) + if (ActiveThreads>=ASIZE(TaskQueue)) WaitDone(); TaskQueue[QueueTop].Proc = Proc; TaskQueue[QueueTop].Param = Data; QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); + ActiveThreads++; } @@ -184,9 +185,6 @@ void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) // are sleeping yet. void ThreadPool::WaitDone() { - // We add ASIZE(TaskQueue) for case if TaskQueue array size is not - // a power of two. Negative numbers would not suit our purpose here. - ActiveThreads=(QueueTop+ASIZE(TaskQueue)-QueueBottom) % ASIZE(TaskQueue); if (ActiveThreads==0) return; #ifdef _WIN_ALL diff --git a/threadpool.hpp b/threadpool.hpp index dc45ca0..85ed90d 100644 --- a/threadpool.hpp +++ b/threadpool.hpp @@ -4,7 +4,10 @@ #ifndef RAR_SMP const uint MaxPoolThreads=1; // For single threaded version. #else -const uint MaxPoolThreads=32; +// We need to use the processor groups API to increase it beyond 64. +// Also be sure to check and adjust if needed per thread and total block size +// when compressing if going above 64. +const uint MaxPoolThreads=64; #ifdef _UNIX @@ -98,9 +101,6 @@ class ThreadPool #endif }; -ThreadPool* CreateThreadPool(); -void DestroyThreadPool(ThreadPool *Pool); - #endif // RAR_SMP #endif // _RAR_THREADPOOL_ diff --git a/ui.hpp b/ui.hpp index 69ce2a5..2654387 100644 --- a/ui.hpp +++ b/ui.hpp @@ -20,7 +20,7 @@ enum UIMESSAGE_CODE { UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, UIERROR_NOTSFX, UIERROR_OLDTOSFX, - UIERROR_WRONGSFXVER, UIERROR_ALREADYENC, UIERROR_DICTOUTMEM, + UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM, UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, @@ -38,6 +38,8 @@ enum UIMESSAGE_CODE { 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, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, @@ -91,16 +93,18 @@ bool uiIsGlobalPasswordSet(); enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; void uiAlarm(UIALARM_TYPE Type); +void uiEolAfterMsg(); bool uiAskNextVolume(wchar *VolName,size_t MaxSize); -bool uiAskRepeatRead(const wchar *FileName); +#if !defined(SILENT) && !defined(SFX_MODULE) +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); +#endif bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull); #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month); #endif - class uiMsgStore { private: diff --git a/uiconsole.cpp b/uiconsole.cpp index 3f4c654..f29ca47 100644 --- a/uiconsole.cpp +++ b/uiconsole.cpp @@ -1,3 +1,5 @@ +static bool AnyMessageDisplayed=0; // For console -idn switch. + // Purely user interface function. Gets and returns user input. UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) { @@ -83,6 +85,8 @@ void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) void uiMsgStore::Msg() { + AnyMessageDisplayed=true; + switch(Code) { case UIERROR_SYSERRMSG: @@ -99,6 +103,8 @@ void uiMsgStore::Msg() Log(Str[0],St(MDataBadCRC),Str[1],Str[0]); break; case UIERROR_BADPSW: + Log(Str[0],St(MWrongFilePassword),Str[1]); + break; case UIWAIT_BADPSW: Log(Str[0],St(MWrongPassword)); break; @@ -119,6 +125,7 @@ void uiMsgStore::Msg() Log(NULL,St(MErrSeek),Str[0]); break; case UIERROR_FILEREAD: + mprintf(L"\n"); Log(Str[0],St(MErrRead),Str[1]); break; case UIERROR_FILEWRITE: @@ -302,7 +309,15 @@ void uiMsgStore::Msg() case UIERROR_ULINKEXIST: Log(NULL,St(MSymLinkExists),Str[0]); break; - + case UIERROR_READERRTRUNCATED: + Log(NULL,St(MErrReadTrunc),Str[0]); + break; + case UIERROR_READERRCOUNT: + Log(NULL,St(MErrReadCount),Num[0]); + break; + case UIERROR_DIRNAMEEXISTS: + Log(NULL,St(MDirNameExists)); + break; #ifndef SFX_MODULE case UIMSG_STRING: @@ -395,11 +410,15 @@ bool uiAskNextVolume(wchar *VolName,size_t MaxSize) } -bool uiAskRepeatRead(const wchar *FileName) +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) { - mprintf(L"\n"); - Log(NULL,St(MErrRead),FileName); - return Ask(St(MRetryAbort))==1; + eprintf(St(MErrReadInfo)); + int Code=Ask(St(MIgnoreAllRetryQuit)); + + Ignore=(Code==1); + All=(Code==2); + Quit=(Code==4); + Retry=!Ignore && !All && !Quit; // Default also for invalid input, not just for 'Retry'. } @@ -421,3 +440,15 @@ const wchar *uiGetMonthName(int Month) return St(MonthID[Month]); } #endif + + +void uiEolAfterMsg() +{ + if (AnyMessageDisplayed) + { + // Avoid deleting several last characters of any previous error message + // with percentage indicator in -idn mode. + AnyMessageDisplayed=false; + mprintf(L"\n"); + } +} diff --git a/uisilent.cpp b/uisilent.cpp index 1b5de13..1df0975 100644 --- a/uisilent.cpp +++ b/uisilent.cpp @@ -67,3 +67,8 @@ const wchar *uiGetMonthName(int Month) return L""; } #endif + + +void uiEolAfterMsg() +{ +} diff --git a/unicode.cpp b/unicode.cpp index ab8d9b2..641f6c8 100644 --- a/unicode.cpp +++ b/unicode.cpp @@ -471,6 +471,7 @@ int wcsnicomp(const wchar *s1,const wchar *s2,size_t n) } +// Case insensitive wcsstr(). const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) { for (size_t i=0;str[i]!=0;i++) @@ -489,6 +490,8 @@ const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) wchar* wcslower(wchar *s) { #ifdef _WIN_ALL + // _wcslwr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharLower involves less overhead. CharLower(s); #else for (wchar *c=s;*c!=0;c++) @@ -503,6 +506,8 @@ wchar* wcslower(wchar *s) wchar* wcsupper(wchar *s) { #ifdef _WIN_ALL + // _wcsupr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharUpper involves less overhead. CharUpper(s); #else for (wchar *c=s;*c!=0;c++) @@ -520,8 +525,9 @@ int toupperw(int ch) #if defined(_WIN_ALL) // CharUpper is more reliable than towupper in Windows, which seems to be // C locale dependent even in Unicode version. For example, towupper failed - // to convert lowercase Russian characters. - return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)ch); + // to convert lowercase Russian characters. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff)); #else return towupper(ch); #endif @@ -532,8 +538,9 @@ int tolowerw(int ch) { #if defined(_WIN_ALL) // CharLower is more reliable than towlower in Windows. - // See comment for towupper above. - return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)ch); + // See comment for towupper above. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff)); #else return towlower(ch); #endif @@ -586,12 +593,6 @@ void SupportDBCS::Init() IsLeadByte[I]=IsDBCSLeadByte(I)!=0; } -// static -SupportDBCS& SupportDBCS::GetInstance() -{ - static SupportDBCS supportDBCS; - return supportDBCS; -} char* SupportDBCS::charnext(const char *s) { @@ -654,3 +655,5 @@ char* SupportDBCS::strrchrd(const char *s, int c) return((char *)found); } #endif + + diff --git a/unicode.hpp b/unicode.hpp index 103ff63..031ac09 100644 --- a/unicode.hpp +++ b/unicode.hpp @@ -33,7 +33,6 @@ class SupportDBCS public: SupportDBCS(); void Init(); - static SupportDBCS& GetInstance(); char* charnext(const char *s); size_t strlend(const char *s); @@ -45,13 +44,15 @@ class SupportDBCS bool DBCSMode; }; -inline char* charnext(const char *s) {return (char *)(IsDBCSMode() ? SupportDBCS::GetInstance().charnext(s):s+1);} -inline size_t strlend(const char *s) {return (uint)(IsDBCSMode() ? SupportDBCS::GetInstance().strlend(s):strlen(s));} -inline char* strchrd(const char *s, int c) {return (char *)(IsDBCSMode() ? SupportDBCS::GetInstance().strchrd(s,c):strchr(s,c));} -inline char* strrchrd(const char *s, int c) {return (char *)(IsDBCSMode() ? SupportDBCS::GetInstance().strrchrd(s,c):strrchr(s,c));} -inline void copychrd(char *dest,const char *src) {if (IsDBCSMode()) SupportDBCS::GetInstance().copychrd(dest,src); else *dest=*src;} -inline bool IsDBCSMode() {return(SupportDBCS::GetInstance().DBCSMode);} -inline void InitDBCS() {SupportDBCS::GetInstance().Init();} +extern SupportDBCS gdbcs; + +inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);} +inline size_t strlend(const char *s) {return (uint)(gdbcs.DBCSMode ? gdbcs.strlend(s):strlen(s));} +inline char* strchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strchrd(s,c):strchr(s,c));} +inline char* strrchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strrchrd(s,c):strrchr(s,c));} +inline void copychrd(char *dest,const char *src) {if (gdbcs.DBCSMode) gdbcs.copychrd(dest,src); else *dest=*src;} +inline bool IsDBCSMode() {return(gdbcs.DBCSMode);} +inline void InitDBCS() {gdbcs.Init();} #else #define charnext(s) ((s)+1) @@ -62,4 +63,5 @@ inline void InitDBCS() {SupportDBCS::GetInstance().Init();} inline void copychrd(char *dest,const char *src) {*dest=*src;} #endif + #endif diff --git a/unpack.cpp b/unpack.cpp index 0163c49..037c355 100644 --- a/unpack.cpp +++ b/unpack.cpp @@ -26,7 +26,7 @@ Unpack::Unpack(ComprDataIO *DataIO) UnpSomeRead=false; #ifdef RAR_SMP MaxUserThreads=1; - UnpThreadPool=CreateThreadPool(); + UnpThreadPool=NULL; ReadBufMT=NULL; UnpThreadData=NULL; #endif @@ -52,13 +52,24 @@ Unpack::~Unpack() if (Window!=NULL) free(Window); #ifdef RAR_SMP - DestroyThreadPool(UnpThreadPool); + delete UnpThreadPool; delete[] ReadBufMT; delete[] UnpThreadData; #endif } +#ifdef RAR_SMP +void Unpack::SetThreads(uint Threads) +{ + // More than 8 threads are unlikely to provide noticeable gain + // for unpacking, but would use the additional memory. + MaxUserThreads=Min(Threads,8); + UnpThreadPool=new ThreadPool(MaxUserThreads); +} +#endif + + void Unpack::Init(size_t WinSize,bool Solid) { // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size diff --git a/unpack.hpp b/unpack.hpp index ec5d688..75dadb0 100644 --- a/unpack.hpp +++ b/unpack.hpp @@ -24,7 +24,7 @@ #define MAX_FILTER_BLOCK_SIZE 0x400000 // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, -// so we keep number of buffered filter in unpacker reasonable. +// so we keep a number of buffered filters in unpacker reasonable. #define UNPACK_MAX_WRITE 0x400000 // Decode compressed bit fields to alphabet numbers. @@ -382,10 +382,7 @@ class Unpack:PackDef void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} #ifdef RAR_SMP - // More than 8 threads are unlikely to provide a noticeable gain - // for unpacking, but would use the additional memory. - void SetThreads(uint Threads) {MaxUserThreads=Min(Threads,8);} - + void SetThreads(uint Threads); void UnpackDecode(UnpackThreadData &D); #endif diff --git a/unpack50.cpp b/unpack50.cpp index ec60c82..9911950 100644 --- a/unpack50.cpp +++ b/unpack50.cpp @@ -436,6 +436,10 @@ byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) } return SrcData; case FILTER_ARM: + // 2019-11-15: we turned off ARM filter by default when compressing, + // mostly because it is inefficient for modern 64 bit ARM binaries. + // It was turned on by default in 5.0 - 5.80b3 , so we still need it + // here for compatibility with some of previously created archives. { uint FileOffset=(uint)WrittenFileSize; // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3" diff --git a/version.hpp b/version.hpp index f9a2ae0..99e7cae 100644 --- a/version.hpp +++ b/version.hpp @@ -1,6 +1,6 @@ -#define RARVER_MAJOR 5 -#define RARVER_MINOR 71 -#define RARVER_BETA 1 -#define RARVER_DAY 2 -#define RARVER_MONTH 4 -#define RARVER_YEAR 2019 +#define RARVER_MAJOR 6 +#define RARVER_MINOR 0 +#define RARVER_BETA 0 +#define RARVER_DAY 1 +#define RARVER_MONTH 12 +#define RARVER_YEAR 2020 diff --git a/win32acl.cpp b/win32acl.cpp index 6bc865b..d4797bd 100644 --- a/win32acl.cpp +++ b/win32acl.cpp @@ -67,7 +67,7 @@ void ExtractACL20(Archive &Arc,const wchar *FileName) void ExtractACL(Archive &Arc,const wchar *FileName) { Array SubData; - if (!Arc.ReadSubData(&SubData,NULL)) + if (!Arc.ReadSubData(&SubData,NULL,false)) return; SetACLPrivileges(); diff --git a/win32stm.cpp b/win32stm.cpp index 54b872e..eaa43be 100644 --- a/win32stm.cpp +++ b/win32stm.cpp @@ -104,7 +104,8 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) if (TestMode) { - Arc.ReadSubData(NULL,NULL); + File CurFile; + Arc.ReadSubData(NULL,&CurFile,true); return; } @@ -116,7 +117,7 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; - if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile)) + if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile,false)) CurFile.Close(); File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE))