From f367a22fe357c923e4607e7b3655c6003590b20d Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:16:31 +0000 Subject: [PATCH 01/76] Added unrar 6.0.3 --- unrar/acknow.txt | 2 +- unrar/arcread.cpp | 2 +- unrar/dll.rc | 8 ++++---- unrar/version.hpp | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unrar/acknow.txt b/unrar/acknow.txt index a68b6727..60a772fa 100644 --- a/unrar/acknow.txt +++ b/unrar/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/unrar/arcread.cpp b/unrar/arcread.cpp index b0cf39f3..d1df6c04 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -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; diff --git a/unrar/dll.rc b/unrar/dll.rc index d4bb3e4c..e12a9272 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 0, 2, 3610 -PRODUCTVERSION 6, 0, 2, 3610 +FILEVERSION 6, 0, 100, 3629 +PRODUCTVERSION 6, 0, 100, 3629 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", "6.0.2\0" - VALUE "ProductVersion", "6.0.2\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" } diff --git a/unrar/version.hpp b/unrar/version.hpp index 236da7ec..99e7caeb 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 0 -#define RARVER_BETA 2 -#define RARVER_DAY 12 -#define RARVER_MONTH 11 +#define RARVER_BETA 0 +#define RARVER_DAY 1 +#define RARVER_MONTH 12 #define RARVER_YEAR 2020 From c00e9c1493eec4b801677953d8a1b295a7288c40 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:16:32 +0000 Subject: [PATCH 02/76] Added unrar 6.0.4 --- unrar/dll.rc | 10 +++++----- unrar/extinfo.hpp | 2 +- unrar/extract.cpp | 30 ++++++++++++++++++++---------- unrar/filcreat.cpp | 2 +- unrar/filefn.cpp | 22 ++++++++++++++++++++-- unrar/filefn.hpp | 2 +- unrar/hardlinks.cpp | 4 ++-- unrar/loclang.hpp | 2 +- unrar/savepos.hpp | 40 ---------------------------------------- unrar/secpassword.cpp | 2 +- unrar/uiconsole.cpp | 2 +- unrar/ulinks.cpp | 13 +++++++++---- unrar/version.hpp | 10 +++++----- unrar/win32lnk.cpp | 22 +++++++++++++++++++++- 14 files changed, 88 insertions(+), 75 deletions(-) delete mode 100644 unrar/savepos.hpp diff --git a/unrar/dll.rc b/unrar/dll.rc index e12a9272..146ea1fd 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 0, 100, 3629 -PRODUCTVERSION 6, 0, 100, 3629 +FILEVERSION 6, 1, 1, 3721 +PRODUCTVERSION 6, 1, 1, 3721 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "6.0.0\0" - VALUE "ProductVersion", "6.0.0\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0" + VALUE "FileVersion", "6.1.1\0" + VALUE "ProductVersion", "6.1.1\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/extinfo.hpp b/unrar/extinfo.hpp index 2b0005da..f3c7511b 100644 --- a/unrar/extinfo.hpp +++ b/unrar/extinfo.hpp @@ -7,7 +7,7 @@ bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wcha void SetUnixOwner(Archive &Arc,const wchar *FileName); #endif -bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); diff --git a/unrar/extract.cpp b/unrar/extract.cpp index abcd3c33..dc824aa9 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -654,7 +654,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting)); if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) - LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting)); + LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); else LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); } @@ -666,7 +666,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } else { - uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName); + uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName); LinkSuccess=false; } @@ -747,7 +747,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } } else - mprintf(L"\b\b\b\b\b "); + { + // We check SkipSolid to remove percent for skipped solid files only. + // We must not apply these \b to links with ShowChecksum==false + // and their possible error messages. + if (SkipSolid) + 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, @@ -1100,19 +1106,23 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) } if (!DirExist) { - CreatePath(DestFileName,true); + CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); - if (MDCode!=MKDIR_SUCCESS) + if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName)) { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); wchar OrigName[ASIZE(DestFileName)]; wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); MakeNameUsable(DestFileName,true); - CreatePath(DestFileName,true); - MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); #ifndef SFX_MODULE - if (MDCode==MKDIR_SUCCESS) - uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif + DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); + if (!DirExist) + { + CreatePath(DestFileName,true,Cmd->DisableNames); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + } } } } @@ -1191,7 +1201,7 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) MakeNameUsable(DestFileName,true); - CreatePath(DestFileName,true); + CreatePath(DestFileName,true,Cmd->DisableNames); if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { #ifndef SFX_MODULE diff --git a/unrar/filcreat.cpp b/unrar/filcreat.cpp index a64a7d4d..620bee81 100644 --- a/unrar/filcreat.cpp +++ b/unrar/filcreat.cpp @@ -49,7 +49,7 @@ bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, if (NewFile!=NULL && NewFile->Create(Name,FileMode)) return true; - CreatePath(Name,true); + CreatePath(Name,true,Cmd->DisableNames); return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index 57a334dd..bab7c738 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -38,7 +38,7 @@ MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) } -bool CreatePath(const wchar *Path,bool SkipLastName) +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) { if (Path==NULL || *Path==0) return false; @@ -73,7 +73,7 @@ bool CreatePath(const wchar *Path,bool SkipLastName) DirName[s-Path]=0; Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; - if (Success) + if (Success && !Silent) { mprintf(St(MCreatDir),DirName); mprintf(L" %s",St(MOk)); @@ -474,6 +474,24 @@ bool DelFile(const wchar *Name) } +bool DelDir(const wchar *Name) +{ +#ifdef _WIN_ALL + bool Success=RemoveDirectory(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=RemoveDirectory(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=rmdir(NameA)==0; + return Success; +#endif +} #if defined(_WIN_ALL) && !defined(SFX_MODULE) diff --git a/unrar/filefn.hpp b/unrar/filefn.hpp index 1bda9b58..78735fee 100644 --- a/unrar/filefn.hpp +++ b/unrar/filefn.hpp @@ -4,7 +4,7 @@ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); -bool CreatePath(const wchar *Path,bool SkipLastName); +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent); void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); bool IsRemovable(const wchar *Name); diff --git a/unrar/hardlinks.cpp b/unrar/hardlinks.cpp index 946a3952..40cc0aa4 100644 --- a/unrar/hardlinks.cpp +++ b/unrar/hardlinks.cpp @@ -1,4 +1,4 @@ -bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) { SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. @@ -9,7 +9,7 @@ bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) ErrHandler.SetErrorCode(RARX_CREATE); return false; } - CreatePath(NameNew,true); + CreatePath(NameNew,true,Cmd->DisableNames); #ifdef _WIN_ALL bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 99108c93..a1cd544a 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -321,7 +321,7 @@ #define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n" #define MInvalidName L"\nERROR: Invalid file name %s" #define MProcessArc L"\n\nProcessing archive %s" -#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file name" +#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name" #define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" #define MUnknownOption L"\nERROR: Unknown option: %s" #define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored" diff --git a/unrar/savepos.hpp b/unrar/savepos.hpp deleted file mode 100644 index 1f8353f6..00000000 --- a/unrar/savepos.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _RAR_SAVEPOS_ -#define _RAR_SAVEPOS_ - -class SaveFilePos -{ - private: - File *SaveFile; - int64 SavePos; - public: - SaveFilePos(File &Src) - { - SaveFile=&Src; - SavePos=Src.Tell(); - } - ~SaveFilePos() - { - // Unless the file is already closed either by current exception - // processing or intentionally by external code. - if (SaveFile->IsOpened()) - { - 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(); - } - } - } -}; - -#endif diff --git a/unrar/secpassword.cpp b/unrar/secpassword.cpp index 4865b3fd..b99e53af 100644 --- a/unrar/secpassword.cpp +++ b/unrar/secpassword.cpp @@ -51,7 +51,7 @@ class CryptLoader }; // We need to call FreeLibrary when RAR is exiting. -CryptLoader GlobalCryptLoader; +static CryptLoader GlobalCryptLoader; #endif SecPassword::SecPassword() diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index f29ca477..4c8329e0 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -1,4 +1,4 @@ -static bool AnyMessageDisplayed=0; // For console -idn switch. +static bool AnyMessageDisplayed=false; // 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) diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index 1656824f..d198f2e0 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -1,9 +1,14 @@ -static bool UnixSymlink(const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) +static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { - CreatePath(LinkName,true); + CreatePath(LinkName,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. DelFile(LinkName); + char LinkNameA[NM]; WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); if (symlink(Target,LinkNameA)==-1) // Error. @@ -75,7 +80,7 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w if (!Cmd->AbsoluteLinks && (*TargetW==0 || IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) return false; - return UnixSymlink(Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); + return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; } @@ -101,5 +106,5 @@ bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) if (!Cmd->AbsoluteLinks && (IsFullPath(Target) || !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) return false; - return UnixSymlink(Target,Name,&hd->mtime,&hd->atime); + return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } diff --git a/unrar/version.hpp b/unrar/version.hpp index 99e7caeb..d345aa56 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 0 -#define RARVER_BETA 0 -#define RARVER_DAY 1 -#define RARVER_MONTH 12 -#define RARVER_YEAR 2020 +#define RARVER_MINOR 1 +#define RARVER_BETA 1 +#define RARVER_DAY 3 +#define RARVER_MONTH 3 +#define RARVER_YEAR 2021 diff --git a/unrar/win32lnk.cpp b/unrar/win32lnk.cpp index a68ed75a..84ab63ff 100644 --- a/unrar/win32lnk.cpp +++ b/unrar/win32lnk.cpp @@ -71,20 +71,36 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) return false; - CreatePath(Name,true); + CreatePath(Name,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. + if (FileExist(Name)) + if (IsDir(GetFileAttr(Name))) + DelDir(Name); + else + DelFile(Name); // 'DirTarget' check is important for Unix symlinks to directories. // Unix symlinks do not have their own 'directory' attribute. if (hd->Dir || hd->DirTarget) { if (!CreateDirectory(Name,NULL)) + { + uiMsg(UIERROR_DIRCREATE,UINULL,Name); + ErrHandler.SetErrorCode(RARX_CREATE); return false; + } } else { HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile == INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); return false; + } CloseHandle(hFile); } @@ -138,7 +154,11 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT| FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); + ErrHandler.SetErrorCode(RARX_CREATE); return false; + } DWORD Returned; if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb, From 1502c092f08357581d62209db2ebfc204db5664e Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:16:33 +0000 Subject: [PATCH 03/76] Added unrar 6.0.5 --- unrar/dll.rc | 8 ++++---- unrar/uiconsole.cpp | 13 ++++++++++++- unrar/version.hpp | 6 +++--- unrar/volume.cpp | 2 +- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 146ea1fd..8dd505da 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 1, 1, 3721 -PRODUCTVERSION 6, 1, 1, 3721 +FILEVERSION 6, 1, 100, 3756 +PRODUCTVERSION 6, 1, 100, 3756 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", "6.1.1\0" - VALUE "ProductVersion", "6.1.1\0" + VALUE "FileVersion", "6.1.0\0" + VALUE "ProductVersion", "6.1.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index 4c8329e0..ceae1a7c 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -85,7 +85,18 @@ void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) void uiMsgStore::Msg() { - AnyMessageDisplayed=true; + // When creating volumes, AnyMessageDisplayed must be reset for UIEVENT_NEWARCHIVE, + // so it ignores this and all earlier messages like UIEVENT_PROTECTEND + // and UIEVENT_PROTECTEND, because they precede "Creating archive" message + // and do not interfere with -idn and file names. If we do not ignore them, + // uiEolAfterMsg() in uiStartFileAddit() can cause unneeded carriage return + // in archiving percent after creating a new volume with -v -idn (and -rr + // for UIEVENT_PROTECT*) switches. AnyMessageDisplayed is set for messages + // after UIEVENT_NEWARCHIVE, so archiving percent with -idn is moved to + // next line and does not delete their last characters. + // Similarly we ignore UIEVENT_RRTESTINGEND for volumes, because it is issued + // before "Testing archive" and would add an excessive \n otherwise. + AnyMessageDisplayed=(Code!=UIEVENT_NEWARCHIVE && Code!=UIEVENT_RRTESTINGEND); switch(Code) { diff --git a/unrar/version.hpp b/unrar/version.hpp index d345aa56..922c67ed 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 1 -#define RARVER_BETA 1 -#define RARVER_DAY 3 -#define RARVER_MONTH 3 +#define RARVER_BETA 0 +#define RARVER_DAY 7 +#define RARVER_MONTH 4 #define RARVER_YEAR 2021 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index 6cfa772e..001a9673 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -136,7 +136,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma Arc.ConvertAttributes(); Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); } - if (ShowFileName) + if (ShowFileName && !Cmd->DisableNames) { mprintf(St(MExtrPoints),Arc.FileHead.FileName); if (!Cmd->DisablePercentage) From b7ec361367124e666d06e658e10536e4d70ef7c2 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:16:33 +0000 Subject: [PATCH 04/76] Added unrar 6.0.6 --- unrar/dll.rc | 8 ++++---- unrar/errhnd.cpp | 2 +- unrar/extract.cpp | 2 ++ unrar/list.cpp | 38 ++++++++++++++++++++++---------------- unrar/pathfn.cpp | 4 +++- unrar/version.hpp | 8 ++++---- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 8dd505da..6bd0ef4d 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 1, 100, 3756 -PRODUCTVERSION 6, 1, 100, 3756 +FILEVERSION 6, 2, 1, 3796 +PRODUCTVERSION 6, 2, 1, 3796 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", "6.1.0\0" - VALUE "ProductVersion", "6.1.0\0" + VALUE "FileVersion", "6.2.1\0" + VALUE "ProductVersion", "6.2.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/errhnd.cpp b/unrar/errhnd.cpp index 18e91973..d473b1f8 100644 --- a/unrar/errhnd.cpp +++ b/unrar/errhnd.cpp @@ -367,7 +367,7 @@ bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) void ErrorHandler::SysErrMsg() { -#if !defined(SFX_MODULE) && !defined(SILENT) +#ifndef SILENT wchar Msg[1024]; if (!GetSysErrMsg(Msg,ASIZE(Msg))) return; diff --git a/unrar/extract.cpp b/unrar/extract.cpp index dc824aa9..6c3e214a 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -888,6 +888,8 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De wchar LastChar=*PointToLastChar(Cmd->ExtrPath); // We need IsPathDiv check here to correctly handle Unix forward slash // in the end of destination path in Windows: rar x arc dest/ + // so we call IsPathDiv first instead of just calling AddEndSlash, + // which checks for only one type of path separator. // IsDriveDiv is needed for current drive dir: rar x arc d: if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) { diff --git a/unrar/list.cpp b/unrar/list.cpp index 476fd3c6..9b652185 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -1,6 +1,6 @@ #include "rar.hpp" -static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare); +static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames); static void ListSymLink(Archive &Arc); static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); static void ListOldSubHeader(Archive &Arc); @@ -95,7 +95,7 @@ void ListArchive(CommandData *Cmd) FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0; if (FileMatched) { - ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare); + ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames); if (!Arc.FileHead.SplitBefore) { TotalUnpSize+=Arc.FileHead.UnpSize; @@ -108,7 +108,7 @@ void ListArchive(CommandData *Cmd) if (FileMatched && !Bare) { if (Technical && ShowService) - ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false); + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,Cmd->DisableNames); } break; } @@ -188,31 +188,37 @@ enum LISTCOL_TYPE { }; -void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare) +void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames) { - wchar *Name=hd.FileName; - RARFORMAT Format=Arc.Format; - - if (Bare) - { - mprintf(L"%s\n",Name); - return; - } - - if (!TitleShown && !Technical) + if (!TitleShown && !Technical && !Bare) { if (Verbose) { mprintf(L"\n%ls",St(MListTitleV)); - mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + if (!DisableNames) + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); } else { mprintf(L"\n%ls",St(MListTitleL)); - mprintf(L"\n----------- --------- ---------- ----- ----"); + if (!DisableNames) + mprintf(L"\n----------- --------- ---------- ----- ----"); } + // Must be set even in DisableNames mode to suppress "0 files" output + // unless no files are matched. TitleShown=true; } + if (DisableNames) + return; + + wchar *Name=hd.FileName; + RARFORMAT Format=Arc.Format; + + if (Bare) + { + mprintf(L"%s\n",Name); + return; + } wchar UnpSizeText[30],PackSizeText[30]; if (hd.UnpSize==INT64NDF) diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 41594bf9..18f70982 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -184,7 +184,9 @@ void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize // the temporary buffer instead of constructing the name in 'Pathname'. wchar OutName[NM]; wcsncpyz(OutName,Path,ASIZE(OutName)); - AddEndSlash(OutName,ASIZE(OutName)); + // Do not add slash to d:, we want to allow relative paths like d:filename. + if (!IsDriveLetter(Path) || Path[2]!=0) + AddEndSlash(OutName,ASIZE(OutName)); wcsncatz(OutName,Name,ASIZE(OutName)); wcsncpyz(Pathname,OutName,MaxSize); } diff --git a/unrar/version.hpp b/unrar/version.hpp index 922c67ed..f9d8ddf7 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 1 -#define RARVER_BETA 0 -#define RARVER_DAY 7 -#define RARVER_MONTH 4 +#define RARVER_MINOR 2 +#define RARVER_BETA 1 +#define RARVER_DAY 17 +#define RARVER_MONTH 5 #define RARVER_YEAR 2021 From 092285440551ee9a850674b3b35074ac313fefba Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:16:34 +0000 Subject: [PATCH 05/76] Added unrar 6.0.7 --- unrar/dll.rc | 8 ++++---- unrar/filcreat.cpp | 6 ++++-- unrar/version.hpp | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 6bd0ef4d..9a590a23 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 2, 1, 3796 -PRODUCTVERSION 6, 2, 1, 3796 +FILEVERSION 6, 2, 100, 3821 +PRODUCTVERSION 6, 2, 100, 3821 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", "6.2.1\0" - VALUE "ProductVersion", "6.2.1\0" + VALUE "FileVersion", "6.2.0\0" + VALUE "ProductVersion", "6.2.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/filcreat.cpp b/unrar/filcreat.cpp index 620bee81..8e7562cb 100644 --- a/unrar/filcreat.cpp +++ b/unrar/filcreat.cpp @@ -1,7 +1,7 @@ #include "rar.hpp" // If NewFile==NULL, we delete created file after user confirmation. -// It is useful we we need to overwrite an existing folder or file, +// It is useful if we need to overwrite an existing folder or file, // but need user confirmation for that. bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) @@ -44,7 +44,9 @@ bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, } // Try to truncate the existing file first instead of delete, - // so we preserve existing file permissions such as NTFS permissions. + // so we preserve existing file permissions, such as NTFS permissions, + // also as "Compressed" attribute and hard links. In GUI version we avoid + // deleting an existing file for non-.rar archive formats as well. uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; if (NewFile!=NULL && NewFile->Create(Name,FileMode)) return true; diff --git a/unrar/version.hpp b/unrar/version.hpp index f9d8ddf7..cf1c77cb 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 2 -#define RARVER_BETA 1 -#define RARVER_DAY 17 -#define RARVER_MONTH 5 +#define RARVER_BETA 0 +#define RARVER_DAY 11 +#define RARVER_MONTH 6 #define RARVER_YEAR 2021 From 2f979b4b6405fae2a4b1aeb0a57107e63766e693 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:17:58 +0000 Subject: [PATCH 06/76] Added unrar 6.1.1 --- unrar/acknow.txt | 47 +++---------------- unrar/archive.cpp | 2 +- unrar/arcread.cpp | 3 ++ unrar/cmddata.cpp | 26 ++++++++++- unrar/cmddata.hpp | 2 +- unrar/cmdfilter.cpp | 2 + unrar/cmdmix.cpp | 16 +++---- unrar/consio.cpp | 31 ++++++++++++- unrar/consio.hpp | 1 + unrar/crc.cpp | 10 ++++ unrar/dll.rc | 8 ++-- unrar/encname.cpp | 2 - unrar/encname.hpp | 3 +- unrar/extract.cpp | 48 +++++++++++-------- unrar/file.cpp | 54 ++++++++++++++++++++-- unrar/file.hpp | 6 +-- unrar/filefn.cpp | 12 +++++ unrar/filefn.hpp | 1 + unrar/find.cpp | 6 +++ unrar/isnt.cpp | 1 + unrar/list.cpp | 13 +++--- unrar/loclang.hpp | 12 ++++- unrar/options.hpp | 10 +++- unrar/os.hpp | 17 ++++--- unrar/pathfn.cpp | 76 ++++++++++++++++++++++++++---- unrar/pathfn.hpp | 3 +- unrar/rar.hpp | 2 +- unrar/rdwrfn.cpp | 33 ------------- unrar/recvol5.cpp | 31 +++++++++---- unrar/rijndael.cpp | 110 ++++++++++++++++++++++++-------------------- unrar/rijndael.hpp | 6 +-- unrar/rs16.cpp | 4 +- unrar/strfn.cpp | 50 ++++++-------------- unrar/strfn.hpp | 8 ++-- unrar/timefn.cpp | 4 +- unrar/ui.hpp | 2 +- unrar/uiconsole.cpp | 9 ++++ unrar/unicode.cpp | 53 --------------------- unrar/unicode.hpp | 19 +------- unrar/version.hpp | 8 ++-- unrar/volume.cpp | 56 +++++----------------- 41 files changed, 431 insertions(+), 376 deletions(-) diff --git a/unrar/acknow.txt b/unrar/acknow.txt index 60a772fa..ec2c2c7c 100644 --- a/unrar/acknow.txt +++ b/unrar/acknow.txt @@ -7,49 +7,16 @@ for samples and ideas allowed to make Reed-Solomon coding more efficient. -* RAR text compression algorithm is based on Dmitry Shkarin PPMII +* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII and Dmitry Subbotin carryless rangecoder public domain source code. - You may find it in ftp.elf.stuba.sk/pub/pc/pack. + You can find it in ftp.elf.stuba.sk/pub/pc/pack. -* RAR encryption includes parts of code from Szymon Stefanek - and Brian Gladman AES implementations also as Steve Reid SHA-1 source. +* RAR encryption includes parts of public domain code + from Szymon Stefanek AES and Steve Reid SHA-1 implementations. - --------------------------------------------------------------------------- - Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. - All rights reserved. - - LICENSE TERMS - - The free distribution and use of this software in both source and binary - form is allowed (with or without changes) provided that: - - 1. distributions of this source code include the above copyright - notice, this list of conditions and the following disclaimer; - - 2. distributions in binary form include the above copyright - notice, this list of conditions and the following disclaimer - in the documentation and/or other associated materials; - - 3. the copyright holder's name is not used to endorse products - built using this software without specific written permission. - - ALTERNATIVELY, provided that this notice is retained in full, this product - may be distributed under the terms of the GNU General Public License (GPL), - in which case the provisions of the GPL apply INSTEAD OF those given above. - - DISCLAIMER - - This software is provided 'as is' with no explicit or implied warranties - in respect of its properties, including, but not limited to, correctness - and/or fitness for purpose. - --------------------------------------------------------------------------- - - Source code of this package also as other cryptographic technology - and computing project related links are available on Brian Gladman's - web site: http://www.gladman.me.uk - -* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. - Original Intel Slicing-by-8 code is available here: +* With exception of SFX modules, RAR uses CRC32 function based + on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code + is available here: https://sourceforge.net/projects/slicing-by-8/ diff --git a/unrar/archive.cpp b/unrar/archive.cpp index 8c5a1da8..0f5de946 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -231,7 +231,7 @@ bool Archive::IsArchive(bool EnableBroken) // first file header to set "comment" flag when reading service header. // Unless we are in silent mode, we need to know about presence of comment // immediately after IsArchive call. - if (HeadersLeft && (!SilentOpen || !Encrypted)) + if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable()) { int64 SavePos=Tell(); int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index d1df6c04..4e9f5064 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -558,6 +558,9 @@ size_t Archive::ReadHeader50() return 0; #else + if (Cmd->SkipEncrypted) + return 0; + byte HeadersInitV[SIZE_INITV]; if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) { diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 455a07b2..82d24f44 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -108,6 +108,11 @@ void CommandData::ParseArg(wchar *Arg) // 'S' can contain SFX name, which case is important in Unix. if (*Command!='I' && *Command!='S') wcsupper(Command); + if (*Command=='P') // Enforce -idq for print command. + { + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); + } } else if (*ArcName==0) @@ -373,6 +378,9 @@ void CommandData::ProcessSwitch(const wchar *Switch) case '3': ExclPath=EXCL_ABSPATH; break; + case '4': + wcsncpyz(ExclArcPath,Switch+3,ASIZE(ExclArcPath)); + break; } break; default: @@ -399,6 +407,8 @@ void CommandData::ProcessSwitch(const wchar *Switch) EncryptHeaders=true; if (Switch[2]!=0) { + if (wcslen(Switch+2)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); Password.Set(Switch+2); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } @@ -561,6 +571,10 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; case 'D': break; + case 'E': + if (toupperw(Switch[2])=='S' && Switch[3]==0) + SkipEncrypted=true; + break; case 'S': { wchar StoreNames[1024]; @@ -648,6 +662,10 @@ void CommandData::ProcessSwitch(const wchar *Switch) AllowIncompatNames=true; break; #endif + case 'P': + wcsncpyz(ExtrPath,Switch+2,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); + break; case 'R': Overwrite=OVERWRITE_AUTORENAME; break; @@ -672,6 +690,8 @@ void CommandData::ProcessSwitch(const wchar *Switch) } else { + if (wcslen(Switch+1)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); Password.Set(Switch+1); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } @@ -752,6 +772,10 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'D': Solid|=SOLID_VOLUME_DEPENDENT; break; + case 'I': + ProhibitConsoleInput(); + wcsncpyz(UseStdin,Switch[2] ? Switch+2:L"stdin",ASIZE(UseStdin)); + break; case 'L': if (IsDigit(Switch[2])) FileSizeLess=atoilw(Switch+2); @@ -935,7 +959,7 @@ void CommandData::ProcessCommand() wcsncpyz(ArcName,Name,ASIZE(ArcName)); } - if (wcschr(L"AFUMD",*Command)==NULL) + if (wcschr(L"AFUMD",*Command)==NULL && *UseStdin==0) { if (GenerateArcName) { diff --git a/unrar/cmddata.hpp b/unrar/cmddata.hpp index 719b4007..2a219220 100644 --- a/unrar/cmddata.hpp +++ b/unrar/cmddata.hpp @@ -50,7 +50,7 @@ class CommandData:public RAROptions bool GetArcName(wchar *Name,int MaxSize); bool CheckWinSize(); - int GetRecoverySize(const wchar *Str,int DefSize); + int GetRecoverySize(const wchar *CmdStr,const wchar *Value,int DefSize); #ifndef SFX_MODULE void ReportWrongSwitches(RARFORMAT Format); diff --git a/unrar/cmdfilter.cpp b/unrar/cmdfilter.cpp index d6517ceb..d098d108 100644 --- a/unrar/cmdfilter.cpp +++ b/unrar/cmdfilter.cpp @@ -262,6 +262,8 @@ bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta) // Return 'true' if we need to exclude the file from processing. bool CommandData::SizeCheck(int64 Size) { + if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size. + return false; if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) return true; if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 3990cc18..1fc4b8ca 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -61,14 +61,14 @@ void CommandData::OutHelp(RAR_EXIT ExitCode) MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, - MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, - MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, - MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, - MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, - MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM, - MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU, - MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal, - MCHelpSwY + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP, + MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwN,MCHelpSwNa, + MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOP,MCHelpSwOR, + MCHelpSwOW,MCHelpSwP,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSI, + MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO, + MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX, + MCHelpSwXa,MCHelpSwXal,MCHelpSwY #endif }; diff --git a/unrar/consio.cpp b/unrar/consio.cpp index fedd5c05..f30f4d02 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -3,6 +3,7 @@ static MESSAGE_TYPE MsgStream=MSG_STDOUT; static RAR_CHARSET RedirectCharset=RCH_DEFAULT; +static bool ProhibitInput=false; const int MaxMsgSize=2*NM+2048; @@ -61,6 +62,12 @@ void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset) } +void ProhibitConsoleInput() +{ + ProhibitInput=true; +} + + #ifndef SILENT static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { @@ -141,10 +148,23 @@ void eprintf(const wchar *fmt,...) #ifndef SILENT +static void QuitIfInputProhibited() +{ + // We cannot handle user prompts if -si is used to read file or archive data + // from stdin. + if (ProhibitInput) + { + mprintf(St(MStdinNoInput)); + ErrHandler.Exit(RARX_FATAL); + } +} + + static void GetPasswordText(wchar *Str,uint MaxLength) { if (MaxLength==0) return; + QuitIfInputProhibited(); if (StdinRedirected) getwstr(Str,MaxLength); // Read from pipe or redirected file. else @@ -190,16 +210,21 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword * while (true) { - if (!StdinRedirected) +// if (!StdinRedirected) if (Type==UIPASSWORD_GLOBAL) eprintf(L"\n%s: ",St(MAskPsw)); else eprintf(St(MAskPswFor),FileName); - wchar PlainPsw[MAXPASSWORD]; + wchar PlainPsw[MAXPASSWORD+1]; GetPasswordText(PlainPsw,ASIZE(PlainPsw)); if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) return false; + if (wcslen(PlainPsw)>=MAXPASSWORD) + { + PlainPsw[MAXPASSWORD-1]=0; + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + } if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) { eprintf(St(MReAskPsw)); @@ -229,6 +254,8 @@ bool getwstr(wchar *str,size_t n) // Print buffered prompt title function before waiting for input. fflush(stderr); + QuitIfInputProhibited(); + *str=0; #if defined(_WIN_ALL) // fgetws does not work well with non-English text in Windows, diff --git a/unrar/consio.hpp b/unrar/consio.hpp index 903dc21a..bf97289e 100644 --- a/unrar/consio.hpp +++ b/unrar/consio.hpp @@ -4,6 +4,7 @@ void InitConsole(); void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); +void ProhibitConsoleInput(); void OutComment(const wchar *Comment,size_t Size); #ifndef SILENT diff --git a/unrar/crc.cpp b/unrar/crc.cpp index cf23bbf4..0e5e1320 100644 --- a/unrar/crc.cpp +++ b/unrar/crc.cpp @@ -14,6 +14,12 @@ #include "rar.hpp" +#ifndef SFX_MODULE +// User suggested to avoid BSD license in SFX module, so they do not need +// to include the license to SFX archive. +#define USE_SLICING +#endif + static uint crc_tables[8][256]; // Tables for Slicing-by-8. @@ -37,6 +43,7 @@ static void InitTables() { InitCRC32(crc_tables[0]); +#ifdef USE_SLICING for (uint I=0;I<256;I++) // Build additional lookup tables. { uint C=crc_tables[0][I]; @@ -46,6 +53,7 @@ static void InitTables() crc_tables[J][I]=C; } } +#endif } @@ -55,6 +63,7 @@ uint CRC32(uint StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; +#ifdef USE_SLICING // Align Data to 8 for better performance. for (;Size>0 && ((size_t)Data & 7);Size--,Data++) StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); @@ -77,6 +86,7 @@ uint CRC32(uint StartCRC,const void *Addr,size_t Size) crc_tables[1][(byte)(NextData >> 16)] ^ crc_tables[0][(byte)(NextData >> 24)]; } +#endif for (;Size>0;Size--,Data++) // Process left data. StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); diff --git a/unrar/dll.rc b/unrar/dll.rc index 9a590a23..5cec308d 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 2, 100, 3821 -PRODUCTVERSION 6, 2, 100, 3821 +FILEVERSION 6, 10, 1, 3939 +PRODUCTVERSION 6, 10, 1, 3939 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", "6.2.0\0" - VALUE "ProductVersion", "6.2.0\0" + VALUE "FileVersion", "6.10.1\0" + VALUE "ProductVersion", "6.10.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/encname.cpp b/unrar/encname.cpp index 84731a71..e1ba1ed7 100644 --- a/unrar/encname.cpp +++ b/unrar/encname.cpp @@ -20,8 +20,6 @@ void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncS { if (FlagBits==0) { - if (EncPos>=EncSize) - break; Flags=EncName[EncPos++]; FlagBits=8; } diff --git a/unrar/encname.hpp b/unrar/encname.hpp index 538bcb69..c6b4ed44 100644 --- a/unrar/encname.hpp +++ b/unrar/encname.hpp @@ -4,9 +4,8 @@ class EncodeFileName { private: - void AddFlags(int Value); + void AddFlags(byte Value,byte *EncName); - byte *EncName; byte Flags; uint FlagBits; size_t FlagsPos; diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 6c3e214a..61fc5827 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -82,7 +82,7 @@ void CmdExtract::DoExtract() void CmdExtract::ExtractArchiveInit(Archive &Arc) { - DataIO.UnpArcSize=Arc.FileLength(); + DataIO.UnpArcSize=Arc.IsSeekable() ? Arc.FileLength() : 0; FileCount=0; MatchedArgs=0; @@ -105,8 +105,22 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc) EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); - if (!Arc.WOpen(ArcName)) - return EXTRACT_ARC_NEXT; + if (*Cmd->UseStdin!=0) + { + Arc.SetHandleType(FILE_HANDLESTD); +#ifdef USE_QOPEN + Arc.SetProhibitQOpen(true); +#endif + } + else + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache. + if (*Cmd->Command=='T' || Cmd->Test) + ResetFileCache(ArcName); // Reset the file cache when testing an archive. +#endif + if (!Arc.WOpen(ArcName)) + return EXTRACT_ARC_NEXT; + } if (!Arc.IsArchive(true)) { @@ -403,6 +417,12 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) FirstFile=false; #endif + if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) + if (Arc.Solid) + return false; // Abort the entire extraction for solid archive. + else + MatchFound=false; // Skip only the current file for non-solid archive. + if (MatchFound || (SkipSolid=Arc.Solid)!=0) { // First common call of uiStartFileExtract. It is done before overwrite @@ -921,19 +941,13 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De #endif #ifndef SFX_MODULE - size_t ArcPathLength=wcslen(Cmd->ArcPath); + wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath; + size_t ArcPathLength=wcslen(ArcPath); if (ArcPathLength>0) { size_t NameLength=wcslen(ArcFileName); - - // Earlier we compared lengths only here, but then noticed a cosmetic bug - // in WinRAR. When extracting a file reference from subfolder with - // "Extract relative paths", so WinRAR sets ArcPath, if reference target - // is missing, error message removed ArcPath both from reference and target - // names. If target was stored in another folder, its name looked wrong. - if (NameLength>=ArcPathLength && - wcsnicompc(Cmd->ArcPath,ArcFileName,ArcPathLength)==0 && - (IsPathDiv(Cmd->ArcPath[ArcPathLength-1]) || + if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 && + (IsPathDiv(ArcPath[ArcPathLength-1]) || IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) { ArcFileName+=Min(ArcPathLength,NameLength); @@ -966,7 +980,7 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De // Must do after Cmd->ArcPath processing above, so file name and arc path // trailing spaces are in sync. if (!Cmd->AllowIncompatNames) - MakeNameCompatible(DestName); + MakeNameCompatible(DestName,DestSize); #endif wchar DiskLetter=toupperw(DestName[0]); @@ -1027,11 +1041,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) { // Suppress "test is ok" message if user cancelled the password prompt. -// 2019.03.23: If some archives are tested ok and prompt is cancelled for others, -// do we really need to suppress "test is ok"? Also if we set an empty password -// and "Use for all archives" in WinRAR Ctrl+P and skip some encrypted archives. -// We commented out this UIERROR_INCERRCOUNT for now. -// uiMsg(UIERROR_INCERRCOUNT); + uiMsg(UIERROR_INCERRCOUNT); return false; } Cmd->ManualPassword=true; diff --git a/unrar/file.cpp b/unrar/file.cpp index 5a8099ec..216bc89a 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -14,11 +14,11 @@ File::File() AllowExceptions=true; PreserveAtime=false; #ifdef _WIN_ALL - NoSequentialRead=false; CreateMode=FMF_UNDEFINED; #endif ReadErrorMode=FREM_ASK; TruncatedAfterReadError=false; + CurFilePos=0; } @@ -58,7 +58,7 @@ bool File::Open(const wchar *Name,uint Mode) uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; if (OpenShared) ShareMode|=FILE_SHARE_WRITE; - uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; + uint Flags=FILE_FLAG_SEQUENTIAL_SCAN; FindData FD; if (PreserveAtime) Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. @@ -379,10 +379,11 @@ int File::Read(void *Data,size_t Size) if (ReadErrorMode==FREM_IGNORE) FilePos=Tell(); - int ReadSize; + int TotalRead=0; while (true) { - ReadSize=DirectRead(Data,Size); + int ReadSize=DirectRead(Data,Size); + if (ReadSize==-1) { ErrorType=FILE_READERROR; @@ -396,6 +397,8 @@ int File::Read(void *Data,size_t Size) size_t SizeToRead=Min(Size-I,512); int ReadCode=DirectRead(Data,SizeToRead); ReadSize+=(ReadCode==-1) ? 512:ReadCode; + if (ReadSize!=-1) + TotalRead+=ReadSize; } } else @@ -415,9 +418,24 @@ int File::Read(void *Data,size_t Size) ErrHandler.ReadError(FileName); } } + TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here. + + if (HandleType==FILE_HANDLESTD && ReadSize>0 && (uint)ReadSize0) // Can be -1 for error and AllowExceptions disabled. + CurFilePos+=TotalRead; + return TotalRead; // It can return -1 only if AllowExceptions is disabled. } @@ -499,6 +517,30 @@ bool File::RawSeek(int64 Offset,int Method) { if (hFile==FILE_BAD_HANDLE) return true; + if (!IsSeekable()) + { + if (Method==SEEK_CUR) + { + Offset+=CurFilePos; + Method=SEEK_SET; + } + if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward. + { + uint64 SkipSize=Offset-CurFilePos; + while (SkipSize>0) + { + byte Buf[4096]; + int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); + if (ReadSize<=0) + return false; + SkipSize-=ReadSize; + } + CurFilePos=Offset; + return true; + } + + return false; // Backward or end of file seek on unseekable file. + } if (Offset<0 && Method!=SEEK_SET) { Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; @@ -533,6 +575,8 @@ int64 File::Tell() ErrHandler.SeekError(FileName); else return -1; + if (!IsSeekable()) + return CurFilePos; #ifdef _WIN_ALL LONG HighDist=0; uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); diff --git a/unrar/file.hpp b/unrar/file.hpp index 1c436d4e..48d68c39 100644 --- a/unrar/file.hpp +++ b/unrar/file.hpp @@ -70,6 +70,8 @@ class File #endif bool PreserveAtime; bool TruncatedAfterReadError; + + int64 CurFilePos; // Used for forward seeks in stdin files. protected: bool OpenShared; // Set by 'Archive' class. public: @@ -111,6 +113,7 @@ class File int64 FileLength(); void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} FILE_HANDLETYPE GetHandleType() {return HandleType;} + bool IsSeekable() {return HandleType!=FILE_HANDLESTD;} bool IsDevice(); static bool RemoveCreated(); FileHandle GetHandle() {return hFile;} @@ -119,9 +122,6 @@ class File int64 Copy(File &Dest,int64 Length=INT64NDF); void SetAllowDelete(bool Allow) {AllowDelete=Allow;} void SetExceptions(bool Allow) {AllowExceptions=Allow;} -#ifdef _WIN_ALL - void RemoveSequentialFlag() {NoSequentialRead=true;} -#endif void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;} bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;} #ifdef _UNIX diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index bab7c738..210156f3 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -517,6 +517,18 @@ bool SetFileCompression(const wchar *Name,bool State) CloseHandle(hFile); return RetCode!=0; } + + +void ResetFileCache(const wchar *Name) +{ + // To reset file cache in Windows it is enough to open it with + // FILE_FLAG_NO_BUFFERING and then close it. + HANDLE hSrc=CreateFile(Name,GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); + if (hSrc!=INVALID_HANDLE_VALUE) + CloseHandle(hSrc); +} #endif diff --git a/unrar/filefn.hpp b/unrar/filefn.hpp index 78735fee..36dc7039 100644 --- a/unrar/filefn.hpp +++ b/unrar/filefn.hpp @@ -41,6 +41,7 @@ bool DelDir(const wchar *Name); #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool SetFileCompression(const wchar *Name,bool State); +void ResetFileCache(const wchar *Name); #endif diff --git a/unrar/find.cpp b/unrar/find.cpp index b22f82d8..04702ab8 100644 --- a/unrar/find.cpp +++ b/unrar/find.cpp @@ -144,9 +144,15 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) fd->Size=st.st_size; #ifdef UNIX_TIME_NS +#if defined(_APPLE) + fd->mtime.SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); + fd->atime.SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); + fd->ctime.SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); +#else fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); +#endif #else fd->mtime.SetUnix(st.st_mtime); fd->atime.SetUnix(st.st_atime); diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index 6fadec04..305058dd 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -13,6 +13,7 @@ DWORD WinNT() dwPlatformId=WinVer.dwPlatformId; dwMajorVersion=WinVer.dwMajorVersion; dwMinorVersion=WinVer.dwMinorVersion; + } DWORD Result=0; if (dwPlatformId==VER_PLATFORM_WIN32_NT) diff --git a/unrar/list.cpp b/unrar/list.cpp index 9b652185..3a99ab0c 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -22,9 +22,6 @@ void ListArchive(CommandData *Cmd) Cmd->Password.Clean(); // Clean user entered password before processing next archive. Archive Arc(Cmd); -#ifdef _WIN_ALL - Arc.RemoveSequentialFlag(); -#endif if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; @@ -310,17 +307,21 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); } + bool WinTitles=false; +#ifdef _WIN_ALL + WinTitles=true; +#endif if (hd.mtime.IsSet()) - mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr); if (hd.ctime.IsSet()) { hd.ctime.GetText(DateStr,ASIZE(DateStr),true); - mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr); } if (hd.atime.IsSet()) { hd.atime.GetText(DateStr,ASIZE(DateStr),true); - mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr); } mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); if (hd.FileHash.Type==HASH_CRC32) diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index a1cd544a..a2488653 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -72,11 +72,11 @@ #define MCHelpSwDW L"\n dw Wipe files after archiving" #define MCHelpSwEa L"\n e[+] Set file exclude and include attributes" #define MCHelpSwED L"\n ed Do not add empty directories" -#define MCHelpSwEN L"\n en Do not put 'end of archive' block" #define MCHelpSwEP L"\n ep Exclude paths from names" #define MCHelpSwEP1 L"\n ep1 Exclude base directory from names" #define MCHelpSwEP2 L"\n ep2 Expand paths to full" #define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter" +#define MCHelpSwEP4 L"\n ep4 Exclude the path prefix from names" #define MCHelpSwF L"\n f Freshen files" #define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" #define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" @@ -95,6 +95,7 @@ #define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" #define MCHelpSwMC L"\n mc Set advanced compression parameters" #define MCHelpSwMD L"\n md[k,m,g] Dictionary size in KB, MB or GB" +#define MCHelpSwME L"\n me[par] Set encryption parameters" #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" @@ -106,11 +107,11 @@ #define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references" #define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]" #define 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" #define MCHelpSwOS L"\n os Save NTFS streams" #define MCHelpSwOW L"\n ow Save or restore file owner and group" #define MCHelpSwP L"\n p[password] Set password" -#define MCHelpSwPm L"\n p- Do not query password" #define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]" #define MCHelpSwR L"\n r Recurse subdirectories" #define MCHelpSwRm L"\n r- Disable recursion" @@ -272,6 +273,9 @@ #define MListMtime L"mtime" #define MListCtime L"ctime" #define MListAtime L"atime" +#define MListModified L"Modified" +#define MListCreated L"Created" +#define MListAccessed L"Accessed" #define MListAttr L"Attributes" #define MListFlags L"Flags" #define MListCompInfo L"Compression" @@ -385,3 +389,7 @@ #define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n" #define MErrReadCount L"\n%u files are archived incompletely because of read errors." #define MDirNameExists L"\nDirectory with such name already exists" +#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin" +#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated." +#define MAdjustValue L"\nAdjusting %s value to %s." +#define MOpFailed L"\nOperation failed" diff --git a/unrar/options.hpp b/unrar/options.hpp index 993b2196..11319582 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -116,9 +116,11 @@ class RAROptions RAR_CHARSET ErrlogCharset; RAR_CHARSET RedirectCharset; - wchar ArcPath[NM]; + wchar ArcPath[NM]; // For -ap. + wchar ExclArcPath[NM]; // For -ep4 switch. SecPassword Password; bool EncryptHeaders; + bool SkipEncrypted; bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. @@ -195,7 +197,11 @@ class RAROptions EXTTIME_MODE xctime; EXTTIME_MODE xatime; bool PreserveAtime; - wchar CompressStdin[NM]; + + // Read data from stdin and store in archive under a name specified here + // when archiving. Read an archive from stdin if any non-empty string + // is specified here when extracting. + wchar UseStdin[NM]; uint Threads; // We use it to init hash even if RAR_SMP is not defined. diff --git a/unrar/os.hpp b/unrar/os.hpp index b69f3487..7df3c6c0 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -36,12 +36,11 @@ // re-definition warnings in third party projects. #ifndef UNICODE #define UNICODE +#define _UNICODE // Set _T() macro to convert from narrow to wide strings. #endif -#undef WINVER -#undef _WIN32_WINNT -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 +#define WINVER _WIN32_WINNT_WINXP +#define _WIN32_WINNT _WIN32_WINNT_WINXP #if !defined(ZIPSFX) #define RAR_SMP @@ -79,8 +78,11 @@ #include #include - #define USE_SSE - #define SSE_ALIGNMENT 16 + // Use SSE only for x86/x64, not ARM Windows. + #if defined(_M_IX86) || defined(_M_X64) + #define USE_SSE + #define SSE_ALIGNMENT 16 + #endif #else #include #endif // _MSC_VER @@ -212,7 +214,8 @@ #endif #endif -#if _POSIX_C_SOURCE >= 200809L +// Unlike Apple x64, utimensat shall be available in all Apple M1 systems. +#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__) #define UNIX_TIME_NS // Nanosecond time precision in Unix. #endif diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 18f70982..3e321285 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -5,7 +5,7 @@ wchar* PointToName(const wchar *Path) for (int I=(int)wcslen(Path)-1;I>=0;I--) if (IsPathDiv(Path[I])) return (wchar*)&Path[I+1]; - return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path); + return (wchar*)((*Path!=0 && IsDriveDiv(Path[1])) ? Path+2:Path); } @@ -319,6 +319,9 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx // of file name if numeric part is missing. wchar* GetVolNumPart(const wchar *ArcName) { + // We do not want to increment any characters in path component. + ArcName=PointToName(ArcName); + if (*ArcName==0) return (wchar *)ArcName; @@ -342,7 +345,7 @@ wchar* GetVolNumPart(const wchar *ArcName) { // Validate the first numeric part only if it has a dot somewhere // before it. - wchar *Dot=wcschr(PointToName(ArcName),'.'); + const wchar *Dot=wcschr(ArcName,'.'); if (Dot!=NULL && Dot0 && CurChar=='M') { // Replace minutes with 'I'. We use 'M' both for months and minutes, - // so we treat as minutes only those 'M' which are found after hours. + // so we treat as minutes only those 'M', which are found after hours. Mask[I]='I'; + MAsMinutes--; } if (CurChar=='N') { @@ -776,7 +783,9 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u const wchar *MaskChars=L"YMDHISWAEN"; + // How many times every modifier character was encountered in the mask. int CField[sizeof(Field)/sizeof(Field[0])]; + memset(CField,0,sizeof(CField)); QuoteMode=false; for (uint I=0;Mask[I]!=0;I++) @@ -818,13 +827,22 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u { size_t FieldPos=ChPtr-MaskChars; int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--; - if (FieldPos==1 && toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M') + + // CField[FieldPos] shall have exactly 3 "MMM" symbols, so we do not + // repeat the month name in case "MMMMMMMM" mask. But since we + // decremented CField[FieldPos] above, we compared it with 2. + if (FieldPos==1 && CField[FieldPos]==2 && + toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M') { wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J); J=wcslen(DateText); I+=2; continue; } + // If CharPos is negative, we have more modifier characters than + // matching time data. We prefer to issue a modifier character + // instead of repeating time data from beginning, so user can notice + // excessive modifiers added by mistake. if (CharPos<0) DateText[J]=Mask[I]; else @@ -987,9 +1005,9 @@ void ConvertToPrecomposed(wchar *Name,size_t NameSize) } -// Remove trailing spaces and dots in file name and in dir names in path. -void MakeNameCompatible(wchar *Name) +void MakeNameCompatible(wchar *Name,size_t MaxSize) { + // Remove trailing spaces and dots in file name and in dir names in path. int Src=0,Dest=0; while (true) { @@ -1007,5 +1025,45 @@ void MakeNameCompatible(wchar *Name) Src++; Dest++; } + + // Rename reserved device names, such as aux.txt to _aux.txt. + // We check them in path components too, where they are also prohibited. + for (uint I=0;Name[I]!=0;I++) + if (I==0 || I>0 && IsPathDiv(Name[I-1])) + { + static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"}; + wchar *s=Name+I; + bool MatchFound=false; + for (uint J=0;JI+1) // I+1, because we do not move the trailing 0. + memmove(s+1,s,(MaxSize-I-1)*sizeof(*s)); + *s='_'; +#ifndef SFX_MODULE + uiMsg(UIMSG_CORRECTINGNAME,nullptr); + uiMsg(UIERROR_RENAMING,nullptr,OrigName,Name); +#endif + } + } } #endif + + diff --git a/unrar/pathfn.hpp b/unrar/pathfn.hpp index 63813d8a..efb28330 100644 --- a/unrar/pathfn.hpp +++ b/unrar/pathfn.hpp @@ -70,7 +70,8 @@ void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask #ifdef _WIN_ALL bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize); void ConvertToPrecomposed(wchar *Name,size_t NameSize); -void MakeNameCompatible(wchar *Name); +void MakeNameCompatible(wchar *Name,size_t MaxSize); #endif + #endif diff --git a/unrar/rar.hpp b/unrar/rar.hpp index 3f7414c8..20e32fb2 100644 --- a/unrar/rar.hpp +++ b/unrar/rar.hpp @@ -16,6 +16,7 @@ #include "errhnd.hpp" #include "secpassword.hpp" #include "array.hpp" +#include "strlist.hpp" #include "timefn.hpp" #include "sha1.hpp" #include "sha256.hpp" @@ -28,7 +29,6 @@ #include "headers.hpp" #include "pathfn.hpp" #include "strfn.hpp" -#include "strlist.hpp" #ifdef _WIN_ALL #include "isnt.hpp" #endif diff --git a/unrar/rdwrfn.cpp b/unrar/rdwrfn.cpp index 66a68fc7..12984fb4 100644 --- a/unrar/rdwrfn.cpp +++ b/unrar/rdwrfn.cpp @@ -148,13 +148,6 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) } -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) -// Disable the run time stack check for unrar.dll, so we can manipulate -// with ProcessDataProc call type below. Run time check would intercept -// a wrong ESP before we restore it. -#pragma runtime_checks( "s", off ) -#endif - void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { @@ -167,28 +160,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) ErrHandler.Exit(RARX_USERBREAK); if (Cmd->ProcessDataProc!=NULL) { - // Here we preserve ESP value. It is necessary for those developers, - // who still define ProcessDataProc callback as "C" type function, - // even though in year 2001 we announced in unrar.dll whatsnew.txt - // that it will be PASCAL type (for compatibility with Visual Basic). -#if defined(_MSC_VER) -#ifndef _WIN_64 - __asm mov ebx,esp -#endif -#elif defined(_WIN_ALL) && defined(__BORLANDC__) - _EBX=_ESP; -#endif int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); - - // Restore ESP after ProcessDataProc with wrongly defined calling - // convention broken it. -#if defined(_MSC_VER) -#ifndef _WIN_64 - __asm mov esp,ebx -#endif -#elif defined(_WIN_ALL) && defined(__BORLANDC__) - _ESP=_EBX; -#endif if (RetCode==0) ErrHandler.Exit(RARX_USERBREAK); } @@ -216,11 +188,6 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) Wait(); } -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) -// Restore the run time stack check for unrar.dll. -#pragma runtime_checks( "s", restore ) -#endif - diff --git a/unrar/recvol5.cpp b/unrar/recvol5.cpp index 3c524d8e..596ea06f 100644 --- a/unrar/recvol5.cpp +++ b/unrar/recvol5.cpp @@ -1,5 +1,9 @@ static const uint MaxVolumes=65535; +// We select this limit arbitrarily, to prevent user creating too many +// rev files by mistake. +#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. + RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) { RealBuf=NULL; @@ -145,13 +149,16 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) wchar *Num=GetVolNumPart(ArcName); while (Num>ArcName && IsDigit(*(Num-1))) Num--; - if (Num==ArcName) + if (Num<=PointToName(ArcName)) return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); wchar FirstVolName[NM]; *FirstVolName=0; + wchar LongestRevName[NM]; + *LongestRevName=0; + int64 RecFileSize=0; FindFile VolFind; @@ -164,7 +171,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) Archive *Vol=new Archive(Cmd); int ItemPos=-1; - if (Vol->WOpen(fd.Name)) + if (!fd.IsDir && Vol->WOpen(fd.Name)) { if (CmpExt(fd.Name,L"rev")) { @@ -176,6 +183,9 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) ItemPos=RecNum; FoundRecVolumes++; + + if (wcslen(fd.Name)>wcslen(LongestRevName)) + wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName)); } } else @@ -231,6 +241,15 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) if (FoundRecVolumes==0) return false; + // If we did not find even a single .rar volume, create .rar volume name + // based on the longest .rev file name. Use longest .rev, so we have + // enough space for volume number. + if (*FirstVolName==0) + { + SetExt(LongestRevName,L"rar",ASIZE(LongestRevName)); + VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true); + } + uiMsg(UIMSG_RECVOLCALCCHECKSUM); MissingVolumes=0; @@ -301,7 +320,7 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) Item->f=NULL; } - if ((Item->New=(Item->f==NULL))) // Additional parentheses to avoid GCC warning. + if ((Item->New=(Item->f==NULL))==true) { wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); uiMsg(UIMSG_CREATING,Item->Name); @@ -316,7 +335,6 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) } NewVol->Prealloc(Item->FileSize); Item->f=NewVol; - Item->New=true; } NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); } @@ -346,13 +364,11 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE. #endif - uint *Data=new uint[TotalCount]; - RSCoder16 RS; if (!RS.Init(DataCount,RecCount,ValidFlags)) { + uiMsg(UIERROR_OPFAILED); delete[] ValidFlags; - delete[] Data; return false; // Should not happen, we check parameter validity above. } @@ -415,7 +431,6 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) RecItems[I].f->Close(); delete[] ValidFlags; - delete[] Data; #if !defined(SILENT) if (!Cmd->DisablePercentage) mprintf(L"\b\b\b\b100%%"); diff --git a/unrar/rijndael.cpp b/unrar/rijndael.cpp index dd19750a..48ac1723 100644 --- a/unrar/rijndael.cpp +++ b/unrar/rijndael.cpp @@ -1,22 +1,41 @@ -/*************************************************************************** - * This code is based on public domain Szymon Stefanek AES implementation: * - * http://www.pragmaware.net/software/rijndael/index.php * - * * - * Dynamic tables generation is based on the Brian Gladman work: * - * http://fp.gladman.plus.com/cryptography_technology/rijndael * - ***************************************************************************/ +/************************************************************************** + * This code is based on Szymon Stefanek public domain AES implementation * + **************************************************************************/ #include "rar.hpp" #ifdef USE_SSE #include #endif -static byte S[256],S5[256],rcon[30]; +static byte S[256]= +{ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 +}; + +static byte S5[256]; + +// Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256. +static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36}; + static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4]; static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4]; static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4]; - inline void Xor128(void *dest,const void *arg1,const void *arg2) { #ifdef ALLOW_MISALIGNED @@ -63,7 +82,7 @@ inline void Copy128(byte *dest,const byte *src) Rijndael::Rijndael() { - if (S[0]==0) + if (S5[0]==0) GenerateTables(); CBCMode = true; // Always true for RAR. } @@ -82,7 +101,7 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe AES_NI=(CPUInfo[2] & 0x2000000)!=0; } else - AES_NI=0; + AES_NI=false; #endif // Other developers asked us to initialize it to suppress "may be used @@ -416,51 +435,40 @@ void Rijndael::keyEncToDec() } -#define ff_poly 0x011b -#define ff_hi 0x80 - -#define FFinv(x) ((x) ? pow[255 - log[x]]: 0) - -#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) -#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) -#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) -#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) -#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) -#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) -#define fwd_affine(x) \ - (w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8)))) +static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication. +{ + const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial. + byte result = 0; + while (b>0) + { + if ((b & 1) != 0) + result ^= a; + a = (a & 0x80) ? (a<<1)^poly : a<<1; + b >>= 1; + } + return result; +} -#define inv_affine(x) \ - (w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8)))) +// 2021-09-24: changed to slower and simpler code without interim tables. +// It is still fast enough for our purpose. void Rijndael::GenerateTables() { - unsigned char pow[512],log[256]; - int i = 0, w = 1; - do - { - pow[i] = (byte)w; - pow[i + 255] = (byte)w; - log[w] = (byte)i++; - w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); - } while (w != 1); - - for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++) - { - rcon[i] = w; - w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); - } - for(int i = 0; i < 256; ++i) + for (int I=0;I<256;I++) + S5[S[I]]=I; + + for (int I=0;I<256;I++) { - unsigned char b=S[i]=fwd_affine(FFinv((byte)i)); - T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b; - T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b); - T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b); - S5[i] = b = FFinv(inv_affine((byte)i)); - U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b); - U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b); - U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b); - U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b); + byte s=S[I]; + T1[I][1]=T1[I][2]=T2[I][2]=T2[I][3]=T3[I][0]=T3[I][3]=T4[I][0]=T4[I][1]=s; + T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2); + T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3); + + byte b=S5[I]; + U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[I][3]=T6[I][0]=T7[I][1]=T8[I][2]=gmul(b,0xb); + U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9); + U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd); + U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe); } } diff --git a/unrar/rijndael.hpp b/unrar/rijndael.hpp index 2144e02e..797899ff 100644 --- a/unrar/rijndael.hpp +++ b/unrar/rijndael.hpp @@ -2,11 +2,7 @@ #define _RIJNDAEL_H_ /************************************************************************** - * This code is based on Szymon Stefanek AES implementation: * - * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz * - * * - * Dynamic tables generation is based on the Brian Gladman's work: * - * http://fp.gladman.plus.com/cryptography_technology/rijndael * + * This code is based on Szymon Stefanek public domain AES implementation * **************************************************************************/ #define _MAX_KEY_COLUMNS (256/32) diff --git a/unrar/rs16.cpp b/unrar/rs16.cpp index bc8dd093..f5c7ccac 100644 --- a/unrar/rs16.cpp +++ b/unrar/rs16.cpp @@ -95,7 +95,9 @@ bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags) if (NE > ValidECC || NE == 0 || ValidECC == 0) return false; } - if (ND + NR > gfSize || NR > ND || ND == 0 || NR == 0) + + // 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check. + if (ND + NR > gfSize || /*NR > ND ||*/ ND == 0 || NR == 0) return false; delete[] MX; diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index 8904b907..f53c826a 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -121,48 +121,26 @@ wchar* RemoveLF(wchar *Str) } -unsigned char loctolower(unsigned char ch) -{ -#if defined(_WIN_ALL) - // Convert to LPARAM first to avoid a warning in 64 bit mode. - // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] - return (int)(LPARAM)CharLowerA((LPSTR)(uintptr_t)ch); -#else - return tolower(ch); -#endif -} - - -unsigned char loctoupper(unsigned char ch) -{ -#if defined(_WIN_ALL) - // Convert to LPARAM first to avoid a warning in 64 bit mode. - // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] - return (int)(LPARAM)CharUpperA((LPSTR)(uintptr_t)ch); -#else - return toupper(ch); -#endif -} - - -// toupper with English only results if English input is provided. -// It avoids Turkish (small i) -> (big I with dot) conversion problem. -// We do not define 'ch' as 'int' to avoid necessity to cast all +#if defined(SFX_MODULE) +// char version of etoupperw. Used in console SFX module only. +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all // signed chars passed to this function to unsigned char. -unsigned char etoupper(unsigned char ch) +unsigned char etoupper(unsigned char c) { - if (ch=='i') - return 'I'; - return toupper(ch); + return c>='a' && c<='z' ? c-'a'+'A' : c; } +#endif -// Unicode version of etoupper. -wchar etoupperw(wchar ch) +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all +// signed wchars passed to this function to unsigned char. +wchar etoupperw(wchar c) { - if (ch=='i') - return 'I'; - return toupperw(ch); + return c>='a' && c<='z' ? c-'a'+'A' : c; } diff --git a/unrar/strfn.hpp b/unrar/strfn.hpp index 2984f8c3..32470310 100644 --- a/unrar/strfn.hpp +++ b/unrar/strfn.hpp @@ -13,16 +13,16 @@ int stricomp(const char *s1,const char *s2); int strnicomp(const char *s1,const char *s2,size_t n); wchar* RemoveEOL(wchar *Str); wchar* RemoveLF(wchar *Str); -unsigned char loctolower(unsigned char ch); -unsigned char loctoupper(unsigned char ch); void strncpyz(char *dest, const char *src, size_t maxlen); void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen); void strncatz(char* dest, const char* src, size_t maxlen); void wcsncatz(wchar* dest, const wchar* src, size_t maxlen); -unsigned char etoupper(unsigned char ch); -wchar etoupperw(wchar ch); +#if defined(SFX_MODULE) +unsigned char etoupper(unsigned char c); +#endif +wchar etoupperw(wchar c); bool IsDigit(int ch); bool IsSpace(int ch); diff --git a/unrar/timefn.cpp b/unrar/timefn.cpp index e86d41a1..0abf49de 100644 --- a/unrar/timefn.cpp +++ b/unrar/timefn.cpp @@ -273,12 +273,12 @@ void RarTime::SetAgeText(const wchar *TimeText) uint Seconds=0,Value=0; for (uint I=0;TimeText[I]!=0;I++) { - int Ch=TimeText[I]; + wchar Ch=TimeText[I]; if (IsDigit(Ch)) Value=Value*10+Ch-'0'; else { - switch(etoupper(Ch)) + switch(etoupperw(Ch)) { case 'D': Seconds+=Value*24*3600; diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 2654387c..1641a4c5 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -39,7 +39,7 @@ enum UIMESSAGE_CODE { UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, - UIERROR_DIRNAMEEXISTS, + UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW,UIERROR_ADJUSTVALUE, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index ceae1a7c..4e2cf88c 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -247,6 +247,9 @@ void uiMsgStore::Msg() mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile. break; #ifndef SFX_MODULE + case UIERROR_OPFAILED: + Log(NULL,St(MOpFailed)); + break; case UIERROR_NEWRARFORMAT: Log(Str[0],St(MNewRarFormat)); break; @@ -329,6 +332,12 @@ void uiMsgStore::Msg() case UIERROR_DIRNAMEEXISTS: Log(NULL,St(MDirNameExists)); break; + case UIERROR_TRUNCPSW: + eprintf(St(MTruncPsw),Num[0]); + break; + case UIERROR_ADJUSTVALUE: + Log(NULL,St(MAdjustValue),Str[0],Str[1]); + break; #ifndef SFX_MODULE case UIMSG_STRING: diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index 641f6c89..5421923f 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -601,59 +601,6 @@ char* SupportDBCS::charnext(const char *s) // to break string processing loops. return (char *)(IsLeadByte[(byte)*s] && s[1]!=0 ? s+2:s+1); } - - -size_t SupportDBCS::strlend(const char *s) -{ - size_t Length=0; - while (*s!=0) - { - if (IsLeadByte[(byte)*s]) - s+=2; - else - s++; - Length++; - } - return(Length); -} - - -char* SupportDBCS::strchrd(const char *s, int c) -{ - while (*s!=0) - if (IsLeadByte[(byte)*s]) - s+=2; - else - if (*s==c) - return((char *)s); - else - s++; - return(NULL); -} - - -void SupportDBCS::copychrd(char *dest,const char *src) -{ - dest[0]=src[0]; - if (IsLeadByte[(byte)src[0]]) - dest[1]=src[1]; -} - - -char* SupportDBCS::strrchrd(const char *s, int c) -{ - const char *found=NULL; - while (*s!=0) - if (IsLeadByte[(byte)*s]) - s+=2; - else - { - if (*s==c) - found=s; - s++; - } - return((char *)found); -} #endif diff --git a/unrar/unicode.hpp b/unrar/unicode.hpp index 031ac09a..8d433c1b 100644 --- a/unrar/unicode.hpp +++ b/unrar/unicode.hpp @@ -33,34 +33,19 @@ class SupportDBCS public: SupportDBCS(); void Init(); - char* charnext(const char *s); - size_t strlend(const char *s); - char *strchrd(const char *s, int c); - char *strrchrd(const char *s, int c); - void copychrd(char *dest,const char *src); bool IsLeadByte[256]; bool DBCSMode; }; - 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();} +inline bool IsDBCSMode() {return gdbcs.DBCSMode;} #else #define charnext(s) ((s)+1) -#define strlend strlen -#define strchrd strchr -#define strrchrd strrchr -#define IsDBCSMode() (true) -inline void copychrd(char *dest,const char *src) {*dest=*src;} +#define IsDBCSMode() (false) #endif diff --git a/unrar/version.hpp b/unrar/version.hpp index cf1c77cb..5fa16024 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 2 -#define RARVER_BETA 0 -#define RARVER_DAY 11 -#define RARVER_MONTH 6 +#define RARVER_MINOR 10 +#define RARVER_BETA 1 +#define RARVER_DAY 7 +#define RARVER_MONTH 10 #define RARVER_YEAR 2021 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index 001a9673..1a5f1d0b 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -40,14 +40,19 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif - bool FailedOpen=false,OldSchemeTested=false; + bool OldSchemeTested=false; + bool FailedOpen=false; // No more next volume open attempts if true. #if !defined(SILENT) - // In -vp mode we force the pause before next volume even if it is present - // and even if we are on the hard disk. It is important when user does not - // want to process partially downloaded volumes preliminary. - if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) - FailedOpen=true; + if (Cmd->VolumePause) + { + // If next volume can't be opened exclusively, it might be still + // downloading, so in -vp mode user may prefer to pause until completion + // even if volume is exist. FMF_OPENEXCLUSIVE works in Windows only. + File TestOpen; + if (!TestOpen.Open(NextName,FMF_OPENEXCLUSIVE) && !uiAskNextVolume(NextName,ASIZE(NextName))) + FailedOpen=true; + } #endif uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; @@ -171,13 +176,6 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma #ifdef RARDLL -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) -// Disable the run time stack check for unrar.dll, so we can manipulate -// with ChangeVolProc call type below. Run time check would intercept -// a wrong ESP before we restore it. -#pragma runtime_checks( "s", off ) -#endif - bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) { bool DllVolChanged=false,DllVolAborted=false; @@ -212,28 +210,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) { char NextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); - // Here we preserve ESP value. It is necessary for those developers, - // who still define ChangeVolProc callback as "C" type function, - // even though in year 2001 we announced in unrar.dll whatsnew.txt - // that it will be PASCAL type (for compatibility with Visual Basic). -#if defined(_MSC_VER) -#ifndef _WIN_64 - __asm mov ebx,esp -#endif -#elif defined(_WIN_ALL) && defined(__BORLANDC__) - _EBX=_ESP; -#endif int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); - - // Restore ESP after ChangeVolProc with wrongly defined calling - // convention broken it. -#if defined(_MSC_VER) -#ifndef _WIN_64 - __asm mov esp,ebx -#endif -#elif defined(_WIN_ALL) && defined(__BORLANDC__) - _ESP=_EBX; -#endif if (RetCode==0) DllVolAborted=true; else @@ -268,21 +245,10 @@ bool DllVolNotify(RAROptions *Cmd,wchar *NextName) } if (Cmd->ChangeVolProc!=NULL) { -#if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) - _EBX=_ESP; -#endif int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); -#if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) - _ESP=_EBX; -#endif if (RetCode==0) return false; } return true; } - -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) -// Restore the run time stack check for unrar.dll. -#pragma runtime_checks( "s", restore ) -#endif #endif From d7a5b2c8f06a9fb22a4839b4741390b4536129f9 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:17:58 +0000 Subject: [PATCH 07/76] Added unrar 6.1.2 --- unrar/arcread.cpp | 11 ++++++++++- unrar/dll.rc | 8 ++++---- unrar/filefn.cpp | 6 +++++- unrar/global.cpp | 2 +- unrar/loclang.hpp | 1 + unrar/ui.hpp | 1 + unrar/uiconsole.cpp | 9 ++++++++- unrar/version.hpp | 6 +++--- 8 files changed, 33 insertions(+), 11 deletions(-) diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 4e9f5064..1a401f48 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -559,7 +559,11 @@ size_t Archive::ReadHeader50() #else if (Cmd->SkipEncrypted) + { + uiMsg(UIMSG_SKIPENCARC,FileName); + FailedHeaderDecryption=true; // Suppress error messages and quit quietly. return 0; + } byte HeadersInitV[SIZE_INITV]; if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) @@ -879,7 +883,12 @@ size_t Archive::ReadHeader50() // code to shell extension, which is not done now. if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) { - RecoveryPercent=hd->SubData[0]; + // It is stored as a single byte up to RAR 6.02 and as vint since + // 6.10, where we extended the maximum RR size from 99% to 1000%. + RawRead RawPercent; + RawPercent.Read(&hd->SubData[0],hd->SubData.Size()); + RecoveryPercent=(int)RawPercent.GetV(); + RSBlockHeader Header; GetRRInfo(this,&Header); RecoverySize=Header.RecSectionSize*Header.RecCount; diff --git a/unrar/dll.rc b/unrar/dll.rc index 5cec308d..dd596f21 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 10, 1, 3939 -PRODUCTVERSION 6, 10, 1, 3939 +FILEVERSION 6, 10, 2, 318 +PRODUCTVERSION 6, 10, 2, 318 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", "6.10.1\0" - VALUE "ProductVersion", "6.10.1\0" + VALUE "FileVersion", "6.10.2\0" + VALUE "ProductVersion", "6.10.2\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index 210156f3..883c26e2 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -397,7 +397,11 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, { #ifndef SILENT if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) - uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength); + { + // Update only the current file progress in WinRAR, set the total to 0 + // to keep it as is. It looks better for WinRAR, + uiExtractProgress(TotalRead,FileLength,0,0); + } else { if ((Flags & CALCFSUM_SHOWPERCENT)!=0) diff --git a/unrar/global.cpp b/unrar/global.cpp index 3975813a..771f0001 100644 --- a/unrar/global.cpp +++ b/unrar/global.cpp @@ -1,6 +1,6 @@ #define INCLUDEGLOBAL -#if defined(__BORLANDC__) || defined(_MSC_VER) +#ifdef _MSC_VER #pragma hdrstop #endif diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index a2488653..ad7d0c92 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -393,3 +393,4 @@ #define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated." #define MAdjustValue L"\nAdjusting %s value to %s." #define MOpFailed L"\nOperation failed" +#define MSkipEncArc L"\nSkipping the encrypted archive %s" diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 1641a4c5..8fc76aa8 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -49,6 +49,7 @@ enum UIMESSAGE_CODE { UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, + UIMSG_SKIPENCARC, UIWAIT_FIRST, UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index 4e2cf88c..d713fac0 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -71,7 +71,10 @@ bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) { - int CurPercent=ToPercent(CurSize,TotalSize); + // We set the total size to 0 to update only the current progress and keep + // the total progress intact in WinRAR. Unlike WinRAR, console RAR has only + // the total progress and updates it with current values in such case. + int CurPercent=TotalSize!=0 ? ToPercent(CurSize,TotalSize) : ToPercent(CurFileSize,TotalFileSize); mprintf(L"\b\b\b\b%3d%%",CurPercent); } @@ -334,6 +337,7 @@ void uiMsgStore::Msg() break; case UIERROR_TRUNCPSW: eprintf(St(MTruncPsw),Num[0]); + eprintf(L"\n"); break; case UIERROR_ADJUSTVALUE: Log(NULL,St(MAdjustValue),Str[0],Str[1]); @@ -378,6 +382,9 @@ void uiMsgStore::Msg() mprintf(St(MFAT32Size)); mprintf(L" "); // For progress percent. break; + case UIMSG_SKIPENCARC: + Log(NULL,St(MSkipEncArc),Str[0]); + break; diff --git a/unrar/version.hpp b/unrar/version.hpp index 5fa16024..0b62c1f2 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 10 -#define RARVER_BETA 1 -#define RARVER_DAY 7 -#define RARVER_MONTH 10 +#define RARVER_BETA 2 +#define RARVER_DAY 15 +#define RARVER_MONTH 11 #define RARVER_YEAR 2021 From 63a7b8bbbb9bcaaa104077b1ac80a16dcf324c8f Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:17:59 +0000 Subject: [PATCH 08/76] Added unrar 6.1.3 --- unrar/dll.rc | 8 +++---- unrar/extract.cpp | 20 +++++++--------- unrar/list.cpp | 15 ++++++------ unrar/rdwrfn.cpp | 58 +++++++++++++++++++++++++++++++++++++---------- unrar/rdwrfn.hpp | 9 ++++++-- unrar/unpack.hpp | 4 ++-- unrar/version.hpp | 6 ++--- unrar/volume.cpp | 21 +++++++++++++---- 8 files changed, 94 insertions(+), 47 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index dd596f21..e5458e2c 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 10, 2, 318 -PRODUCTVERSION 6, 10, 2, 318 +FILEVERSION 6, 10, 3, 343 +PRODUCTVERSION 6, 10, 3, 343 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", "6.10.2\0" - VALUE "ProductVersion", "6.10.2\0" + VALUE "FileVersion", "6.10.3\0" + VALUE "ProductVersion", "6.10.3\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 61fc5827..ad047998 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -49,8 +49,7 @@ void CmdExtract::DoExtract() if (Code!=EXTRACT_ARC_REPEAT) break; } - if (FindFile::FastFind(ArcName,&FD)) - DataIO.ProcessedArcSize+=FD.Size; + DataIO.ProcessedArcSize+=DataIO.LastArcSize; } // Clean user entered password. Not really required, just for extra safety. @@ -82,7 +81,7 @@ void CmdExtract::DoExtract() void CmdExtract::ExtractArchiveInit(Archive &Arc) { - DataIO.UnpArcSize=Arc.IsSeekable() ? Arc.FileLength() : 0; + DataIO.AdjustTotalArcSize(&Arc); FileCount=0; MatchedArgs=0; @@ -230,14 +229,11 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() if (Repeat) { // If we started extraction from not first volume and need to - // restart it from first, we must correct DataIO.TotalArcSize - // for correct total progress display. We subtract the size - // of current volume and all volumes after it and add the size - // of new (first) volume. - FindData OldArc,NewArc; - if (FindFile::FastFind(Arc.FileName,&OldArc) && - FindFile::FastFind(ArcName,&NewArc)) - DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; + // restart it from first, we must set DataIO.TotalArcSize to size + // of new first volume to display the total progress correctly. + FindData NewArc; + if (FindFile::FastFind(ArcName,&NewArc)) + DataIO.TotalArcSize=NewArc.Size; return EXTRACT_ARC_REPEAT; } else @@ -652,7 +648,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) uint64 Preallocated=0; if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && - Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && + Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() && (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) { CurFile.Prealloc(Arc.FileHead.UnpSize); diff --git a/unrar/list.cpp b/unrar/list.cpp index 3a99ab0c..e5052c01 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -377,15 +377,16 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo { mprintf(L"\n%12ls: ",L"Unix owner"); if (*hd.UnixOwnerName!=0) - mprintf(L"%ls:",GetWide(hd.UnixOwnerName)); + mprintf(L"%ls",GetWide(hd.UnixOwnerName)); + else + if (hd.UnixOwnerNumeric) + mprintf(L"#%d",hd.UnixOwnerID); + mprintf(L":"); if (*hd.UnixGroupName!=0) mprintf(L"%ls",GetWide(hd.UnixGroupName)); - if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric)) - mprintf(L" "); - if (hd.UnixOwnerNumeric) - mprintf(L"#%d:",hd.UnixOwnerID); - if (hd.UnixGroupNumeric) - mprintf(L"#%d:",hd.UnixGroupID); + else + if (hd.UnixGroupNumeric) + mprintf(L"#%d",hd.UnixGroupID); } mprintf(L"\n"); diff --git a/unrar/rdwrfn.cpp b/unrar/rdwrfn.cpp index 12984fb4..5e13ae65 100644 --- a/unrar/rdwrfn.cpp +++ b/unrar/rdwrfn.cpp @@ -16,6 +16,7 @@ void ComprDataIO::Init() UnpackFromMemory=false; UnpackToMemory=false; UnpPackedSize=0; + UnpPackedLeft=0; ShowProgress=true; TestMode=false; SkipUnpCRC=false; @@ -35,7 +36,9 @@ void ComprDataIO::Init() SubHead=NULL; SubHeadPos=NULL; CurrentCommand=0; - ProcessedArcSize=TotalArcSize=0; + ProcessedArcSize=0; + LastArcSize=0; + TotalArcSize=0; } @@ -75,10 +78,10 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) } else { - size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count; + size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count; if (SizeToRead > 0) { - if (UnpVolume && Decryption && (int64)Count>UnpPackedSize) + if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft) { // We need aligned blocks for decryption and we want "Keep broken // files" to work efficiently with missing encrypted volumes. @@ -109,7 +112,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) ReadAddr+=ReadSize; Count-=ReadSize; #endif - UnpPackedSize-=ReadSize; + UnpPackedLeft-=ReadSize; // Do not ask for next volume if we read something from current volume. // If next volume is missing, we need to process all data from current @@ -118,7 +121,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) // we ask for next volume also if we have non-aligned encryption block. // Since we adjust data size for decryption earlier above, // it does not hurt "Keep broken files" mode efficiency. - if (UnpVolume && UnpPackedSize == 0 && + if (UnpVolume && UnpPackedLeft == 0 && (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) ) { #ifndef NOVOLUME @@ -134,7 +137,7 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) } Archive *SrcArc=(Archive *)SrcFile; if (SrcArc!=NULL) - ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize); + ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize); if (ReadSize!=-1) { ReadSize=TotalRead; @@ -197,12 +200,8 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) { if (ShowProgress && SrcFile!=NULL) { - if (TotalArcSize!=0) - { - // important when processing several archives or multivolume archive - ArcSize=TotalArcSize; - ArcPos+=ProcessedArcSize; - } + // Important when processing several archives or multivolume archive. + ArcPos+=ProcessedArcSize; Archive *SrcArc=(Archive *)SrcFile; RAROptions *Cmd=SrcArc->GetRAROptions(); @@ -286,3 +285,38 @@ void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size) UnpackToMemoryAddr=Addr; UnpackToMemorySize=Size; } + + +// Extraction progress is based on the position in archive and we adjust +// the total archives size here, so trailing blocks do not prevent progress +// reaching 100% at the end of extraction. Alternatively we could print "100%" +// after completing the entire archive extraction, but then we would need +// to take into account possible messages like the checksum error after +// last file percent progress. +void ComprDataIO::AdjustTotalArcSize(Archive *Arc) +{ + // If we know a position of QO or RR blocks, use them to adjust the total + // packed size to beginning of these blocks. Earlier we already calculated + // the total size based on entire archive sizes. We also set LastArcSize + // to start of first trailing block, to add it later to ProcessedArcSize. + int64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; + if (Arc->MainHead.QOpenOffset!=0) // QO is always preceding RR record. + LastArcSize=Arc->MainHead.QOpenOffset; + else + if (Arc->MainHead.RROffset!=0) + LastArcSize=Arc->MainHead.RROffset; + else + { + // If neither QO nor RR are found, exclude the approximate size of + // end of archive block. + // We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC, + // but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file + // name, so we do not have two files with 100% at the end of archive. + const uint EndBlock=23; + + if (ArcLength>EndBlock) + LastArcSize=ArcLength-EndBlock; + } + + TotalArcSize-=ArcLength-LastArcSize; +} diff --git a/unrar/rdwrfn.hpp b/unrar/rdwrfn.hpp index fc38fd30..3060a0ff 100644 --- a/unrar/rdwrfn.hpp +++ b/unrar/rdwrfn.hpp @@ -1,6 +1,7 @@ #ifndef _RAR_DATAIO_ #define _RAR_DATAIO_ +class Archive; class CmdAdd; class Unpack; class ArcFileSearch; @@ -29,6 +30,7 @@ class ComprDataIO byte *UnpWrAddr; int64 UnpPackedSize; + int64 UnpPackedLeft; bool ShowProgress; bool TestMode; @@ -61,7 +63,7 @@ class ComprDataIO void UnpWrite(byte *Addr,size_t Count); void EnableShowProgress(bool Show) {ShowProgress=Show;} void GetUnpackedData(byte **Data,size_t *Size); - void SetPackedSizeToRead(int64 Size) {UnpPackedSize=Size;} + void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;} void SetTestMode(bool Mode) {TestMode=Mode;} void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;} void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;} @@ -74,12 +76,12 @@ class ComprDataIO void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;} + void AdjustTotalArcSize(Archive *Arc); bool PackVolume; bool UnpVolume; bool NextVolumeMissing; - int64 UnpArcSize; int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; @@ -87,6 +89,9 @@ class ComprDataIO // Used to calculate the total operation progress. int64 ProcessedArcSize; + // Last extracted archive size up to QO or RR block. + int64 LastArcSize; + int64 TotalArcSize; DataHash PackedDataHash; // Packed write and unpack read hash. diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index 75dadb0e..30a9a2ee 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -23,8 +23,8 @@ // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. #define MAX_FILTER_BLOCK_SIZE 0x400000 -// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, -// so we keep a number of buffered filters in unpacker reasonable. +// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_READ, +// so we keep the number of buffered filters in unpacker reasonable. #define UNPACK_MAX_WRITE 0x400000 // Decode compressed bit fields to alphabet numbers. diff --git a/unrar/version.hpp b/unrar/version.hpp index 0b62c1f2..2be9af0b 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 10 -#define RARVER_BETA 2 -#define RARVER_DAY 15 -#define RARVER_MONTH 11 +#define RARVER_BETA 3 +#define RARVER_DAY 10 +#define RARVER_MONTH 12 #define RARVER_YEAR 2021 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index 1a5f1d0b..e0edf4d0 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -25,10 +25,12 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName); } + bool PrevVolEncrypted=Arc.Encrypted; + int64 PosBeforeClose=Arc.Tell(); if (DataIO!=NULL) - DataIO->ProcessedArcSize+=Arc.FileLength(); + DataIO->ProcessedArcSize+=DataIO->LastArcSize; Arc.Close(); @@ -132,6 +134,16 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma return false; #endif + if (Arc.Encrypted!=PrevVolEncrypted) + { + // There is no legitimate reason for encrypted header state to be + // changed in the middle of volume sequence. So we abort here to prevent + // 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); + } + if (SplitHeader) Arc.SearchBlock(HeaderType); else @@ -156,10 +168,9 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma DataIO->UnpVolume=hd->SplitAfter; DataIO->SetPackedSizeToRead(hd->PackSize); } -#ifdef SFX_MODULE - DataIO->UnpArcSize=Arc.FileLength(); -#endif - + + DataIO->AdjustTotalArcSize(&Arc); + // Reset the size of packed data read from current volume. It is used // to display the total progress and preceding volumes are already // compensated with ProcessedArcSize, so we need to reset this variable. From efddae46a956c0337c617196cc24f3fac8009add Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:17:59 +0000 Subject: [PATCH 09/76] Added unrar 6.1.4 --- unrar/consio.cpp | 1 + unrar/dll.rc | 10 +++++----- unrar/file.cpp | 7 ++++++- unrar/file.hpp | 11 +++++++++++ unrar/version.hpp | 8 ++++---- unrar/volume.cpp | 21 ++++++++++++--------- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/unrar/consio.cpp b/unrar/consio.cpp index f30f4d02..829442e9 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -267,6 +267,7 @@ bool getwstr(wchar *str,size_t n) Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. File SrcFile; SrcFile.SetHandleType(FILE_HANDLESTD); + SrcFile.SetLineInputMode(true); int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); if (ReadSize<=0) { diff --git a/unrar/dll.rc b/unrar/dll.rc index e5458e2c..6d6b8288 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 10, 3, 343 -PRODUCTVERSION 6, 10, 3, 343 +FILEVERSION 6, 10, 100, 389 +PRODUCTVERSION 6, 10, 100, 389 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "6.10.3\0" - VALUE "ProductVersion", "6.10.3\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2021\0" + VALUE "FileVersion", "6.10.0\0" + VALUE "ProductVersion", "6.10.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/file.cpp b/unrar/file.cpp index 216bc89a..e7b584da 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -7,6 +7,7 @@ File::File() NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; + LineInput=false; SkipClose=false; ErrorType=FILE_SUCCESS; OpenShared=false; @@ -420,13 +421,17 @@ int File::Read(void *Data,size_t Size) } TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here. - if (HandleType==FILE_HANDLESTD && ReadSize>0 && (uint)ReadSize0 && (uint)ReadSizeVolumePause) - { - // If next volume can't be opened exclusively, it might be still - // downloading, so in -vp mode user may prefer to pause until completion - // even if volume is exist. FMF_OPENEXCLUSIVE works in Windows only. - File TestOpen; - if (!TestOpen.Open(NextName,FMF_OPENEXCLUSIVE) && !uiAskNextVolume(NextName,ASIZE(NextName))) - FailedOpen=true; - } + // In -vp mode we force the pause before next volume even if it is present + // and even if we are on the hard disk. It is important when user does not + // want to process partially downloaded volumes preliminary. + // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause + // if we could open the next volume with FMF_OPENEXCLUSIVE. But another + // developer asked us to return the previous behavior and always prompt + // for confirmation. They want to control when unrar continues, because + // the next file might not be fully decoded yet. They write chunks of data + // and then close the file again until the next chunk comes in. + + if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) + FailedOpen=true; #endif uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; From 3fcd7f12fe1caf1d5bffc71cdc01053c146ec9f8 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:00 +0000 Subject: [PATCH 10/76] Added unrar 6.2.1 --- unrar/UnRARDll.vcxproj | 12 +- unrar/archive.cpp | 12 +- unrar/archive.hpp | 10 +- unrar/arcread.cpp | 109 ++++++----- unrar/array.hpp | 34 +--- unrar/blake2s.hpp | 9 +- unrar/cmddata.cpp | 24 ++- unrar/cmddata.hpp | 6 +- unrar/cmdmix.cpp | 7 + unrar/compress.hpp | 1 + unrar/crc.cpp | 161 +++++++++++++++++ unrar/crc.hpp | 4 + unrar/crypt.cpp | 12 +- unrar/crypt.hpp | 77 +++++++- unrar/crypt3.cpp | 5 +- unrar/crypt5.cpp | 12 +- unrar/dll.rc | 8 +- unrar/extract.cpp | 397 +++++++++++++++++++++++++++++++---------- unrar/extract.hpp | 25 ++- unrar/filcreat.cpp | 2 +- unrar/filcreat.hpp | 2 +- unrar/file.cpp | 67 +++++-- unrar/file.hpp | 10 +- unrar/filefn.cpp | 4 +- unrar/filefn.hpp | 2 - unrar/find.cpp | 18 +- unrar/getbits.cpp | 2 +- unrar/getbits.hpp | 14 +- unrar/hash.cpp | 2 +- unrar/hash.hpp | 10 +- unrar/headers.cpp | 10 +- unrar/headers.hpp | 16 +- unrar/headers5.hpp | 7 + unrar/isnt.cpp | 2 +- unrar/list.cpp | 11 ++ unrar/loclang.hpp | 5 + unrar/makefile | 2 +- unrar/options.cpp | 8 - unrar/options.hpp | 12 +- unrar/os.hpp | 13 +- unrar/qopen.cpp | 2 +- unrar/rar.hpp | 4 +- unrar/rardefs.hpp | 10 +- unrar/rawint.hpp | 6 +- unrar/rdwrfn.cpp | 4 +- unrar/recvol.cpp | 4 +- unrar/recvol.hpp | 22 +-- unrar/recvol3.cpp | 6 +- unrar/recvol5.cpp | 8 +- unrar/rijndael.cpp | 98 +++++++++- unrar/rijndael.hpp | 10 ++ unrar/scantree.cpp | 19 +- unrar/secpassword.cpp | 15 +- unrar/secpassword.hpp | 9 +- unrar/strfn.cpp | 26 +++ unrar/strfn.hpp | 1 + unrar/system.cpp | 6 +- unrar/threadmisc.cpp | 2 + unrar/ui.hpp | 37 ++-- unrar/uicommon.cpp | 2 +- unrar/uiconsole.cpp | 4 +- unrar/uisilent.cpp | 3 +- unrar/ulinks.cpp | 34 +++- unrar/unicode.cpp | 7 +- unrar/unicode.hpp | 2 +- unrar/unpack.cpp | 2 +- unrar/unpack.hpp | 6 +- unrar/unpack30.cpp | 2 +- unrar/unpack50.cpp | 4 +- unrar/unpack50mt.cpp | 14 +- unrar/version.hpp | 6 +- unrar/volume.cpp | 10 +- unrar/volume.hpp | 3 - 73 files changed, 1117 insertions(+), 415 deletions(-) diff --git a/unrar/UnRARDll.vcxproj b/unrar/UnRARDll.vcxproj index ec5c17b0..72cecd8d 100644 --- a/unrar/UnRARDll.vcxproj +++ b/unrar/UnRARDll.vcxproj @@ -138,7 +138,7 @@ Sync EnableFastChecks MultiThreadedDebug - 4Bytes + Default false Use rar.hpp @@ -168,7 +168,7 @@ Sync EnableFastChecks MultiThreadedDebug - 4Bytes + Default false Use rar.hpp @@ -198,7 +198,7 @@ false Sync MultiThreaded - 4Bytes + Default true true NoExtensions @@ -239,7 +239,7 @@ false Sync MultiThreaded - 4Bytes + Default true true false @@ -274,7 +274,7 @@ false Sync MultiThreaded - 4Bytes + Default true true NoExtensions @@ -315,7 +315,7 @@ false Sync MultiThreaded - 4Bytes + Default true true false diff --git a/unrar/archive.cpp b/unrar/archive.cpp index 0f5de946..25f0c3b7 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -3,15 +3,15 @@ #include "arccmt.cpp" -Archive::Archive(RAROptions *InitCmd) +Archive::Archive(CommandData *InitCmd) { Cmd=NULL; // Just in case we'll have an exception in 'new' below. DummyCmd=(InitCmd==NULL); - Cmd=DummyCmd ? (new RAROptions):InitCmd; + Cmd=DummyCmd ? (new CommandData):InitCmd; OpenShared=Cmd->OpenShared; - Format=RARFMT15; + Format=RARFMT_NONE; Solid=false; Volume=false; MainComment=false; @@ -31,9 +31,9 @@ Archive::Archive(RAROptions *InitCmd) NextBlockPos=0; - memset(&MainHead,0,sizeof(MainHead)); - memset(&CryptHead,0,sizeof(CryptHead)); - memset(&EndArcHead,0,sizeof(EndArcHead)); + MainHead.Reset(); + CryptHead={}; + EndArcHead.Reset(); VolNumber=0; VolWrite=0; diff --git a/unrar/archive.hpp b/unrar/archive.hpp index d9518f1d..35a399bc 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -32,8 +32,8 @@ class Archive:public File size_t ReadHeader14(); size_t ReadHeader15(); size_t ReadHeader50(); - void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); - void RequestArcPassword(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb); + void RequestArcPassword(RarCheckPassword *SelPwd); void UnexpEndArcMsg(); void BrokenHeaderMsg(); void UnkEncVerMsg(const wchar *Name,const wchar *Info); @@ -45,7 +45,7 @@ class Archive:public File #endif ComprDataIO SubDataIO; bool DummyCmd; - RAROptions *Cmd; + CommandData *Cmd; RarTime LatestTime; @@ -58,7 +58,7 @@ class Archive:public File bool ProhibitQOpen; #endif public: - Archive(RAROptions *InitCmd=NULL); + Archive(CommandData *InitCmd=NULL); ~Archive(); static RARFORMAT IsSignature(const byte *D,size_t Size); bool IsArchive(bool EnableBroken); @@ -83,7 +83,7 @@ class Archive:public File const wchar *Name,uint Flags); bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode); HEADER_TYPE GetHeaderType() {return CurHeaderType;} - RAROptions* GetRAROptions() {return Cmd;} + CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} #if 0 void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 1a401f48..3d12684e 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -100,6 +100,9 @@ void Archive::UnexpEndArcMsg() // If block positions are equal to file size, this is not an error. // It can happen when we reached the end of older RAR 1.5 archive, // which did not have the end of archive block. + // We can't replace this check by checking that read size is exactly 0 + // in the beginning of file header, because in this case the read position + // still can be beyond the end of archive. if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) { uiMsg(UIERROR_UNEXPEOF,FileName); @@ -145,7 +148,7 @@ size_t Archive::ReadHeader15() #ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. return 0; #else - RequestArcPassword(); + RequestArcPassword(NULL); byte Salt[SIZE_SALT30]; if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) @@ -577,14 +580,20 @@ size_t Archive::ReadHeader50() // in -p to not stop batch processing for encrypted archives. bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); + RarCheckPassword CheckPwd; + if (CryptHead.UsePswCheck && !BrokenHeader) + CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck); + while (true) // Repeat the password prompt for wrong passwords. { - RequestArcPassword(); + RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); byte PswCheck[SIZE_PSWCHECK]; HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); - // Verify password validity. - if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) + // Verify password validity. If header is damaged, we cannot rely on + // password check value, because it can be damaged too. + if (CryptHead.UsePswCheck && !BrokenHeader && + memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P. { @@ -850,8 +859,6 @@ size_t Archive::ReadHeader50() hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); - hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; - char FileName[NM*4]; size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); Raw.GetB((byte *)FileName,ReadNameSize); @@ -875,25 +882,6 @@ size_t Archive::ReadHeader50() if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; -#if 0 - // For RAR5 format we read the user specified recovery percent here. - // It would be useful to do it for shell extension too, so we display - // the correct recovery record size in archive properties. But then - // we would need to include the entire recovery record processing - // code to shell extension, which is not done now. - if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) - { - // It is stored as a single byte up to RAR 6.02 and as vint since - // 6.10, where we extended the maximum RR size from 99% to 1000%. - RawRead RawPercent; - RawPercent.Read(&hd->SubData[0],hd->SubData.Size()); - RecoveryPercent=(int)RawPercent.GetV(); - - RSBlockHeader Header; - GetRRInfo(this,&Header); - RecoverySize=Header.RecSectionSize*Header.RecCount; - } -#endif if (BadCRC) // Add the file name to broken header message displayed above. uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); @@ -916,7 +904,7 @@ size_t Archive::ReadHeader50() #if !defined(RAR_NOCRYPT) -void Archive::RequestArcPassword() +void Archive::RequestArcPassword(RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { @@ -946,7 +934,7 @@ void Archive::RequestArcPassword() ErrHandler.Exit(RARX_USERBREAK); } #else - if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd)) { Close(); uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. @@ -959,7 +947,7 @@ void Archive::RequestArcPassword() #endif -void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) +void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) { // Read extra data from the end of block skipping any fields before it. size_t ExtraStart=Raw->Size()-ExtraSize; @@ -982,22 +970,57 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) if (bb->HeaderType==HEAD_MAIN) { MainHeader *hd=(MainHeader *)bb; - if (FieldType==MHEXTRA_LOCATOR) + switch(FieldType) { - hd->Locator=true; - uint Flags=(uint)Raw->GetV(); - if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) - { - uint64 Offset=Raw->GetV(); - if (Offset!=0) // 0 means that reserved space was not enough to write the offset. - hd->QOpenOffset=Offset+CurBlockPos; - } - if ((Flags & MHEXTRA_LOCATOR_RR)!=0) - { - uint64 Offset=Raw->GetV(); - if (Offset!=0) // 0 means that reserved space was not enough to write the offset. - hd->RROffset=Offset+CurBlockPos; - } + case MHEXTRA_LOCATOR: + { + hd->Locator=true; + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->QOpenOffset=Offset+CurBlockPos; + } + if ((Flags & MHEXTRA_LOCATOR_RR)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->RROffset=Offset+CurBlockPos; + } + } + break; + case MHEXTRA_METADATA: + { + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_METADATA_NAME)!=0) + { + uint64 NameSize=Raw->GetV(); + if (NameSize<0x10000) // Prevent excessive allocation. + { + std::vector NameU((size_t)NameSize); // UTF-8 name. + Raw->GetB(&NameU[0],(size_t)NameSize); + // If starts from 0, the name was longer han reserved space + // when saving this extra field. + if (NameU[0]!=0) + { + NameU.push_back(0); + std::vector NameW(NameU.size()*4); + UtfToWide(&NameU[0],&NameW[0],NameW.size()); + hd->OrigName.assign(&NameW[0]); + } + } + } + if ((Flags & MHEXTRA_METADATA_CTIME)!=0) + if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0) + if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0) + hd->OrigCtime.SetUnixNS(Raw->Get8()); + else + hd->OrigCtime.SetUnix((time_t)Raw->Get4()); + else + hd->OrigCtime.SetWin(Raw->Get8()); + } + break; } } diff --git a/unrar/array.hpp b/unrar/array.hpp index 20d258d5..ac786f71 100644 --- a/unrar/array.hpp +++ b/unrar/array.hpp @@ -10,7 +10,6 @@ template class Array size_t BufSize; size_t AllocSize; size_t MaxSize; - bool Secure; // Clean memory if true. public: Array(); Array(size_t Size); @@ -24,14 +23,13 @@ template class Array void Alloc(size_t Items); void Reset(); void SoftReset(); - void operator = (Array &Src); + Array& operator = (const Array &Src); void Push(T Item); void Append(T *Item,size_t Count); T* Addr(size_t Item) {return Buffer+Item;} void SetMaxSize(size_t Size) {MaxSize=Size;} T* Begin() {return Buffer;} T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} - void SetSecure() {Secure=true;} }; @@ -41,7 +39,6 @@ template void Array::CleanData() BufSize=0; AllocSize=0; MaxSize=0; - Secure=false; } @@ -71,11 +68,7 @@ template Array::Array(const Array &Src) template Array::~Array() { if (Buffer!=NULL) - { - if (Secure) - cleandata(Buffer,AllocSize*sizeof(T)); free(Buffer); - } } @@ -111,25 +104,9 @@ template void Array::Add(size_t Items) size_t Suggested=AllocSize+AllocSize/4+32; size_t NewSize=Max(BufSize,Suggested); - T *NewBuffer; - if (Secure) - { - NewBuffer=(T *)malloc(NewSize*sizeof(T)); - if (NewBuffer==NULL) - ErrHandler.MemoryError(); - if (Buffer!=NULL) - { - memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); - cleandata(Buffer,AllocSize*sizeof(T)); - free(Buffer); - } - } - else - { - NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); - if (NewBuffer==NULL) - ErrHandler.MemoryError(); - } + T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); Buffer=NewBuffer; AllocSize=NewSize; } @@ -165,12 +142,13 @@ template void Array::SoftReset() } -template void Array::operator =(Array &Src) +template Array& Array::operator =(const Array &Src) { Reset(); Alloc(Src.BufSize); if (Src.BufSize!=0) memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); + return *this; } diff --git a/unrar/blake2s.hpp b/unrar/blake2s.hpp index f88ef378..06e396a7 100644 --- a/unrar/blake2s.hpp +++ b/unrar/blake2s.hpp @@ -20,10 +20,15 @@ enum blake2s_constant // 'new' operator. struct blake2s_state { - enum { BLAKE_ALIGNMENT = 64 }; + // Use constexpr instead of enums, because otherwise clang -std=c++20 + // issues a warning about "arithmetic between different enumeration types" + // in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration. + static constexpr size_t BLAKE_ALIGNMENT = 64; // buffer and uint32 h[8], t[2], f[2]; - enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; + // 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip. + // It might differ in later versions. + static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES; byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 82d24f44..3a4ab99e 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -26,9 +26,10 @@ void CommandData::Init() FileArgs.Reset(); ExclArgs.Reset(); InclArgs.Reset(); - StoreArgs.Reset(); ArcNames.Reset(); - NextVolSizes.Reset(); + StoreArgs.Reset(); + Password.Clean(); + NextVolSizes.clear(); } @@ -314,6 +315,21 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'I': IgnoreGeneralAttr=true; break; + case 'M': + switch(toupperw(Switch[2])) + { + case 0: + case 'S': + ArcMetadata=ARCMETA_SAVE; + break; + case 'R': + ArcMetadata=ARCMETA_RESTORE; + break; + default: + BadSwitch(Switch); + break; + } + break; case 'N': // Reserved for archive name. break; case 'O': @@ -415,7 +431,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) else if (!Password.IsSet()) { - uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); eprintf(L"\n"); } break; @@ -685,7 +701,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'P': if (Switch[1]==0) { - uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); eprintf(L"\n"); } else diff --git a/unrar/cmddata.hpp b/unrar/cmddata.hpp index 2a219220..0feb404b 100644 --- a/unrar/cmddata.hpp +++ b/unrar/cmddata.hpp @@ -2,7 +2,7 @@ #define _RAR_CMDDATA_ -#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" +#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst" enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; @@ -65,6 +65,10 @@ class CommandData:public RAROptions StringList InclArgs; StringList ArcNames; StringList StoreArgs; + + SecPassword Password; + + std::vector NextVolSizes; }; #endif diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 1fc4b8ca..6bd1e1ac 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -92,6 +92,13 @@ void CommandData::OutHelp(RAR_EXIT ExitCode) if (Found) continue; #endif +#ifdef _UNIX + if (CmpMSGID(Help[I],MRARTitle2)) + { + mprintf(St(MFwrSlTitle2)); + continue; + } +#endif #if !defined(_UNIX) && !defined(_WIN_ALL) if (CmpMSGID(Help[I],MCHelpSwOW)) continue; diff --git a/unrar/compress.hpp b/unrar/compress.hpp index 73f7ee41..4ef8570f 100644 --- a/unrar/compress.hpp +++ b/unrar/compress.hpp @@ -17,6 +17,7 @@ class PackDef static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3; static const uint LOW_DIST_REP_COUNT = 16; static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ diff --git a/unrar/crc.cpp b/unrar/crc.cpp index 0e5e1320..0c6aef16 100644 --- a/unrar/crc.cpp +++ b/unrar/crc.cpp @@ -110,3 +110,164 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) #endif +#if 0 +static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64. + +void InitCRC64(uint64 *CRCTab) +{ + const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42; + for (uint I=0;I<256;I++) + { + uint64 C=I; + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^poly: (C>>1); + CRCTab[I]=C; + } +} + + +static void InitTables64() +{ + InitCRC64(crc64_tables[0]); + + for (uint I=0;I<256;I++) // Build additional lookup tables. + { + uint64 C=crc64_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc64_tables[0][(byte)C]^(C>>8); + crc64_tables[J][I]=C; + } + } +} + + +// We cannot place the intialization to CRC64(), because we use this function +// in multithreaded mode and it conflicts with multithreading. +struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64; + +uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7)!=0;Size--,Data++) + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (byte *DataEnd=Data+Size/8*8; Data> 8 ) ] ^ + crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^ + crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^ + crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^ + crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^ + crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^ + crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ; + } + + for (Size%=8;Size>0;Size--,Data++) // Process left data. + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + +#if 0 +static void TestCRC(); +struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC; + +void TestCRC() +{ + const uint FirstSize=300; + byte b[FirstSize]; + + if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84) + mprintf(L"\nCRC32 test1 OK"); + else + mprintf(L"\nCRC32 test1 FAILED"); + + if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE) + mprintf(L"\nCRC32 test2 OK"); + else + mprintf(L"\nCRC32 test2 FAILED"); + + for (uint I=0;I<14;I++) // Check for possible int sign extension. + b[I]=(byte)0x7f+I; + if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA) + mprintf(L"\nCRC32 test3 OK"); + else + mprintf(L"\nCRC32 test3 FAILED"); + + for (uint I=0;IGet(PwdW,ASIZE(PwdW)); + PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. + char PwdA[MAXPASSWORD]; WideToChar(PwdW,PwdA,ASIZE(PwdA)); + PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. switch(Method) { diff --git a/unrar/crypt.hpp b/unrar/crypt.hpp index f6382ef5..22d18e77 100644 --- a/unrar/crypt.hpp +++ b/unrar/crypt.hpp @@ -15,7 +15,10 @@ enum CRYPT_METHOD { #define CRYPT_BLOCK_SIZE 16 #define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf -#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. +// 2013.04.29: set to 15 for RAR 5.00 beta 1. +// 2022.09.07: changed to 16 for upcoming RAR 6.20. +#define CRYPT5_KDF_LG2_COUNT 16 // LOG2 of PDKDF2 iteration count. + #define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. #define CRYPT_VERSION 0 // Supported encryption version. @@ -30,6 +33,18 @@ class CryptData uint Lg2Count; // Log2 of PBKDF2 repetition count. byte PswCheckValue[SHA256_DIGEST_SIZE]; byte HashKeyValue[SHA256_DIGEST_SIZE]; + + KDF5CacheItem() {Clean();} + ~KDF5CacheItem() {Clean();} + + void Clean() + { + cleandata(Salt,sizeof(Salt)); + cleandata(Key,sizeof(Key)); + cleandata(&Lg2Count,sizeof(Lg2Count)); + cleandata(PswCheckValue,sizeof(PswCheckValue)); + cleandata(HashKeyValue,sizeof(HashKeyValue)); + } }; struct KDF3CacheItem @@ -39,6 +54,17 @@ class CryptData byte Key[16]; byte Init[16]; bool SaltPresent; + + KDF3CacheItem() {Clean();} + ~KDF3CacheItem() {Clean();} + + void Clean() + { + cleandata(Salt,sizeof(Salt)); + cleandata(Key,sizeof(Key)); + cleandata(Init,sizeof(Init)); + cleandata(&SaltPresent,sizeof(SaltPresent)); + } }; @@ -77,7 +103,6 @@ class CryptData ushort Key15[4]; public: CryptData(); - ~CryptData(); bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt, byte *HashKey,byte *PswCheck); @@ -88,6 +113,54 @@ class CryptData static void SetSalt(byte *Salt,size_t SaltSize); }; + +class CheckPassword +{ + public: + enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW}; + virtual CONFIDENCE GetConfidence()=0; + virtual bool Check(SecPassword *Password)=0; +}; + +class RarCheckPassword:public CheckPassword +{ + private: + CryptData *Crypt; + uint Lg2Count; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + byte PswCheck[SIZE_PSWCHECK]; + public: + RarCheckPassword() + { + Crypt=NULL; + } + ~RarCheckPassword() + { + delete Crypt; + } + void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck) + { + if (Crypt==NULL) + Crypt=new CryptData; + memcpy(this->Salt,Salt,sizeof(this->Salt)); + memcpy(this->InitV,InitV,sizeof(this->InitV)); + this->Lg2Count=Lg2Count; + memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck)); + } + bool IsSet() {return Crypt!=NULL;} + + // RAR5 provides the higly reliable 64 bit password verification value. + CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;} + + bool Check(SecPassword *Password) + { + byte PswCheck[SIZE_PSWCHECK]; + Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck); + return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0; + } +}; + void GetRnd(byte *RndBuf,size_t BufSize); void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, diff --git a/unrar/crypt3.cpp b/unrar/crypt3.cpp index fe3bf97b..e6e3a82c 100644 --- a/unrar/crypt3.cpp +++ b/unrar/crypt3.cpp @@ -18,8 +18,9 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co if (!Cached) { byte RawPsw[2*MAXPASSWORD+SIZE_SALT30]; - WideToRaw(PwdW,RawPsw,ASIZE(RawPsw)); - size_t RawLength=2*wcslen(PwdW); + size_t PswLength=wcslen(PwdW); + size_t RawLength=2*PswLength; + WideToRaw(PwdW,PswLength,RawPsw,RawLength); if (Salt!=NULL) { memcpy(RawPsw+RawLength,Salt,SIZE_SALT30); diff --git a/unrar/crypt5.cpp b/unrar/crypt5.cpp index 7562469f..5ed65af8 100644 --- a/unrar/crypt5.cpp +++ b/unrar/crypt5.cpp @@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, sha256_context ICtx; if (ICtxOpt!=NULL && *SetIOpt) - ICtx=*ICtxOpt; // Use already calculated first block context. + ICtx=*ICtxOpt; // Use already calculated the first block context. else { // This calculation is the same for all iterations with same password. @@ -90,10 +90,10 @@ void pbkdf2(const byte *Pwd, size_t PwdLength, byte SaltData[MaxSalt+4]; memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); - SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. - SaltData[SaltLength + 1] = 0; - SaltData[SaltLength + 2] = 0; - SaltData[SaltLength + 3] = 1; + SaltData[SaltLength + 0] = 0; // Block index appened to salt. + SaltData[SaltLength + 1] = 0; // + SaltData[SaltLength + 2] = 0; // Since we do not request the key width + SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1. // First iteration: HMAC of password, salt and block index (1). byte U1[SHA256_DIGEST_SIZE]; @@ -140,7 +140,7 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password && + if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt && memcmp(Item->Salt,Salt,SIZE_SALT50)==0) { memcpy(Key,Item->Key,sizeof(Key)); diff --git a/unrar/dll.rc b/unrar/dll.rc index ae9a964e..b2dcdc9b 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 11, 1, 418 -PRODUCTVERSION 6, 11, 1, 418 +FILEVERSION 6, 20, 1, 663 +PRODUCTVERSION 6, 20, 1, 663 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", "6.11.1\0" - VALUE "ProductVersion", "6.11.1\0" + VALUE "FileVersion", "6.20.1\0" + VALUE "ProductVersion", "6.20.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index dc109b9d..96ad528b 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -5,9 +5,12 @@ CmdExtract::CmdExtract(CommandData *Cmd) CmdExtract::Cmd=Cmd; *ArcName=0; - *DestFileName=0; + ArcAnalyzed=false; + Analyze=new AnalyzeData; + memset(Analyze,0,sizeof(*Analyze)); + TotalFileCount=0; Unp=new Unpack(&DataIO); #ifdef RAR_SMP @@ -18,7 +21,26 @@ CmdExtract::CmdExtract(CommandData *Cmd) CmdExtract::~CmdExtract() { + ReleaseAnalyzeData(); delete Unp; + delete Analyze; +} + + +void CmdExtract::ReleaseAnalyzeData() +{ + for (size_t I=0;ICommand[0]); - FindData FD; - while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) - if (FindFile::FastFind(ArcName,&FD)) - DataIO.TotalArcSize+=FD.Size; + if (*Cmd->UseStdin==0) + { + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) + DataIO.TotalArcSize+=FD.Size; + } Cmd->ArcNames.Rewind(); while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) @@ -97,6 +122,8 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc) AllMatchesExact=true; AnySolidDataUnpackedWell=false; + ArcAnalyzed=false; + StartTime.SetCurrentTime(); } @@ -168,20 +195,33 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() } #endif + Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT. + int64 VolumeSetSize=0; // Total size of volumes after the current volume. +#ifndef SFX_MODULE + if (!ArcAnalyzed && *Cmd->UseStdin==0) + { + AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering); + ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT. + } +#endif + if (Arc.Volume) { #ifndef SFX_MODULE // Try to speed up extraction for independent solid volumes by starting // extraction from non-first volume if we can. - if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering)) + if (*Analyze->StartName!=0) { + wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName)); + *Analyze->StartName=0; + UseExactVolName=true; return EXTRACT_ARC_REPEAT; } #endif - + // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. @@ -216,7 +256,13 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() else uiStartArchiveExtract(!Cmd->Test,ArcName); - Arc.ViewComment(); +#ifndef SFX_MODULE + if (Analyze->StartPos!=0) + { + Arc.Seek(Analyze->StartPos,SEEK_SET); + Analyze->StartPos=0; + } +#endif while (1) @@ -272,7 +318,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) return false; HEADER_TYPE HeaderType=Arc.GetHeaderType(); - if (HeaderType!=HEAD_FILE) + if (HeaderType==HEAD_FILE) + { + // Unlike Arc.FileName, ArcName might store an old volume name here. + if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos && + (*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0)) + return false; + } + else { #ifndef SFX_MODULE if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) @@ -315,6 +368,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Arc.FileHead.UnpSize<0) Arc.FileHead.UnpSize=0; + // 2022.03.20: We might remove this check in the future. + // It duplicates Analyze->EndPos and Analyze->EndName in all cases except + // volumes on removable media. if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) return false; @@ -413,13 +469,32 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) FirstFile=false; #endif + bool RefTarget=false; + if (!MatchFound) + for (size_t I=0;ITest) // While harmless, it is useless for 't'. + { + wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); + AddEndSlash(DestFileName,ASIZE(DestFileName)); + wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); + MkTemp(DestFileName,ASIZE(DestFileName)); + MatchedRef->TmpName=wcsdup(DestFileName); + } + RefTarget=true; // Need it even for 't' to test the reference source. + break; + } + if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) if (Arc.Solid) return false; // Abort the entire extraction for solid archive. else MatchFound=false; // Skip only the current file for non-solid archive. - if (MatchFound || (SkipSolid=Arc.Solid)!=0) + if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0) { // First common call of uiStartFileExtract. It is done before overwrite // prompts, so if SkipSolid state is changed below, we'll need to make @@ -427,7 +502,8 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) return false; - ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); + if (!RefTarget) + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); // DestFileName can be set empty in case of excessive -ap switch. ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; @@ -464,9 +540,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) return !Arc.Solid; // Can try extracting next file only in non-solid archive. } - while (true) // Repeat the password prompt for wrong and empty passwords. + if (Arc.FileHead.Encrypted) { - if (Arc.FileHead.Encrypted) + RarCheckPassword CheckPwd; + if (Arc.Format==RARFMT50 && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader) + CheckPwd.Set(Arc.FileHead.Salt,Arc.FileHead.InitV,Arc.FileHead.Lg2Count,Arc.FileHead.PswCheck); + + while (true) // Repeat the password prompt for wrong and empty passwords. { // Stop archive extracting if user cancelled a password prompt. #ifdef RARDLL @@ -476,62 +556,61 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) return false; } #else - if (!ExtrGetPassword(Arc,ArcFileName)) + if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL)) { PasswordCancelled=true; return false; } #endif - } - - // Set a password before creating the file, so we can skip creating - // in case of wrong password. - SecPassword FilePassword=Cmd->Password; -#if defined(_WIN_ALL) && !defined(SFX_MODULE) - ConvertDosPassword(Arc,FilePassword); -#endif - byte PswCheck[SIZE_PSWCHECK]; - DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, - Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, - Arc.FileHead.InitV,Arc.FileHead.Lg2Count, - Arc.FileHead.HashKey,PswCheck); - - // If header is damaged, we cannot rely on password check value, - // because it can be damaged too. - if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && - memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && - !Arc.BrokenHeader) - { - if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; + #if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); + #endif + + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); + + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) { - // This message is used by Android GUI to reset cached passwords. - // Update appropriate code if changed. - uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); - } - else // For passwords entered manually. - { - // This message is used by Android GUI and Windows GUI and SFX to - // reset cached passwords. Update appropriate code if changed. - uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); - Cmd->Password.Clean(); - - // Avoid new requests for unrar.dll to prevent the infinite loop - // if app always returns the same password. -#ifndef RARDLL - continue; // Request a password again. -#endif + if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); + Cmd->Password.Clean(); + + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. + #ifndef RARDLL + continue; // Request a password again. + #endif + } + #ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_PASSWORD. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_PASSWORD; + #endif + ErrHandler.SetErrorCode(RARX_BADPWD); + ExtrFile=false; } -#ifdef RARDLL - // If we already have ERAR_EOPEN as result of missing volume, - // we should not replace it with less precise ERAR_BAD_PASSWORD. - if (Cmd->DllError!=ERAR_EOPEN) - Cmd->DllError=ERAR_BAD_PASSWORD; -#endif - ErrHandler.SetErrorCode(RARX_BADPWD); - ExtrFile=false; + break; } - break; } #ifdef RARDLL @@ -542,11 +621,12 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) File CurFile; bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; - if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) + if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) { if (ExtrFile && Command!='P' && !Cmd->Test) { - // Overwrite prompt for symbolic and hard links. + // Overwrite prompt for symbolic and hard links and when we move + // a temporary file to the file reference instead of copying it. bool UserReject=false; if (FileExist(DestFileName) && !UserReject) FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); @@ -675,7 +755,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Type==FSREDIR_HARDLINK) LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); else - LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize); } else if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) @@ -866,22 +946,66 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) } -bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) { SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. File Existing; - if (!Existing.WOpen(NameExisting)) + if (!Existing.Open(NameExisting)) { - uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); - uiMsg(UIERROR_FILECOPYHINT,ArcName); + bool OpenFailed=true; + // If we couldn't find the existing file, check if match is present + // in temporary reference sources list. + for (size_t I=0;IDllError=ERAR_EREFERENCE; + Cmd->DllError=ERAR_EREFERENCE; #endif - return false; + return false; + } } - Array Buffer(0x100000); + Array Buffer(0x100000); int64 CopySize=0; while (true) @@ -890,6 +1014,10 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar * int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); if (ReadSize==0) break; + // Update only the current file progress in WinRAR, set the total to 0 + // to keep it as is. It looks better for WinRAR. + uiExtractProgress(CopySize,UnpSize,0,0); + New.Write(&Buffer[0],ReadSize); CopySize+=ReadSize; } @@ -1033,11 +1161,11 @@ bool CmdExtract::ExtrDllGetPassword() #ifndef RARDLL -bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { - if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !Cmd->Password.IsSet()*/) { // Suppress "test is ok" message if user cancelled the password prompt. uiMsg(UIERROR_INCERRCOUNT); @@ -1055,7 +1183,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) case -1: ErrHandler.Exit(RARX_USERBREAK); case 2: - if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)) return false; break; case 3: @@ -1258,31 +1386,54 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) #ifndef SFX_MODULE -// To speed up solid volumes extraction, try to find a non-first start volume, -// which still allows to unpack all files. It is possible for independent -// solid volumes with solid statistics reset in the beginning. -bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) +// Find non-matched reference sources in solid and non-solid archives. +// Detect the optimal start position for semi-solid archives +// and optimal start volume for independent solid volumes. +// +// Alternatively we could collect references while extracting an archive +// and perform the second extraction pass for references only. +// But it would be slower for solid archives than scaning headers +// in first pass and extracting everything in second, as implemented now. +// +void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering) { + ReleaseAnalyzeData(); // If processing non-first archive in multiple archives set. + wchar *ArgName=Cmd->FileArgs.GetString(); Cmd->FileArgs.Rewind(); if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) - return false; // No need to check further for * and *.* masks. + return; // No need to check further for * and *.* masks. - wchar StartName[NM]; - *StartName=0; - // Start search from first volume if all volumes preceding current are available. wchar NextName[NM]; - GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName)); + if (Volume) + GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName)); + else + wcsncpyz(NextName,ArcName,ASIZE(NextName)); + + bool MatchFound=false; + bool FirstVolume=true; + bool PrevMatched=false; + bool OpenNext=false; - bool Matched=false; - while (!Matched) + while (true) { Archive Arc(Cmd); - if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume) + if (!Arc.Open(NextName) || !Arc.IsArchive(false)) + { + if (OpenNext) + { + // If we couldn't open trailing volumes, we can't set early exit + // parameters. It is possible that some volume are on removable media + // and will be provided by user when extracting. + *Analyze->EndName=0; + Analyze->EndPos=0; + } break; + } - bool OpenNext=false; + OpenNext=false; + bool FirstFile=true; while (Arc.ReadHeader()>0) { Wait(); @@ -1297,13 +1448,60 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) { if (!Arc.FileHead.SplitBefore) { - if (!Arc.FileHead.Solid) // Can start extraction from here. - wcsncpyz(StartName,NextName,ASIZE(StartName)); + if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. + { + if (!FirstVolume) + wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); + if (!FirstFile) + Analyze->StartPos=Arc.CurBlockPos; + } + FirstFile=false; if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) { - Matched=true; // First matched file found, must stop further scan. - break; + MatchFound = true; + PrevMatched = true; + + // Matched file reference pointing at maybe non-matched source file. + // Even though we know RedirName, we can't check if source file + // is certainly non-matched, because it can be filtered out by + // date or attributes, which we do not know here. + if (Arc.FileHead.RedirType==FSREDIR_FILECOPY) + { + bool AlreadyAdded=false; + for (size_t I=0;IEndName,NextName,ASIZE(Analyze->EndName)); + if (!FirstFile) + Analyze->EndPos=Arc.CurBlockPos; + } + PrevMatched=false; } } if (Arc.FileHead.SplitAfter) @@ -1316,16 +1514,19 @@ bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) } Arc.Close(); - if (!OpenNext) + if (Volume && OpenNext) + { + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FirstVolume=false; + } + else 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; + + // If file references are present, we can't reliably skip in semi-solid + // archives, because reference source can be present in skipped data. + if (RefList.Size()!=0) + memset(Analyze,0,sizeof(*Analyze)); } #endif diff --git a/unrar/extract.hpp b/unrar/extract.hpp index 159759b5..aac463c7 100644 --- a/unrar/extract.hpp +++ b/unrar/extract.hpp @@ -6,13 +6,32 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; class CmdExtract { private: + struct ExtractRef + { + wchar *RefName; + wchar *TmpName; + uint64 RefCount; + }; + Array RefList; + + struct AnalyzeData + { + wchar StartName[NM]; + uint64 StartPos; + wchar EndName[NM]; + uint64 EndPos; + } *Analyze; + + bool ArcAnalyzed; + + void ReleaseAnalyzeData(); EXTRACT_ARC_CODE ExtractArchive(); - bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize); void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); #ifdef RARDLL bool ExtrDllGetPassword(); #else - bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); @@ -21,7 +40,7 @@ class CmdExtract bool ExtrCreateFile(Archive &Arc,File &CurFile); bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); #ifndef SFX_MODULE - bool DetectStartVolume(const wchar *VolName,bool NewNumbering); + void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering); void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); #endif diff --git a/unrar/filcreat.cpp b/unrar/filcreat.cpp index 8e7562cb..d58e4f6f 100644 --- a/unrar/filcreat.cpp +++ b/unrar/filcreat.cpp @@ -3,7 +3,7 @@ // If NewFile==NULL, we delete created file after user confirmation. // It is useful if we need to overwrite an existing folder or file, // but need user confirmation for that. -bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) diff --git a/unrar/filcreat.hpp b/unrar/filcreat.hpp index 44f801d4..456a4a4a 100644 --- a/unrar/filcreat.hpp +++ b/unrar/filcreat.hpp @@ -1,7 +1,7 @@ #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ -bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize=INT64NDF, RarTime *FileTime=NULL,bool WriteOnly=false); diff --git a/unrar/file.cpp b/unrar/file.cpp index e7b584da..7bf60fd4 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -522,29 +522,35 @@ bool File::RawSeek(int64 Offset,int Method) { if (hFile==FILE_BAD_HANDLE) return true; - if (!IsSeekable()) + if (!IsSeekable()) // To extract archives from stdin with -si. { - if (Method==SEEK_CUR) - { - Offset+=CurFilePos; - Method=SEEK_SET; - } - if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward. + // We tried to dynamically allocate 32 KB buffer here, but it improved + // speed in Windows 10 by mere ~1.5%. + byte Buf[4096]; + if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos) { - uint64 SkipSize=Offset-CurFilePos; - while (SkipSize>0) + uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos; + while (SkipSize>0) // Reading to emulate seek forward. { - byte Buf[4096]; int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); if (ReadSize<=0) return false; SkipSize-=ReadSize; + CurFilePos+=ReadSize; } - CurFilePos=Offset; + return true; + } + // May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking + // RAR 4.x archives without the end of archive block created with -en. + if (Method==SEEK_END) + { + int ReadSize; + while ((ReadSize=Read(Buf,ASIZE(Buf)))>0) + CurFilePos+=ReadSize; return true; } - return false; // Backward or end of file seek on unseekable file. + return false; // Backward seek on unseekable file. } if (Offset<0 && Method!=SEEK_SET) { @@ -732,17 +738,40 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) } -void File::GetOpenFileTime(RarTime *ft) +#ifdef _UNIX +void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_ALL - FILETIME FileTime; - GetFileTime(hFile,NULL,NULL,&FileTime); - ft->SetWinFT(&FileTime); +#ifdef UNIX_TIME_NS +#if defined(_APPLE) + if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); + if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); + if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); +#else + if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); + if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); + if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); +#endif +#else + if (ftm!=NULL) ftm->SetUnix(st.st_mtime); + if (ftc!=NULL) ftc->SetUnix(st.st_ctime); + if (fta!=NULL) fta->SetUnix(st.st_atime); +#endif +} #endif -#if defined(_UNIX) || defined(_EMX) + + +void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) +{ +#ifdef _WIN_ALL + FILETIME ctime,atime,mtime; + GetFileTime(hFile,&ctime,&atime,&mtime); + if (ftm!=NULL) ftm->SetWinFT(&mtime); + if (ftc!=NULL) ftc->SetWinFT(&ctime); + if (fta!=NULL) fta->SetWinFT(&atime); +#elif defined(_UNIX) struct stat st; fstat(GetFD(),&st); - ft->SetUnix(st.st_mtime); + StatToRarTime(st,ftm,ftc,fta); #endif } diff --git a/unrar/file.hpp b/unrar/file.hpp index 1de5fe6d..5f55de96 100644 --- a/unrar/file.hpp +++ b/unrar/file.hpp @@ -14,8 +14,6 @@ #define FILE_BAD_HANDLE NULL #endif -class RAROptions; - enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; @@ -88,6 +86,9 @@ class File wchar FileName[NM]; FILE_ERRORTYPE ErrorType; + + byte *SeekBuf; // To read instead of seek for stdin files. + static const size_t SeekBufSize=0x10000; public: File(); virtual ~File(); @@ -118,7 +119,10 @@ class File void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); - void GetOpenFileTime(RarTime *ft); +#ifdef _UNIX + static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta); +#endif + void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. int64 FileLength(); void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index 883c26e2..aaef305b 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -320,7 +320,6 @@ bool SetFileAttr(const wchar *Name,uint Attr) } -#if 0 wchar *MkTemp(wchar *Name,size_t MaxSize) { size_t Length=wcslen(Name); @@ -354,7 +353,6 @@ wchar *MkTemp(wchar *Name,size_t MaxSize) } return Name; } -#endif #if !defined(SFX_MODULE) @@ -399,7 +397,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) { // Update only the current file progress in WinRAR, set the total to 0 - // to keep it as is. It looks better for WinRAR, + // to keep it as is. It looks better for WinRAR. uiExtractProgress(TotalRead,FileLength,0,0); } else diff --git a/unrar/filefn.hpp b/unrar/filefn.hpp index 36dc7039..53d86653 100644 --- a/unrar/filefn.hpp +++ b/unrar/filefn.hpp @@ -27,9 +27,7 @@ bool IsDeleteAllowed(uint FileAttr); void PrepareToDelete(const wchar *Name); uint GetFileAttr(const wchar *Name); bool SetFileAttr(const wchar *Name,uint Attr); -#if 0 wchar* MkTemp(wchar *Name,size_t MaxSize); -#endif enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; diff --git a/unrar/find.cpp b/unrar/find.cpp index 04702ab8..c9f2c576 100644 --- a/unrar/find.cpp +++ b/unrar/find.cpp @@ -117,7 +117,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) if (hFind==INVALID_HANDLE_VALUE) return false; FindClose(hFind); -#else +#elif defined(_UNIX) char FindMaskA[NM]; WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); @@ -143,21 +143,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) fd->FileAttr=st.st_mode; fd->Size=st.st_size; -#ifdef UNIX_TIME_NS -#if defined(_APPLE) - fd->mtime.SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); - fd->atime.SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); - fd->ctime.SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); -#else - fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); - fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); - fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); -#endif -#else - fd->mtime.SetUnix(st.st_mtime); - fd->atime.SetUnix(st.st_atime); - fd->ctime.SetUnix(st.st_ctime); -#endif + File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime); wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); #endif diff --git a/unrar/getbits.cpp b/unrar/getbits.cpp index e4db2695..8805f278 100644 --- a/unrar/getbits.cpp +++ b/unrar/getbits.cpp @@ -5,7 +5,7 @@ BitInput::BitInput(bool AllocBuffer) ExternalBuffer=false; if (AllocBuffer) { - // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // getbits*() attempt to read data from InAddr, ... InAddr+3 positions. // So let's allocate 3 additional bytes for situation, when we need to // read only 1 byte from the last position of buffer and avoid a crash // from access to next 3 bytes, which contents we do not need. diff --git a/unrar/getbits.hpp b/unrar/getbits.hpp index 2e151da9..00acbea9 100644 --- a/unrar/getbits.hpp +++ b/unrar/getbits.hpp @@ -28,26 +28,38 @@ class BitInput InAddr+=Bits>>3; InBit=Bits&7; } - + // Return 16 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. uint getbits() { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + uint32 BitField=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); + BitField >>= (16-InBit); +#else uint BitField=(uint)InBuf[InAddr] << 16; BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+2]; BitField >>= (8-InBit); +#endif return BitField & 0xffff; } + // Return 32 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. uint getbits32() { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + uint32 BitField=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); +#else uint BitField=(uint)InBuf[InAddr] << 24; BitField|=(uint)InBuf[InAddr+1] << 16; BitField|=(uint)InBuf[InAddr+2] << 8; BitField|=(uint)InBuf[InAddr+3]; +#endif BitField <<= InBit; BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); return BitField & 0xffffffff; diff --git a/unrar/hash.cpp b/unrar/hash.cpp index a4559e05..106cc608 100644 --- a/unrar/hash.cpp +++ b/unrar/hash.cpp @@ -26,7 +26,7 @@ void HashValue::Init(HASH_TYPE Type) } -bool HashValue::operator == (const HashValue &cmp) +bool HashValue::operator == (const HashValue &cmp) const { if (Type==HASH_NONE || cmp.Type==HASH_NONE) return true; diff --git a/unrar/hash.hpp b/unrar/hash.hpp index b7d879f6..6315680e 100644 --- a/unrar/hash.hpp +++ b/unrar/hash.hpp @@ -6,8 +6,14 @@ enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; struct HashValue { void Init(HASH_TYPE Type); - bool operator == (const HashValue &cmp); - bool operator != (const HashValue &cmp) {return !(*this==cmp);} + + // Use the const member, so types on both sides of "==" match. + // Otherwise clang -std=c++20 issues "ambiguity is between a regular call + // to this operator and a call with the argument order reversed" warning. + bool operator == (const HashValue &cmp) const; + + // Not actually used now. Const member for same reason as operator == above. + bool operator != (const HashValue &cmp) const {return !(*this==cmp);} HASH_TYPE Type; union diff --git a/unrar/headers.cpp b/unrar/headers.cpp index b042dc39..b441376c 100644 --- a/unrar/headers.cpp +++ b/unrar/headers.cpp @@ -49,13 +49,5 @@ FileHeader& FileHeader::operator = (FileHeader &hd) void MainHeader::Reset() { - HighPosAV=0; - PosAV=0; - CommentInHeader=false; - PackComment=false; - Locator=false; - QOpenOffset=0; - QOpenMaxSize=0; - RROffset=0; - RRMaxSize=0; + *this={}; } diff --git a/unrar/headers.hpp b/unrar/headers.hpp index 6af453a9..9f865713 100644 --- a/unrar/headers.hpp +++ b/unrar/headers.hpp @@ -6,7 +6,7 @@ #define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. #define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. #define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. -#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size. #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 @@ -162,12 +162,16 @@ struct MainHeader:BaseBlock ushort HighPosAV; uint PosAV; bool CommentInHeader; - bool PackComment; // For RAR 1.4 archive format only. + bool PackComment; // For RAR 1.4 archive format only. bool Locator; - uint64 QOpenOffset; // Offset of quick list record. - uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. - uint64 RROffset; // Offset of recovery record. - uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + uint64 QOpenOffset; // Offset of quick list record. + uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. + uint64 RROffset; // Offset of recovery record. + uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field. + std::wstring OrigName; // Original archive name. + RarTime OrigCtime; // Original archive creation time. + void Reset(); }; diff --git a/unrar/headers5.hpp b/unrar/headers5.hpp index 9ea8d979..50f5955d 100644 --- a/unrar/headers5.hpp +++ b/unrar/headers5.hpp @@ -59,11 +59,18 @@ // Main header extra field values. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. +#define MHEXTRA_METADATA 0x02 // Archive metadata. // Flags for MHEXTRA_LOCATOR. #define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. #define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. +// Flags for MHEXTRA_METADATA. +#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present. +#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present. +#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format. +#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision. + // File and service header extra field values. #define FHEXTRA_CRYPT 0x01 // Encryption parameters. #define FHEXTRA_HASH 0x02 // File hash. diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index f85472c3..3cc876b9 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -40,7 +40,7 @@ static bool WMI_IsWindows10() IWbemServices *pSvc = NULL; - hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc); + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc); if (FAILED(hres)) { diff --git a/unrar/list.cpp b/unrar/list.cpp index e5052c01..2defd27a 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -36,6 +36,7 @@ void ListArchive(CommandData *Cmd) { Arc.ViewComment(); mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: ",St(MListDetails)); uint SetCount=0; const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); @@ -61,6 +62,16 @@ void ListArchive(CommandData *Cmd) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); if (Arc.Encrypted) mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + + if (!Arc.MainHead.OrigName.empty()) + mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str()); + if (Arc.MainHead.OrigCtime.IsSet()) + { + wchar DateStr[50]; + Arc.MainHead.OrigCtime.GetText(DateStr,ASIZE(DateStr),Technical); + mprintf(L"\n%s: %s",St(MOriginalTime),DateStr); + } + mprintf(L"\n"); } diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index ad7d0c92..8c7c0881 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -28,6 +28,7 @@ #define MRARTitle1 L"\nUsage: rar - - " #define MUNRARTitle1 L"\nUsage: unrar - - " #define MRARTitle2 L"\n <@listfiles...> " +#define MFwrSlTitle2 L"\n <@listfiles...> " #define MCHelpCmd L"\n\n" #define MCHelpCmdA L"\n a Add files to archive" #define MCHelpCmdC L"\n c Add archive comment" @@ -58,6 +59,7 @@ #define MCHelpSwAD L"\n ad[1,2] Alternate destination path" #define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" #define MCHelpSwAI L"\n ai Ignore file attributes" +#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]" #define MCHelpSwAO L"\n ao Add files with Archive attribute set" #define MCHelpSwAP L"\n ap Set path inside archive" #define MCHelpSwAS L"\n as Synchronize archive contents" @@ -394,3 +396,6 @@ #define MAdjustValue L"\nAdjusting %s value to %s." #define MOpFailed L"\nOperation failed" #define MSkipEncArc L"\nSkipping the encrypted archive %s" +#define MOrigName L"Original name" +#define MOriginalTime L"Original time" +#define MFileRenamed L"\n%s is renamed to %s" diff --git a/unrar/makefile b/unrar/makefile index ace156c7..8e98a2c6 100644 --- a/unrar/makefile +++ b/unrar/makefile @@ -126,7 +126,7 @@ OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filc archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \ resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \ rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \ - list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o + list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o .cpp.o: $(COMPILE) -D$(WHAT) -c $< diff --git a/unrar/options.cpp b/unrar/options.cpp index 40323be8..22ae27ce 100644 --- a/unrar/options.cpp +++ b/unrar/options.cpp @@ -6,14 +6,6 @@ RAROptions::RAROptions() } -RAROptions::~RAROptions() -{ - // It is important for security reasons, so we do not have the unnecessary - // password data left in memory. - memset(this,0,sizeof(RAROptions)); -} - - void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); diff --git a/unrar/options.hpp b/unrar/options.hpp index 11319582..e249eb59 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -45,6 +45,12 @@ enum OVERWRITE_MODE OVERWRITE_FORCE_ASK }; +enum ARC_METADATA +{ + ARCMETA_NONE=0, + ARCMETA_SAVE, // -ams + ARCMETA_RESTORE // -amr +}; enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS }; @@ -84,11 +90,12 @@ struct FilterMode #define MAX_GENERATE_MASK 128 +// Here we store simple data types, which we can clear and move all together +// quickly. Rest of data types goes to CommandData. class RAROptions { public: RAROptions(); - ~RAROptions(); void Init(); uint ExclFileAttr; @@ -118,7 +125,6 @@ class RAROptions wchar ArcPath[NM]; // For -ap. wchar ExclArcPath[NM]; // For -ep4 switch. - SecPassword Password; bool EncryptHeaders; bool SkipEncrypted; @@ -132,6 +138,7 @@ class RAROptions HASH_TYPE HashType; int Recovery; int RecVolNumber; + ARC_METADATA ArcMetadata; bool DisablePercentage; bool DisableCopyright; bool DisableDone; @@ -147,7 +154,6 @@ class RAROptions PATH_EXCL_MODE ExclPath; RECURSE_MODE Recurse; int64 VolSize; - Array NextVolSizes; uint CurVolNum; bool AllYes; bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. diff --git a/unrar/os.hpp b/unrar/os.hpp index 7df3c6c0..4b21e49d 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -13,6 +13,8 @@ #endif #include +#include +#include #if defined(_WIN_ALL) || defined(_EMX) @@ -39,8 +41,15 @@ #define _UNICODE // Set _T() macro to convert from narrow to wide strings. #endif +#if 0 +// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR, +// but still keep SFX modules XP compatible. +#define WINVER _WIN32_WINNT_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#else #define WINVER _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP +#endif #if !defined(ZIPSFX) #define RAR_SMP @@ -72,9 +81,6 @@ #include #endif #ifdef _MSC_VER - #if _MSC_VER<1500 - #define for if (0) ; else for - #endif #include #include @@ -98,7 +104,6 @@ #include #include - #define SAVE_LINKS #define ENABLE_ACCESS diff --git a/unrar/qopen.cpp b/unrar/qopen.cpp index 43346b06..d906d06b 100644 --- a/unrar/qopen.cpp +++ b/unrar/qopen.cpp @@ -97,7 +97,7 @@ void QuickOpen::Load(uint64 BlockPos) if (Arc->SubHead.Encrypted) { - RAROptions *Cmd=Arc->GetRAROptions(); + CommandData *Cmd=Arc->GetCommandData(); #ifndef RAR_NOCRYPT if (Cmd->Password.IsSet()) Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, diff --git a/unrar/rar.hpp b/unrar/rar.hpp index 20e32fb2..67edb673 100644 --- a/unrar/rar.hpp +++ b/unrar/rar.hpp @@ -12,6 +12,7 @@ #include "version.hpp" #include "rardefs.hpp" #include "rarlang.hpp" +#include "rawint.hpp" #include "unicode.hpp" #include "errhnd.hpp" #include "secpassword.hpp" @@ -34,7 +35,6 @@ #endif #include "file.hpp" #include "crc.hpp" -#include "ui.hpp" #include "filefn.hpp" #include "filestr.hpp" #include "find.hpp" @@ -47,11 +47,11 @@ #include "archive.hpp" #include "match.hpp" #include "cmddata.hpp" +#include "ui.hpp" #include "filcreat.hpp" #include "consio.hpp" #include "system.hpp" #include "log.hpp" -#include "rawint.hpp" #include "rawread.hpp" #include "encname.hpp" #include "resource.hpp" diff --git a/unrar/rardefs.hpp b/unrar/rardefs.hpp index 095792a0..6858d39c 100644 --- a/unrar/rardefs.hpp +++ b/unrar/rardefs.hpp @@ -9,9 +9,13 @@ #define ASIZE(x) (sizeof(x)/sizeof(x[0])) -// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16) -// for CryptProtectMemory in SecPassword. -#define MAXPASSWORD 128 +// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of +// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword. +// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords +// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR +// to 128 for compatibility and because it is enough for AES-256. +#define MAXPASSWORD 512 +#define MAXPASSWORD_RAR 128 #define MAXSFXSIZE 0x200000 diff --git a/unrar/rawint.hpp b/unrar/rawint.hpp index 30379888..c8cd86fc 100644 --- a/unrar/rawint.hpp +++ b/unrar/rawint.hpp @@ -84,7 +84,7 @@ inline uint32 RawGetBE4(const byte *m) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) return _byteswap_ulong(*(uint32 *)m); -#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) return __builtin_bswap32(*(uint32 *)m); #else return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; @@ -97,7 +97,7 @@ inline void RawPutBE4(uint32 i,byte *mem) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) *(uint32*)mem = _byteswap_ulong(i); -#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) *(uint32*)mem = __builtin_bswap32(i); #else mem[0]=byte(i>>24); @@ -112,7 +112,7 @@ inline uint32 ByteSwap32(uint32 i) { #ifdef _MSC_VER return _byteswap_ulong(i); -#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +#elif defined(__clang__) || defined(__GNUC__) return __builtin_bswap32(i); #else return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); diff --git a/unrar/rdwrfn.cpp b/unrar/rdwrfn.cpp index 5e13ae65..fa713760 100644 --- a/unrar/rdwrfn.cpp +++ b/unrar/rdwrfn.cpp @@ -155,7 +155,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { #ifdef RARDLL - RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions(); + CommandData *Cmd=((Archive *)SrcFile)->GetCommandData(); if (Cmd->DllOpMode!=RAR_SKIP) { if (Cmd->Callback!=NULL && @@ -204,7 +204,7 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) ArcPos+=ProcessedArcSize; Archive *SrcArc=(Archive *)SrcFile; - RAROptions *Cmd=SrcArc->GetRAROptions(); + CommandData *Cmd=SrcArc->GetCommandData(); int CurPercent=ToPercent(ArcPos,ArcSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) diff --git a/unrar/recvol.cpp b/unrar/recvol.cpp index adf58404..b1782071 100644 --- a/unrar/recvol.cpp +++ b/unrar/recvol.cpp @@ -5,7 +5,7 @@ -bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent) { Archive Arc(Cmd); if (!Arc.Open(Name)) @@ -42,7 +42,7 @@ bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) } -void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) { wchar RevName[NM]; *RevName=0; diff --git a/unrar/recvol.hpp b/unrar/recvol.hpp index 06510a21..4a6d663f 100644 --- a/unrar/recvol.hpp +++ b/unrar/recvol.hpp @@ -14,11 +14,11 @@ class RecVolumes3 ThreadPool *RSThreadPool; #endif public: - RecVolumes3(RAROptions *Cmd,bool TestOnly); + RecVolumes3(CommandData *Cmd,bool TestOnly); ~RecVolumes3(); - void Make(RAROptions *Cmd,wchar *ArcName); - bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); - void Test(RAROptions *Cmd,const wchar *Name); + void Make(CommandData *Cmd,wchar *ArcName); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); }; @@ -48,8 +48,8 @@ struct RecRSThreadData class RecVolumes5 { private: - void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); - void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode); + void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); + void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode); uint ReadHeader(File *RecFile,bool FirstRev); Array RecItems; @@ -76,13 +76,13 @@ class RecVolumes5 public: // 'public' only because called from thread functions. void ProcessAreaRS(RecRSThreadData *td); public: - RecVolumes5(RAROptions *Cmd,bool TestOnly); + RecVolumes5(CommandData *Cmd,bool TestOnly); ~RecVolumes5(); - bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); - void Test(RAROptions *Cmd,const wchar *Name); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); }; -bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent); -void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name); +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent); +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name); #endif diff --git a/unrar/recvol3.cpp b/unrar/recvol3.cpp index 9fb846a2..ecf6dd35 100644 --- a/unrar/recvol3.cpp +++ b/unrar/recvol3.cpp @@ -36,7 +36,7 @@ THREAD_PROC(RSDecodeThread) } #endif -RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly) +RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly) { memset(SrcFile,0,sizeof(SrcFile)); if (TestOnly) @@ -99,7 +99,7 @@ static bool IsNewStyleRev(const wchar *Name) } -bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); @@ -497,7 +497,7 @@ void RSEncode::DecodeBuf() } -void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name) +void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) { if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32. { diff --git a/unrar/recvol5.cpp b/unrar/recvol5.cpp index 596ea06f..2d9c9471 100644 --- a/unrar/recvol5.cpp +++ b/unrar/recvol5.cpp @@ -4,7 +4,7 @@ static const uint MaxVolumes=65535; // rev files by mistake. #define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. -RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) +RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly) { RealBuf=NULL; RealReadBuffer=NULL; @@ -70,7 +70,7 @@ THREAD_PROC(RecThreadRS) #endif -void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) +void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) { /* RSCoder16 RS; @@ -141,7 +141,7 @@ void RecVolumes5::ProcessAreaRS(RecRSThreadData *td) -bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); @@ -494,7 +494,7 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) } -void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name) +void RecVolumes5::Test(CommandData *Cmd,const wchar *Name) { wchar VolName[NM]; wcsncpyz(VolName,Name,ASIZE(VolName)); diff --git a/unrar/rijndael.cpp b/unrar/rijndael.cpp index 48ac1723..02c4d147 100644 --- a/unrar/rijndael.cpp +++ b/unrar/rijndael.cpp @@ -90,18 +90,20 @@ Rijndael::Rijndael() void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { -#ifdef USE_SSE - // Check SSE here instead of constructor, so if object is a part of some - // structure memset'ed before use, this variable is not lost. + // Check SIMD here instead of constructor, so if object is a part of some + // structure memset'ed before use, these variables are not lost. +#if defined(USE_SSE) int CPUInfo[4]; - __cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function. - if ((CPUInfo[0] & 0x7fffffff)>=1) + __cpuid(CPUInfo, 0); + if (CPUInfo[0]>=1) // Check the maximum supported cpuid function. { __cpuid(CPUInfo, 1); AES_NI=(CPUInfo[2] & 0x2000000)!=0; } else AES_NI=false; +#elif defined(USE_NEON) + AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; #endif // Other developers asked us to initialize it to suppress "may be used @@ -141,18 +143,25 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe keyEncToDec(); } + void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) { if (inputLen <= 0) return; size_t numBlocks = inputLen/16; -#ifdef USE_SSE +#if defined(USE_SSE) if (AES_NI) { blockEncryptSSE(input,numBlocks,outBuffer); return; } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockEncryptNeon(input,numBlocks,outBuffer); + return; + } #endif byte *prevBlock = m_initVector; @@ -239,6 +248,40 @@ void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffe } #endif + +#ifdef USE_NEON +void Rijndael::blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer) +{ + byte *prevBlock = m_initVector; + while (numBlocks > 0) + { + byte block[16]; + if (CBCMode) + vst1q_u8(block, veorq_u8(vld1q_u8(prevBlock), vld1q_u8(input))); + else + vst1q_u8(block, vld1q_u8(input)); + + uint8x16_t data = vld1q_u8(block); + for (uint i = 0; i < m_uRounds-1; i++) + { + data = vaeseq_u8(data, vld1q_u8((byte *)m_expandedKey[i])); + data = vaesmcq_u8(data); + } + data = vaeseq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds-1]))); + data = veorq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds]))); + vst1q_u8(outBuffer, data); + + prevBlock=outBuffer; + + outBuffer += 16; + input += 16; + numBlocks--; + } + vst1q_u8(m_initVector, vld1q_u8(prevBlock)); + return; +} +#endif + void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer) { @@ -246,12 +289,18 @@ void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer) return; size_t numBlocks=inputLen/16; -#ifdef USE_SSE +#if defined(USE_SSE) if (AES_NI) { blockDecryptSSE(input,numBlocks,outBuffer); return; } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockDecryptNeon(input,numBlocks,outBuffer); + return; + } #endif byte block[16], iv[4][4]; @@ -343,6 +392,41 @@ void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuf #endif +#ifdef USE_NEON +void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer) +{ + byte iv[16]; + memcpy(iv,m_initVector,16); + + while (numBlocks > 0) + { + uint8x16_t data = vld1q_u8(input); + + for (int i=m_uRounds-1; i>0; i--) + { + data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1])); + data = vaesimcq_u8(data); + } + + data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1])); + data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0])); + + if (CBCMode) + data = veorq_u8(data, vld1q_u8(iv)); + + vst1q_u8(iv, vld1q_u8(input)); + vst1q_u8(outBuffer, data); + + input += 16; + outBuffer += 16; + numBlocks--; + } + + memcpy(m_initVector,iv,16); +} +#endif + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ALGORITHM ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/unrar/rijndael.hpp b/unrar/rijndael.hpp index 797899ff..96e1d0d1 100644 --- a/unrar/rijndael.hpp +++ b/unrar/rijndael.hpp @@ -18,6 +18,16 @@ class Rijndael bool AES_NI; #endif +#ifdef USE_NEON + // Set "crypto" attribute as replacement of -march=armv8-a+crypto switch. + __attribute__((target("crypto"))) + void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer); + __attribute__((target("crypto"))) + void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer); + + bool AES_Neon; +#endif + void keySched(byte key[_MAX_KEY_COLUMNS][4]); void keyEncToDec(); void GenerateTables(); diff --git a/unrar/scantree.cpp b/unrar/scantree.cpp index a13a3ebc..dbaf1e42 100644 --- a/unrar/scantree.cpp +++ b/unrar/scantree.cpp @@ -215,10 +215,21 @@ bool ScanTree::GetNextMask() UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); #endif - // We wish to scan entire disk if mask like c:\ is specified - // regardless of recursion mode. Use c:\*.* mask when need to scan only - // the root directory. - ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + // We prefer to scan entire disk if mask like \\server\share\ or c:\ + // is specified regardless of recursion mode. Use \\server\share\*.* + // or c:\*.* mask to scan only the root directory. + if (CurMask[0]=='\\' && CurMask[1]=='\\') + { + const wchar *Slash=wcschr(CurMask+2,'\\'); + if (Slash!=NULL) + { + Slash=wcschr(Slash+1,'\\'); + ScanEntireDisk=Slash!=NULL && *(Slash+1)==0; + } + } + else + ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + wchar *Name=PointToName(CurMask); if (*Name==0) diff --git a/unrar/secpassword.cpp b/unrar/secpassword.cpp index b99e53af..7eddea89 100644 --- a/unrar/secpassword.cpp +++ b/unrar/secpassword.cpp @@ -56,7 +56,6 @@ static CryptLoader GlobalCryptLoader; SecPassword::SecPassword() { - CrossProcess=false; Set(L""); } @@ -70,7 +69,8 @@ SecPassword::~SecPassword() void SecPassword::Clean() { PasswordSet=false; - cleandata(Password,sizeof(Password)); + if (Password.size()>0) + cleandata(&Password[0],Password.size()); } @@ -104,7 +104,7 @@ void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstS // Source string can be shorter than destination as in case when we process // -p parameter, so we need to take into account both sizes. memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); - SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess); + SecHideData(Dst,DstSize*sizeof(*Dst),Encode,false); } @@ -112,7 +112,7 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize) { if (PasswordSet) { - Process(Password,ASIZE(Password),Psw,MaxSize,false); + Process(&Password[0],Password.size(),Psw,MaxSize,false); Psw[MaxSize-1]=0; } else @@ -127,12 +127,12 @@ void SecPassword::Set(const wchar *Psw) if (*Psw==0) { PasswordSet=false; - memset(Password,0,sizeof(Password)); + std::fill(Password.begin(), Password.end(), 0); } else { PasswordSet=true; - Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true); + Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true); } } @@ -163,6 +163,9 @@ bool SecPassword::operator == (SecPassword &psw) } +// Set CrossProcess to true if we need to pass a password to another process. +// We use CrossProcess when transferring parameters to UAC elevated WinRAR +// and Windows GUI SFX modules. void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) { // CryptProtectMemory is not available in UWP and CryptProtectData diff --git a/unrar/secpassword.hpp b/unrar/secpassword.hpp index 375d3887..5284bce1 100644 --- a/unrar/secpassword.hpp +++ b/unrar/secpassword.hpp @@ -8,10 +8,7 @@ class SecPassword private: void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); - wchar Password[MAXPASSWORD]; - - // It is important to have this 'bool' value, so if our object is cleaned - // with memset as a part of larger structure, it is handled correctly. + std::vector Password = std::vector(MAXPASSWORD); bool PasswordSet; public: SecPassword(); @@ -22,10 +19,6 @@ class SecPassword bool IsSet() {return PasswordSet;} size_t Length(); bool operator == (SecPassword &psw); - - // Set to true if we need to pass a password to another process. - // We use it when transferring parameters to UAC elevated WinRAR. - bool CrossProcess; }; diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index f53c826a..7617f7a5 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -357,6 +357,32 @@ void itoa(int64 n,wchar *Str,size_t MaxSize) } +// Convert the number to string using thousand separators. +void fmtitoa(int64 n,wchar *Str,size_t MaxSize) +{ + static wchar ThSep=0; // Thousands separator. +#ifdef _WIN_ALL + wchar Info[10]; + if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0) + ThSep=*Info; +#elif defined(_UNIX) + ThSep=*localeconv()->thousands_sep; +#endif + if (ThSep==0) // If failed to detect the actual separator value. + ThSep=' '; + wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int. + itoa(n,RawText,ASIZE(RawText)); + uint S=0,D=0,L=wcslen(RawText)%3; + while (RawText[S]!=0 && D+1=7) { diff --git a/unrar/threadmisc.cpp b/unrar/threadmisc.cpp index 742eda41..7a6ec782 100644 --- a/unrar/threadmisc.cpp +++ b/unrar/threadmisc.cpp @@ -149,3 +149,5 @@ uint GetNumberOfThreads() return NumCPU; } + + diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 8fc76aa8..5def26df 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -49,7 +49,7 @@ enum UIMESSAGE_CODE { UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, - UIMSG_SKIPENCARC, + UIMSG_SKIPENCARC, UIMSG_FILERENAME, UIWAIT_FIRST, UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, @@ -77,7 +77,7 @@ enum UIASKREP_RESULT { }; UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); -UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); void uiInit(SOUND_NOTIFY_MODE Sound); @@ -88,7 +88,7 @@ void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd); bool uiIsGlobalPasswordSet(); enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; @@ -145,30 +145,31 @@ class uiMsgStore // Templates recognize usual NULL as integer, not wchar*. #define UINULL ((wchar *)NULL) -inline void uiMsg(UIMESSAGE_CODE Code) +inline void uiMsgBase(uiMsgStore &Store) { - uiMsgStore Store(Code); - Store.Msg(); + // Called last, when no parameters are left. } -template void uiMsg(UIMESSAGE_CODE Code,T1 a1) +template void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN) { - uiMsgStore Store(Code); + // Process first parameter and pass the rest to same uiMsgBase. Store< void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2) -{ - uiMsgStore Store(Code); - Store< void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3) +// Use variadic templates. +// +// We must pass variable parameters by reference, so no temporary copies are +// created for custom string objects like CStringBase in 7-Zip decompression +// code. Such temporary copies would be destroyed inside of recursive +// uiMsgBase calls, leaving us with Str[] items pointing at released memory. +// Since we pass integer values as well, we can't use & references +// and must resort to && rvalue references. +template void uiMsg(UIMESSAGE_CODE Code,TN&&... aN) { - uiMsgStore Store(code); - Store<Overwrite==OVERWRITE_NONE) return UIASKREP_R_SKIP; diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index d713fac0..b1ac5132 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -262,6 +262,7 @@ void uiMsgStore::Msg() break; case UIERROR_MISSINGVOL: Log(Str[0],St(MAbsNextVol),Str[0]); + mprintf(L" "); // For progress percent. break; #ifndef SFX_MODULE case UIERROR_NEEDPREVVOL: @@ -395,7 +396,8 @@ void uiMsgStore::Msg() } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, + SecPassword *Password,CheckPassword *CheckPwd) { // Unlike GUI we cannot provide Cancel button here, so we use the empty // password to abort. Otherwise user not knowing a password would need to diff --git a/unrar/uisilent.cpp b/unrar/uisilent.cpp index 1df09756..81558857 100644 --- a/unrar/uisilent.cpp +++ b/unrar/uisilent.cpp @@ -33,7 +33,8 @@ void uiMsgStore::Msg() } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, + SecPassword *Password,CheckPassword *CheckPwd) { return false; } diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index d198f2e0..5f53e3a9 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -50,6 +50,26 @@ static bool IsFullPath(const char *PathA) // Unix ASCII version. } +// For security purpose we prefer to be sure that CharToWide completed +// successfully and even if it truncated a string for some reason, +// it didn't affect the number of path related characters we analyze +// in IsRelativeSymlinkSafe later. +// This check is likely to be excessive, but let's keep it anyway. +static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize) +{ + if (!CharToWide(Src,Dest,DestSize) || *Dest==0) + return false; + uint SrcChars=0,DestChars=0; + for (uint I=0;Src[I]!=0;I++) + if (Src[I]=='/' || Src[I]=='.') + SrcChars++; + for (uint I=0;Dest[I]!=0;I++) + if (Dest[I]=='/' || Dest[I]=='.') + DestChars++; + return SrcChars==DestChars; +} + + bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) { char Target[NM]; @@ -72,12 +92,12 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w return true; wchar TargetW[NM]; - CharToWide(Target,TargetW,ASIZE(TargetW)); - // Check for *TargetW==0 to catch CharToWide failure. + if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + return false; // Use Arc.FileHead.FileName instead of LinkName, since LinkName // can include the destination path as a prefix, which can // confuse IsRelativeSymlinkSafe algorithm. - if (!Cmd->AbsoluteLinks && (*TargetW==0 || IsFullPath(TargetW) || + if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) return false; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); @@ -100,11 +120,15 @@ bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) return false; DosSlashToUnix(Target,Target,ASIZE(Target)); } + + wchar TargetW[NM]; + if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + return false; // Use hd->FileName instead of LinkName, since LinkName can include // the destination path as a prefix, which can confuse // IsRelativeSymlinkSafe algorithm. - if (!Cmd->AbsoluteLinks && (IsFullPath(Target) || - !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW))) return false; return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index 5421923f..73b09bb2 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -229,10 +229,11 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success) #endif -// SrcSize is in wide characters, not in bytes. -byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) +// SrcSize is source data size in wide characters, not in bytes. +// DestSize is the maximum allowed destination size. +byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize) { - for (size_t I=0;I>8); diff --git a/unrar/unicode.hpp b/unrar/unicode.hpp index 8d433c1b..9bfd9c5d 100644 --- a/unrar/unicode.hpp +++ b/unrar/unicode.hpp @@ -7,7 +7,7 @@ bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); -byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize); +byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize); wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); size_t WideToUtfSize(const wchar *Src); diff --git a/unrar/unpack.cpp b/unrar/unpack.cpp index 037c3554..9236e748 100644 --- a/unrar/unpack.cpp +++ b/unrar/unpack.cpp @@ -309,7 +309,7 @@ void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) Dec->QuickBits=MAX_QUICK_DECODE_BITS; break; default: - Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; + Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0; break; } diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index 30a9a2ee..737b31c2 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -93,17 +93,17 @@ struct UnpackBlockTables #ifdef RAR_SMP enum UNP_DEC_TYPE { - UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER + UNPDT_LITERAL=0,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER }; struct UnpackDecodedItem { - UNP_DEC_TYPE Type; + byte Type; // 'byte' instead of enum type to reduce memory use. ushort Length; union { uint Distance; - byte Literal[4]; + byte Literal[8]; // Store up to 8 chars here to speed up extraction. }; }; diff --git a/unrar/unpack30.cpp b/unrar/unpack30.cpp index 6a8efa23..7c2adfab 100644 --- a/unrar/unpack30.cpp +++ b/unrar/unpack30.cpp @@ -55,7 +55,7 @@ void Unpack::Unpack29(bool Solid) if (!UnpReadBuf30()) break; } - if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) + if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr) { UnpWriteBuf30(); if (WrittenFileSize>DestUnpSize) diff --git a/unrar/unpack50.cpp b/unrar/unpack50.cpp index 99119507..e040907c 100644 --- a/unrar/unpack50.cpp +++ b/unrar/unpack50.cpp @@ -42,7 +42,7 @@ void Unpack::Unpack5(bool Solid) break; } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) @@ -93,7 +93,7 @@ void Unpack::Unpack5(bool Solid) } else { - Distance+=Inp.getbits32()>>(32-DBits); + Distance+=Inp.getbits()>>(16-DBits); Inp.addbits(DBits); } } diff --git a/unrar/unpack50mt.cpp b/unrar/unpack50mt.cpp index 691ac8e9..82c9c4a8 100644 --- a/unrar/unpack50mt.cpp +++ b/unrar/unpack50mt.cpp @@ -345,7 +345,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D) if (D.DecodedSize>1) { UnpackDecodedItem *PrevItem=CurItem-1; - if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3) + if (PrevItem->Type==UNPDT_LITERAL && PrevItem->LengthLiteral)-1) { PrevItem->Length++; PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; @@ -388,7 +388,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D) } else { - Distance+=D.Inp.getbits32()>>(32-DBits); + Distance+=D.Inp.getbits()>>(16-DBits); D.Inp.addbits(DBits); } } @@ -451,7 +451,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) while (ItemDestUnpSize) @@ -461,10 +461,10 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) if (Item->Type==UNPDT_LITERAL) { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) - if (Item->Length==3 && UnpPtrLength==7 && UnpPtrLiteral; - UnpPtr+=4; + *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal); + UnpPtr+=8; } else #endif @@ -559,7 +559,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) break; } } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) diff --git a/unrar/version.hpp b/unrar/version.hpp index fed120a5..5a349f0f 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 11 +#define RARVER_MINOR 20 #define RARVER_BETA 1 -#define RARVER_DAY 22 -#define RARVER_MONTH 2 +#define RARVER_DAY 25 +#define RARVER_MONTH 10 #define RARVER_YEAR 2022 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index 917851de..4924d8d0 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -1,15 +1,15 @@ #include "rar.hpp" #ifdef RARDLL -static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); -static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); +static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize); +static bool DllVolNotify(CommandData *Cmd,wchar *NextName); #endif bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { - RAROptions *Cmd=Arc.GetRAROptions(); + CommandData *Cmd=Arc.GetCommandData(); HEADER_TYPE HeaderType=Arc.GetHeaderType(); FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; @@ -190,7 +190,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma #ifdef RARDLL -bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) +bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize) { bool DllVolChanged=false,DllVolAborted=false; @@ -246,7 +246,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) #ifdef RARDLL -bool DllVolNotify(RAROptions *Cmd,wchar *NextName) +bool DllVolNotify(CommandData *Cmd,wchar *NextName) { char NextNameA[NM]; WideToChar(NextName,NextNameA,ASIZE(NextNameA)); diff --git a/unrar/volume.hpp b/unrar/volume.hpp index 2d6a6d5c..4ada1091 100644 --- a/unrar/volume.hpp +++ b/unrar/volume.hpp @@ -1,10 +1,7 @@ #ifndef _RAR_VOLUME_ #define _RAR_VOLUME_ -void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos, - ComprDataIO *DataIO); bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, wchar Command); -void SetVolWrite(Archive &Dest,int64 VolSize); #endif From 4594bd1cd1144088832adf2e59c02cdeb5a018b0 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:00 +0000 Subject: [PATCH 11/76] Added unrar 6.1.5 --- unrar/consio.cpp | 29 +++++++++++---- unrar/dll.rc | 8 ++-- unrar/extract.cpp | 5 ++- unrar/isnt.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++- unrar/isnt.hpp | 3 ++ unrar/makefile | 2 +- unrar/pathfn.cpp | 4 +- unrar/version.hpp | 8 ++-- 8 files changed, 132 insertions(+), 20 deletions(-) diff --git a/unrar/consio.cpp b/unrar/consio.cpp index 829442e9..fa35d614 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -171,18 +171,33 @@ static void GetPasswordText(wchar *Str,uint MaxLength) { #ifdef _WIN_ALL HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); - HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE); - DWORD ConInMode,ConOutMode; - DWORD Read=0; + DWORD ConInMode; GetConsoleMode(hConIn,&ConInMode); - GetConsoleMode(hConOut,&ConOutMode); - SetConsoleMode(hConIn,ENABLE_LINE_INPUT); - SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT); + SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. + // We prefer ReadConsole to ReadFile, so we can read Unicode input. + DWORD Read=0; ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); Str[Read]=0; SetConsoleMode(hConIn,ConInMode); - SetConsoleMode(hConOut,ConOutMode); + + // If entered password is longer than MAXPASSWORD and truncated, + // read its unread part anyway, so it isn't read later as the second + // password for -p switch. Low level FlushConsoleInputBuffer doesn't help + // for high level ReadConsole, which in line input mode seems to store + // the rest of string in its own internal buffer. + if (wcschr(Str,'\r')==NULL) // If '\r' is missing, the password was truncated. + while (true) + { + wchar Trail[64]; + DWORD TrailRead=0; + // Use ASIZE(Trail)-1 to reserve the space for trailing 0. + ReadConsole(hConIn,Trail,ASIZE(Trail)-1,&TrailRead,NULL); + Trail[TrailRead]=0; + if (TrailRead==0 || wcschr(Trail,'\r')!=NULL) + break; + } + #else char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters. #if defined(_EMX) || defined (__VMS) diff --git a/unrar/dll.rc b/unrar/dll.rc index 6d6b8288..ae9a964e 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 10, 100, 389 -PRODUCTVERSION 6, 10, 100, 389 +FILEVERSION 6, 11, 1, 418 +PRODUCTVERSION 6, 11, 1, 418 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", "6.10.0\0" - VALUE "ProductVersion", "6.10.0\0" + VALUE "FileVersion", "6.11.1\0" + VALUE "ProductVersion", "6.11.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index ad047998..dc109b9d 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -666,8 +666,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) { + wchar RedirName[NM]; + ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + wchar NameExisting[NM]; - ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting)); + ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index 305058dd..f85472c3 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -1,6 +1,5 @@ #include "rar.hpp" -#ifdef _WIN_ALL DWORD WinNT() { static int dwPlatformId=-1; @@ -22,4 +21,94 @@ DWORD WinNT() return Result; } -#endif + + +// Replace it with documented Windows 11 check when available. +#include +#include +#pragma comment(lib, "wbemuuid.lib") + +static bool WMI_IsWindows10() +{ + IWbemLocator *pLoc = NULL; + + HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER, + IID_IWbemLocator,(LPVOID *)&pLoc); + + if (FAILED(hres)) + return false; + + IWbemServices *pSvc = NULL; + + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc); + + if (FAILED(hres)) + { + pLoc->Release(); + return false; + } + + hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL, + RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IEnumWbemClassObject *pEnumerator = NULL; + hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + bool Win10=false; + while (pEnumerator!=NULL) + { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (uReturn==0) + break; + + VARIANT vtProp; + + hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); + Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL; + VariantClear(&vtProp); + + pclsObj->Release(); + } + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + + return Win10; +} + + +// Replace it with actual check when available. +bool IsWindows11OrGreater() +{ + static bool IsSet=false,IsWin11=false; + if (!IsSet) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + IsWin11=WinVer.dwMajorVersion>10 || + WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); + IsSet=true; + } + return IsWin11; +} diff --git a/unrar/isnt.hpp b/unrar/isnt.hpp index 85790da4..fed0b517 100644 --- a/unrar/isnt.hpp +++ b/unrar/isnt.hpp @@ -10,4 +10,7 @@ enum WINNT_VERSION { DWORD WinNT(); +// Replace it with actual check when available. +bool IsWindows11OrGreater(); + #endif diff --git a/unrar/makefile b/unrar/makefile index 214f87ef..ace156c7 100644 --- a/unrar/makefile +++ b/unrar/makefile @@ -123,7 +123,7 @@ UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o LIB_OBJ=filestr.o scantree.o dll.o qopen.o OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \ - archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \ + archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \ resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \ rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \ list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 3e321285..983bd743 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -1044,7 +1044,9 @@ void MakeNameCompatible(wchar *Name,size_t MaxSize) else if (Devices[J][K]==0) { - MatchFound=s[K]==0 || s[K]=='.' || IsPathDiv(s[K]); + // Names like aux.txt are accessible without \\?\ prefix + // since Windows 11. Pure aux is still prohibited. + MatchFound=s[K]==0 || s[K]=='.' && !IsWindows11OrGreater() || IsPathDiv(s[K]); break; } else diff --git a/unrar/version.hpp b/unrar/version.hpp index ea242029..fed120a5 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 10 -#define RARVER_BETA 0 -#define RARVER_DAY 24 -#define RARVER_MONTH 1 +#define RARVER_MINOR 11 +#define RARVER_BETA 1 +#define RARVER_DAY 22 +#define RARVER_MONTH 2 #define RARVER_YEAR 2022 From 412c7bf0240cd07f36649ded2574db45f990d2d4 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:01 +0000 Subject: [PATCH 12/76] Added unrar 6.2.2 --- unrar/dll.rc | 8 ++++---- unrar/extract.cpp | 12 +++++++++++- unrar/headers.hpp | 2 +- unrar/secpassword.cpp | 11 +++++------ unrar/version.hpp | 6 +++--- unrar/win32stm.cpp | 7 +++++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index b2dcdc9b..5d9c6ac5 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 20, 1, 663 -PRODUCTVERSION 6, 20, 1, 663 +FILEVERSION 6, 20, 2, 681 +PRODUCTVERSION 6, 20, 2, 681 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", "6.20.1\0" - VALUE "ProductVersion", "6.20.1\0" + VALUE "FileVersion", "6.20.2\0" + VALUE "ProductVersion", "6.20.2\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 96ad528b..406b1a01 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -765,7 +765,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } else { - uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName); + uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName); LinkSuccess=false; } @@ -1028,6 +1028,16 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) { + if (Cmd->Test) + { + // Destination name conversion isn't needed for simple archive test. + // This check also allows to avoid issuing "Attempting to correct... + // Renaming..." messages in MakeNameCompatible() below for problematic + // names like aux.txt when testing an archive. + wcsncpyz(DestName,ArcFileName,DestSize); + return; + } + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); if (*Cmd->ExtrPath!=0) diff --git a/unrar/headers.hpp b/unrar/headers.hpp index 9f865713..8a16a457 100644 --- a/unrar/headers.hpp +++ b/unrar/headers.hpp @@ -234,7 +234,7 @@ struct FileHeader:BlockHeader bool LargeFile; // 'true' for HEAD_SERVICE block, which is a child of preceding file block. - // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. + // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives. bool SubBlock; HOST_SYSTEM_TYPE HSType; diff --git a/unrar/secpassword.cpp b/unrar/secpassword.cpp index 7eddea89..42ed47d5 100644 --- a/unrar/secpassword.cpp +++ b/unrar/secpassword.cpp @@ -124,12 +124,11 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize) void SecPassword::Set(const wchar *Psw) { - if (*Psw==0) - { - PasswordSet=false; - std::fill(Password.begin(), Password.end(), 0); - } - else + // Eliminate any traces of previously stored password for security reason + // in case it was longer than new one. + Clean(); + + if (*Psw!=0) { PasswordSet=true; Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true); diff --git a/unrar/version.hpp b/unrar/version.hpp index 5a349f0f..2749f7f3 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 20 -#define RARVER_BETA 1 -#define RARVER_DAY 25 -#define RARVER_MONTH 10 +#define RARVER_BETA 2 +#define RARVER_DAY 12 +#define RARVER_MONTH 11 #define RARVER_YEAR 2022 diff --git a/unrar/win32stm.cpp b/unrar/win32stm.cpp index eaa43be2..9907de70 100644 --- a/unrar/win32stm.cpp +++ b/unrar/win32stm.cpp @@ -111,16 +111,19 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) wcsncatz(FullName,StreamName,ASIZE(FullName)); + FindData fd; - bool Found=FindFile::FastFind(FileName,&fd); + bool HostFound=FindFile::FastFind(FileName,&fd); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile,false)) CurFile.Close(); + + // 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); From 571deeda60ec9b81e65289c580d3a4f45bc70dbe Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:02 +0000 Subject: [PATCH 13/76] Added unrar 6.2.3 --- unrar/arcread.cpp | 4 +- unrar/crypt.hpp | 5 +-- unrar/dll.rc | 8 ++-- unrar/extinfo.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++--- unrar/extinfo.hpp | 3 +- unrar/extract.cpp | 47 ++++++++++++++++++++++-- unrar/extract.hpp | 6 +++ unrar/hardlinks.cpp | 2 - unrar/model.cpp | 6 ++- unrar/pathfn.cpp | 14 +++++-- unrar/timefn.hpp | 11 ++++++ unrar/ulinks.cpp | 6 ++- unrar/version.hpp | 6 +-- unrar/win32stm.cpp | 8 +++- 14 files changed, 180 insertions(+), 35 deletions(-) diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 3d12684e..c533c4a1 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -1476,7 +1476,9 @@ bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) { if (SubHead.UnpSize>0x1000000) { - // So huge allocation must never happen in valid archives. + // Prevent the excessive allocation. When reading to memory, normally + // this function operates with reasonably small blocks, such as + // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream. uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); return false; } diff --git a/unrar/crypt.hpp b/unrar/crypt.hpp index 22d18e77..9dc02159 100644 --- a/unrar/crypt.hpp +++ b/unrar/crypt.hpp @@ -15,10 +15,7 @@ enum CRYPT_METHOD { #define CRYPT_BLOCK_SIZE 16 #define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf -// 2013.04.29: set to 15 for RAR 5.00 beta 1. -// 2022.09.07: changed to 16 for upcoming RAR 6.20. -#define CRYPT5_KDF_LG2_COUNT 16 // LOG2 of PDKDF2 iteration count. - +#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. #define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. #define CRYPT_VERSION 0 // Supported encryption version. diff --git a/unrar/dll.rc b/unrar/dll.rc index 5d9c6ac5..f67dc63e 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 20, 2, 681 -PRODUCTVERSION 6, 20, 2, 681 +FILEVERSION 6, 20, 3, 714 +PRODUCTVERSION 6, 20, 3, 714 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", "6.20.2\0" - VALUE "ProductVersion", "6.20.2\0" + VALUE "FileVersion", "6.20.3\0" + VALUE "ProductVersion", "6.20.3\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extinfo.cpp b/unrar/extinfo.cpp index 5cb90a40..0f25f312 100644 --- a/unrar/extinfo.cpp +++ b/unrar/extinfo.cpp @@ -112,6 +112,68 @@ static bool LinkInPath(const wchar *Name) } +// Delete symbolic links in file path, if any, and replace them by directories. +// Prevents extracting files outside of destination folder with symlink chains. +bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked) +{ + // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like + // "lnk1/../dir", but converts the path to "dir". In Unix we need to call + // this function to prevent placing unpacked files outside of destination + // folder if previously we unpacked "dir/lnk1" -> "..", + // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". + // We may still need this function to prevent abusing symlink chains + // in link source path if we remove detection of such chains + // in IsRelativeSymlinkSafe. This function seems to make other symlink + // related safety checks redundant, but for now we prefer to keep them too. + // + // 2022.12.01: the performance impact is minimized after adding the check + // against the previous path and enabling this verification only after + // extracting a symlink with ".." in target. So we enabled it for Windows + // as well for extra safety. +//#ifdef _UNIX + wchar Path[NM]; + if (wcslen(SrcName)>=ASIZE(Path)) + return false; // It should not be that long, skip. + wcsncpyz(Path,SrcName,ASIZE(Path)); + + size_t SkipLength=wcslen(SkipPart); + + if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0) + SkipLength=0; // Parameter validation, not really needed now. + + // Do not check parts already checked in previous path to improve performance. + for (uint I=0;Path[I]!=0 && ISkipLength) + SkipLength=I; + + wchar *Name=Path; + if (SkipLength>0) + { + // Avoid converting symlinks in destination path part specified by user. + Name+=SkipLength; + while (IsPathDiv(*Name)) + Name++; + } + + for (wchar *s=Path+wcslen(Path)-1;s>Name;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) +#ifdef _WIN_ALL + if (!DelDir(Path)) +#else + if (!DelFile(Path)) +#endif + return false; // Couldn't delete the symlink to replace it with directory. + } + LastChecked=SrcName; +//#endif + return true; +} + + bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) { // Catch root dir based /path/file paths also as stuff like \\?\. @@ -131,10 +193,14 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr UpLevels++; TargetName++; } - // If link target includes "..", it must not have another links - // in the path, because they can bypass our safety check. For example, + // If link target includes "..", it must not have another links in its + // source path, because they can bypass our safety check. For example, // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next - // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. + // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and + // file "dir/lnk1/lnk2/poc.txt" last. + // Do not confuse with link chains in target, this is in link source path. + // It is important for Windows too, though this check can be omitted + // if LinksToDirs is invoked in Windows as well. if (UpLevels>0 && LinkInPath(PrepSrcName)) return false; @@ -160,15 +226,26 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr } -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink) { + // Returning true in Uplink indicates that link target might include ".." + // and enables additional checks. It is ok to falsely return true here, + // as it implies only the minor performance penalty. But we shall always + // return true for links with ".." in target for security reason. + + UpLink=true; // Assume the target might include potentially unsafe "..". +#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL) + if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows. + UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL; +#endif + #if defined(SAVE_LINKS) && defined(_UNIX) // For RAR 3.x archives we process links even in test mode to skip link data. if (Arc.Format==RARFMT15) - return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink); if (Arc.Format==RARFMT50) return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); -#elif defined _WIN_ALL +#elif defined(_WIN_ALL) // RAR 5.0 archives store link information in file header, so there is // no need to additionally test it if we do not create a file. if (Arc.Format==RARFMT50) diff --git a/unrar/extinfo.hpp b/unrar/extinfo.hpp index f3c7511b..d8551d46 100644 --- a/unrar/extinfo.hpp +++ b/unrar/extinfo.hpp @@ -1,8 +1,9 @@ #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ +bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked); bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink); #ifdef _UNIX void SetUnixOwner(Archive &Arc,const wchar *FileName); #endif diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 406b1a01..b5de6cdc 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -12,6 +12,12 @@ CmdExtract::CmdExtract(CommandData *Cmd) memset(Analyze,0,sizeof(*Analyze)); TotalFileCount=0; + + // Common for all archives involved. Set here instead of DoExtract() + // to use in unrar.dll too. Allows to avoid LinksToDirs() calls + // and save CPU time in no symlinks including ".." in target were extracted. + UpLinkExtracted=false; + Unp=new Unpack(&DataIO); #ifdef RAR_SMP Unp->SetThreads(Cmd->Threads); @@ -125,6 +131,8 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc) ArcAnalyzed=false; StartTime.SetCurrentTime(); + + LastCheckedSymlink.clear(); } @@ -618,6 +626,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); #endif + if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && + UpLinkExtracted) + ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); + File CurFile; bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; @@ -747,7 +759,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) { wchar RedirName[NM]; - ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + + // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with + // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning + // from 5.10 even Windows version uses / internally and converts + // them to \ when reading FHEXTRA_REDIR. + // We must perform this conversion before ConvertPath call, + // so paths mixing different slashes like \dir1/dir2\file are + // processed correctly. + SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + + ConvertPath(RedirName,RedirName,ASIZE(RedirName)); wchar NameExisting[NM]; ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); @@ -761,7 +783,22 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) { if (FileCreateMode) - LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); + { + bool UpLink; + LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); + UpLinkExtracted|=LinkSuccess && UpLink; + + // We do not actually need to reset the cache here if we cache + // only the single last checked path, because at this point + // it will always contain the link own path and link can't + // overwrite its parent folder. But if we ever decide to cache + // several already checked paths, we'll need to reset them here. + // Otherwise if no files were created in one of such paths, + // let's say because of file create error, it might be possible + // to overwrite the path with link and avoid checks. We keep this + // code here as a reminder in case of possible modifications. + LastCheckedSymlink.clear(); // Reset cache for safety reason. + } } else { @@ -948,8 +985,6 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) { - SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. - File Existing; if (!Existing.Open(NameExisting)) { @@ -1269,6 +1304,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); if (!DirExist) { + if (!Cmd->AbsoluteLinks && UpLinkExtracted) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); } @@ -1350,6 +1387,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) MakeNameUsable(DestFileName,true); + if (!Cmd->AbsoluteLinks && UpLinkExtracted) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { diff --git a/unrar/extract.hpp b/unrar/extract.hpp index aac463c7..0fb5a963 100644 --- a/unrar/extract.hpp +++ b/unrar/extract.hpp @@ -71,6 +71,12 @@ class CmdExtract bool PrevProcessed; // If previous file was successfully extracted or tested. wchar DestFileName[NM]; bool PasswordCancelled; + bool UpLinkExtracted; // At least one symlink with ".." in target was extracted. + + // Last path checked for symlinks. We use it to improve the performance, + // so we do not check recently checked folders again. + std::wstring LastCheckedSymlink; + #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) bool Fat32,NotFat32; #endif diff --git a/unrar/hardlinks.cpp b/unrar/hardlinks.cpp index 40cc0aa4..171b5fa0 100644 --- a/unrar/hardlinks.cpp +++ b/unrar/hardlinks.cpp @@ -1,7 +1,5 @@ bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) { - SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. - if (!FileExist(NameExisting)) { uiMsg(UIERROR_HLINKCREATE,NameNew); diff --git a/unrar/model.cpp b/unrar/model.cpp index 83391c5a..e4f9e3c5 100644 --- a/unrar/model.cpp +++ b/unrar/model.cpp @@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model) Model->Coder.SubRange.LowCount=HiCnt; Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; i=NumStats-Model->NumMasked; - pps--; + + // 2022.12.02: we removed pps-- here and changed the code below to avoid + // "array subscript -1 is outside array bounds" warning in some compilers. do { - pps++; if (pps>=ps+ASIZE(ps)) // Extra safety check. return false; Model->CharMask[(*pps)->Symbol]=Model->EscCount; + pps++; } while ( --i ); psee2c->Summ += Model->Coder.SubRange.scale; Model->NumMasked = NumStats; diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 983bd743..162eda21 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize) const wchar *s=DestPtr; if (s[0]!=0 && IsDriveDiv(s[1])) s+=2; - if (s[0]=='\\' && s[1]=='\\') + + // Skip UNC Windows \\server\share\ or Unix //server/share/ + if (IsPathDiv(s[0]) && IsPathDiv(s[1])) { - const wchar *Slash=wcschr(s+2,'\\'); - if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) - s=Slash+1; + uint SlashCount=0; + for (const wchar *t=s+2;*t!=0;t++) + if (IsPathDiv(*t) && ++SlashCount==2) + { + s=t+1; // Found two more path separators after leading two. + break; + } } for (const wchar *t=s;*t!=0;t++) if (IsPathDiv(*t)) diff --git a/unrar/timefn.hpp b/unrar/timefn.hpp index 52713616..49b61e85 100644 --- a/unrar/timefn.hpp +++ b/unrar/timefn.hpp @@ -22,6 +22,17 @@ class RarTime // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601. // We use nanoseconds here to handle the high precision Unix time. + // It allows dates up to July 2185. + // + // If we'll ever need to extend the date range, we can define a lower + // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows + // versions can differ in least significant digits of "lt" time output + // for Unix archives. + // Alternatively we can introduce 'bool HighPrecision' set to true + // in SetUnixNS() and TicksPerSecond() instead of constant above. + // It might be more reliable than defining TicksPerSecond variable, + // which wouldn't survive memset of any structure hosting RarTime. + // We would need to eliminate all such memsets in the entire code first. uint64 itime; public: // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND. diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index 5f53e3a9..141a97fe 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -70,7 +70,8 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize) } -bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, + const wchar *LinkName,bool &UpLink) { char Target[NM]; if (IsLink(Arc.FileHead.FileAttr)) @@ -100,13 +101,14 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) return false; + UpLink=strstr(Target,"..")!=NULL; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; } -bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) +static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) { char Target[NM]; WideToChar(hd->RedirName,Target,ASIZE(Target)); diff --git a/unrar/version.hpp b/unrar/version.hpp index 2749f7f3..97e5b8fa 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 20 -#define RARVER_BETA 2 -#define RARVER_DAY 12 -#define RARVER_MONTH 11 +#define RARVER_BETA 3 +#define RARVER_DAY 15 +#define RARVER_MONTH 12 #define RARVER_YEAR 2022 diff --git a/unrar/win32stm.cpp b/unrar/win32stm.cpp index 9907de70..048fd86b 100644 --- a/unrar/win32stm.cpp +++ b/unrar/win32stm.cpp @@ -118,8 +118,12 @@ 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,false)) - CurFile.Close(); + + if (CurFile.WCreate(FullName)) + { + if (Arc.ReadSubData(NULL,&CurFile,false)) + CurFile.Close(); + } // Restoring original file timestamps. File HostFile; From d2b82835c48188e783b035198501d9a7929aaf6b Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:02 +0000 Subject: [PATCH 14/76] Added unrar 6.2.4 --- unrar/arcread.cpp | 8 ++++---- unrar/dll.rc | 10 +++++----- unrar/extract.cpp | 36 +++++++++++++++++++++++++++++++----- unrar/extract.hpp | 5 ++++- unrar/headers.hpp | 2 +- unrar/list.cpp | 4 ++-- unrar/version.hpp | 8 ++++---- 7 files changed, 51 insertions(+), 22 deletions(-) diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index c533c4a1..97d11849 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -1000,7 +1000,7 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) { std::vector NameU((size_t)NameSize); // UTF-8 name. Raw->GetB(&NameU[0],(size_t)NameSize); - // If starts from 0, the name was longer han reserved space + // If starts from 0, the name was longer than reserved space // when saving this extra field. if (NameU[0]!=0) { @@ -1014,11 +1014,11 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) if ((Flags & MHEXTRA_METADATA_CTIME)!=0) if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0) if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0) - hd->OrigCtime.SetUnixNS(Raw->Get8()); + hd->OrigTime.SetUnixNS(Raw->Get8()); else - hd->OrigCtime.SetUnix((time_t)Raw->Get4()); + hd->OrigTime.SetUnix((time_t)Raw->Get4()); else - hd->OrigCtime.SetWin(Raw->Get8()); + hd->OrigTime.SetWin(Raw->Get8()); } break; } diff --git a/unrar/dll.rc b/unrar/dll.rc index f67dc63e..705f984c 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 20, 3, 714 -PRODUCTVERSION 6, 20, 3, 714 +FILEVERSION 6, 20, 100, 748 +PRODUCTVERSION 6, 20, 100, 748 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "6.20.3\0" - VALUE "ProductVersion", "6.20.3\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2022\0" + VALUE "FileVersion", "6.20.0\0" + VALUE "ProductVersion", "6.20.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index b5de6cdc..2434c078 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -16,7 +16,18 @@ CmdExtract::CmdExtract(CommandData *Cmd) // Common for all archives involved. Set here instead of DoExtract() // to use in unrar.dll too. Allows to avoid LinksToDirs() calls // and save CPU time in no symlinks including ".." in target were extracted. - UpLinkExtracted=false; +#if defined(_WIN_ALL) + // We can't expand symlink path components in another symlink target + // in Windows. We can't create symlinks in Android now. Even though we do not + // really need LinksToDirs() calls in these systems, we still call it + // for extra safety, but only if symlink with ".." in target was extracted. + ConvertSymlinkPaths=false; +#else + // We enable it by default in Unix to care about the case when several + // archives are unpacked to same directory with several independent RAR runs. + // Worst case performance penalty for a lot of small files seems to be ~3%. + ConvertSymlinkPaths=true; +#endif Unp=new Unpack(&DataIO); #ifdef RAR_SMP @@ -486,6 +497,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (!Cmd->Test) // While harmless, it is useless for 't'. { + // If reference source isn't selected, but target is selected, + // we unpack the source under the temporary name and then rename + // or copy it to target name. We do not unpack it under the target + // name immediately, because the same source can be used by multiple + // targets and it is possible that first target isn't unpacked + // for some reason. Also targets might have associated service blocks + // like ACLs. All this would complicate processing a lot. wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); AddEndSlash(DestFileName,ASIZE(DestFileName)); wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); @@ -627,7 +645,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) #endif if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && - UpLinkExtracted) + ConvertSymlinkPaths) ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); File CurFile; @@ -786,7 +804,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { bool UpLink; LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); - UpLinkExtracted|=LinkSuccess && UpLink; + ConvertSymlinkPaths|=LinkSuccess && UpLink; // We do not actually need to reset the cache here if we cache // only the single last checked path, because at this point @@ -1304,7 +1322,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); if (!DirExist) { - if (!Cmd->AbsoluteLinks && UpLinkExtracted) + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); @@ -1387,7 +1405,7 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) MakeNameUsable(DestFileName,true); - if (!Cmd->AbsoluteLinks && UpLinkExtracted) + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) @@ -1499,6 +1517,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi { if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. { + // We would gain nothing and unnecessarily complicate extraction + // by setting these values for first volume or first archived file. if (!FirstVolume) wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); if (!FirstFile) @@ -1511,6 +1531,10 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi MatchFound = true; PrevMatched = true; + // Reset the previously set early exit position, if any, because + // we found a new matched file. + Analyze->EndPos=0; + // Matched file reference pointing at maybe non-matched source file. // Even though we know RedirName, we can't check if source file // is certainly non-matched, because it can be filtered out by @@ -1545,6 +1569,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi { if (PrevMatched) // First non-matched item after matched. { + // We would gain nothing and unnecessarily complicate extraction + // by setting these values for first volume or first archived file. if (!FirstVolume) wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName)); if (!FirstFile) diff --git a/unrar/extract.hpp b/unrar/extract.hpp index 0fb5a963..adfba8fd 100644 --- a/unrar/extract.hpp +++ b/unrar/extract.hpp @@ -71,7 +71,10 @@ class CmdExtract bool PrevProcessed; // If previous file was successfully extracted or tested. wchar DestFileName[NM]; bool PasswordCancelled; - bool UpLinkExtracted; // At least one symlink with ".." in target was extracted. + + // In Windows it is set to true if at least one symlink with ".." + // in target was extracted. + bool ConvertSymlinkPaths; // Last path checked for symlinks. We use it to improve the performance, // so we do not check recently checked folders again. diff --git a/unrar/headers.hpp b/unrar/headers.hpp index 8a16a457..0941220f 100644 --- a/unrar/headers.hpp +++ b/unrar/headers.hpp @@ -170,7 +170,7 @@ struct MainHeader:BaseBlock uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field. std::wstring OrigName; // Original archive name. - RarTime OrigCtime; // Original archive creation time. + RarTime OrigTime; // Original archive time. void Reset(); }; diff --git a/unrar/list.cpp b/unrar/list.cpp index 2defd27a..e4444e13 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -65,10 +65,10 @@ void ListArchive(CommandData *Cmd) if (!Arc.MainHead.OrigName.empty()) mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str()); - if (Arc.MainHead.OrigCtime.IsSet()) + if (Arc.MainHead.OrigTime.IsSet()) { wchar DateStr[50]; - Arc.MainHead.OrigCtime.GetText(DateStr,ASIZE(DateStr),Technical); + Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical); mprintf(L"\n%s: %s",St(MOriginalTime),DateStr); } diff --git a/unrar/version.hpp b/unrar/version.hpp index 97e5b8fa..bbb42ecd 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 20 -#define RARVER_BETA 3 -#define RARVER_DAY 15 -#define RARVER_MONTH 12 -#define RARVER_YEAR 2022 +#define RARVER_BETA 0 +#define RARVER_DAY 17 +#define RARVER_MONTH 1 +#define RARVER_YEAR 2023 From cc7d8e8c40c6c49354c59b4d2b8e0eee71a2f670 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:03 +0000 Subject: [PATCH 15/76] Added unrar 6.2.5 --- unrar/cmdfilter.cpp | 4 ++-- unrar/crypt.cpp | 2 +- unrar/dll.rc | 8 ++++---- unrar/errhnd.cpp | 5 ++++- unrar/extract.cpp | 2 ++ unrar/version.hpp | 6 +++--- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/unrar/cmdfilter.cpp b/unrar/cmdfilter.cpp index d098d108..e0add14b 100644 --- a/unrar/cmdfilter.cpp +++ b/unrar/cmdfilter.cpp @@ -289,8 +289,8 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy return 0; if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) return 0; - if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || - FileHead.Dir && !InclDir)) + if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 && + (!FileHead.Dir || !InclDir)) return 0; if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; diff --git a/unrar/crypt.cpp b/unrar/crypt.cpp index e69b244d..d14c78f8 100644 --- a/unrar/crypt.cpp +++ b/unrar/crypt.cpp @@ -47,7 +47,7 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, SecPassword *Password,const byte *Salt, const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck) { - if (!Password->IsSet() || Method==CRYPT_NONE) + if (Method==CRYPT_NONE || !Password->IsSet()) return false; CryptData::Method=Method; diff --git a/unrar/dll.rc b/unrar/dll.rc index 705f984c..25474733 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 20, 100, 748 -PRODUCTVERSION 6, 20, 100, 748 +FILEVERSION 6, 21, 1, 755 +PRODUCTVERSION 6, 21, 1, 755 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", "6.20.0\0" - VALUE "ProductVersion", "6.20.0\0" + VALUE "FileVersion", "6.21.1\0" + VALUE "ProductVersion", "6.21.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/errhnd.cpp b/unrar/errhnd.cpp index d473b1f8..97193e5a 100644 --- a/unrar/errhnd.cpp +++ b/unrar/errhnd.cpp @@ -169,10 +169,13 @@ void ErrorHandler::OpenErrorMsg(const wchar *FileName) void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) { - Wait(); // Keep GUI responsive if many files cannot be opened when archiving. uiMsg(UIERROR_FILEOPEN,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_OPEN); + + // Keep GUI responsive if many files cannot be opened when archiving. + // Call after SysErrMsg to avoid modifying the error code and SysErrMsg text. + Wait(); } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 2434c078..2233eb9b 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -638,6 +638,8 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) break; } } + else + DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); #ifdef RARDLL if (*Cmd->DllDestName!=0) diff --git a/unrar/version.hpp b/unrar/version.hpp index bbb42ecd..b37b8f1c 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 20 -#define RARVER_BETA 0 -#define RARVER_DAY 17 +#define RARVER_MINOR 21 +#define RARVER_BETA 1 +#define RARVER_DAY 24 #define RARVER_MONTH 1 #define RARVER_YEAR 2023 From e63362d16a142f4bb01d7e101f6e1a52505dc3d5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:03 +0000 Subject: [PATCH 16/76] Added unrar 6.2.6 --- unrar/dll.rc | 8 ++--- unrar/extract.cpp | 40 ++++++++++++++++++------- unrar/extract.hpp | 2 +- unrar/makefile | 9 ++++-- unrar/pathfn.cpp | 75 ++++++++++++++++++++++++++--------------------- unrar/pathfn.hpp | 1 - unrar/version.hpp | 6 ++-- 7 files changed, 85 insertions(+), 56 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 25474733..693afd57 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 21, 1, 755 -PRODUCTVERSION 6, 21, 1, 755 +FILEVERSION 6, 21, 100, 778 +PRODUCTVERSION 6, 21, 100, 778 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", "6.21.1\0" - VALUE "ProductVersion", "6.21.1\0" + VALUE "FileVersion", "6.21.0\0" + VALUE "ProductVersion", "6.21.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 2233eb9b..65b50709 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -38,13 +38,13 @@ CmdExtract::CmdExtract(CommandData *Cmd) CmdExtract::~CmdExtract() { - ReleaseAnalyzeData(); + FreeAnalyzeData(); delete Unp; delete Analyze; } -void CmdExtract::ReleaseAnalyzeData() +void CmdExtract::FreeAnalyzeData() { for (size_t I=0;IFileArgs.GetString(); Cmd->FileArgs.Rewind(); @@ -1481,10 +1481,16 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi wcsncpyz(NextName,ArcName,ASIZE(NextName)); bool MatchFound=false; - bool FirstVolume=true; bool PrevMatched=false; bool OpenNext=false; + bool FirstVolume=true; + + // We shall set FirstFile once for all volumes and not for each volume. + // So we do not reuse the outdated Analyze->StartPos from previous volume + // if extracted file resides completely in the beginning of current one. + bool FirstFile=true; + while (true) { Archive Arc(Cmd); @@ -1502,7 +1508,6 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi } OpenNext=false; - bool FirstFile=true; while (Arc.ReadHeader()>0) { Wait(); @@ -1520,13 +1525,18 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. { // We would gain nothing and unnecessarily complicate extraction - // by setting these values for first volume or first archived file. + // if we set StartName for first volume or StartPos for first + // archived file. if (!FirstVolume) wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); + + // We shall set FirstFile once for all volumes for this code + // to work properly. Alternatively we could append + // "|| Analyze->StartPos!=0" to the condition, so we do not reuse + // the outdated Analyze->StartPos value from previous volume. if (!FirstFile) Analyze->StartPos=Arc.CurBlockPos; } - FirstFile=false; if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) { @@ -1571,16 +1581,18 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi { if (PrevMatched) // First non-matched item after matched. { - // We would gain nothing and unnecessarily complicate extraction - // by setting these values for first volume or first archived file. + // We would perform the unnecessarily string comparison + // when extracting if we set this value for first volume + // or non-volume archive. if (!FirstVolume) wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName)); - if (!FirstFile) - Analyze->EndPos=Arc.CurBlockPos; + Analyze->EndPos=Arc.CurBlockPos; } PrevMatched=false; } } + + FirstFile=false; if (Arc.FileHead.SplitAfter) { OpenNext=true; // Allow open next volume. @@ -1595,6 +1607,12 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi { NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); FirstVolume=false; + + // Needed for multivolume archives. Added in case some 'break' + // will quit early from loop above, so we do not set it in the loop. + // Now it can happen for hypothetical archive without file records + // and with HEAD_ENDARC record. + FirstFile=false; } else break; diff --git a/unrar/extract.hpp b/unrar/extract.hpp index adfba8fd..18396c5b 100644 --- a/unrar/extract.hpp +++ b/unrar/extract.hpp @@ -24,7 +24,7 @@ class CmdExtract bool ArcAnalyzed; - void ReleaseAnalyzeData(); + void FreeAnalyzeData(); EXTRACT_ARC_CODE ExtractArchive(); bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize); void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); diff --git a/unrar/makefile b/unrar/makefile index 8e98a2c6..55af49be 100644 --- a/unrar/makefile +++ b/unrar/makefile @@ -142,20 +142,23 @@ clean: @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) @rm -f unrar libunrar.* -unrar: clean $(OBJECTS) $(UNRAR_OBJ) +# We removed 'clean' from dependencies, because it prevented parallel +# 'make -Jn' builds. + +unrar: $(OBJECTS) $(UNRAR_OBJ) @rm -f unrar $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) $(STRIP) unrar sfx: WHAT=SFX_MODULE -sfx: clean $(OBJECTS) +sfx: $(OBJECTS) @rm -f default.sfx $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS) $(STRIP) default.sfx lib: WHAT=RARDLL lib: CXXFLAGS+=$(LIBFLAGS) -lib: clean $(OBJECTS) $(LIB_OBJ) +lib: $(OBJECTS) $(LIB_OBJ) @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 162eda21..50b6b29d 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -428,50 +428,39 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering) bool IsNameUsable(const wchar *Name) { -#ifndef _UNIX - if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) + // We were asked to apply Windows-like conversion in Linux in case + // files are unpacked to Windows share. This code is invoked only + // if file failed to be created, so it doesn't affect extraction + // of Unix compatible names to native Unix drives. +#ifdef _UNIX + // Windows shares in Unix do not allow the drive letter, + // so unlike Windows version, we check all characters here. + if (wcschr(Name,':')!=NULL) + return false; +#else + if (Name[0]!=0 && Name[1]!=0 && wcschr(Name+2,':')!=NULL) return false; +#endif for (const wchar *s=Name;*s!=0;s++) { if ((uint)*s<32) return false; + + // It is for Windows shares in Unix. We can create such names in Windows. +#ifdef _UNIX + // No spaces or dots before the path separator are allowed in Windows + // shares. But they are allowed and automtically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) return false; - } #endif + } return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; } -void MakeNameUsable(char *Name,bool Extended) -{ -#ifdef _WIN_ALL - // In Windows we also need to convert characters not defined in current - // code page. This double conversion changes them to '?', which is - // catched by code below. - size_t NameLength=strlen(Name); - wchar NameW[NM]; - CharToWide(Name,NameW,ASIZE(NameW)); - WideToChar(NameW,Name,NameLength+1); - Name[NameLength]=0; -#endif - for (char *s=Name;*s!=0;s=charnext(s)) - { - if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32) - *s='_'; -#ifdef _EMX - if (*s=='=') - *s='_'; -#endif -#ifndef _UNIX - if (s-Name>1 && *s==':') - *s='_'; - // Remove ' ' and '.' before path separator, but allow .\ and ..\. - if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1])) - *s='_'; -#endif - } -} void MakeNameUsable(wchar *Name,bool Extended) @@ -480,7 +469,27 @@ void MakeNameUsable(wchar *Name,bool Extended) { if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) *s='_'; -#ifndef _UNIX +#ifdef _UNIX + // We were asked to apply Windows-like conversion in Linux in case + // files are unpacked to Windows share. This code is invoked only + // if file failed to be created, so it doesn't affect extraction + // of Unix compatible names to native Unix drives. + if (Extended) + { + // Windows shares in Unix do not allow the drive letter, + // so unlike Windows version, we check all characters here. + if (*s==':') + *s='_'; + + // No spaces or dots before the path separator are allowed on Windows + // shares. But they are allowed and automtically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + *s='_'; + } +#else if (s-Name>1 && *s==':') *s='_'; #if 0 // We already can create such files. diff --git a/unrar/pathfn.hpp b/unrar/pathfn.hpp index efb28330..62cae0ad 100644 --- a/unrar/pathfn.hpp +++ b/unrar/pathfn.hpp @@ -29,7 +29,6 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx wchar* GetVolNumPart(const wchar *ArcName); void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); bool IsNameUsable(const wchar *Name); -void MakeNameUsable(char *Name,bool Extended); void MakeNameUsable(wchar *Name,bool Extended); void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); diff --git a/unrar/version.hpp b/unrar/version.hpp index b37b8f1c..fa887fa0 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 21 -#define RARVER_BETA 1 -#define RARVER_DAY 24 -#define RARVER_MONTH 1 +#define RARVER_BETA 0 +#define RARVER_DAY 16 +#define RARVER_MONTH 2 #define RARVER_YEAR 2023 From 340695bebbcc2625284879165eddb98db5db9179 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:04 +0000 Subject: [PATCH 17/76] Added unrar 6.2.7 --- unrar/arcread.cpp | 4 ++++ unrar/dll.rc | 8 ++++---- unrar/extract.cpp | 10 ++++++++++ unrar/version.hpp | 8 ++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 97d11849..86488cd4 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -254,7 +254,11 @@ size_t Archive::ReadHeader15() hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; hd->SaltSet=(hd->Flags & LHD_SALT)!=0; + + // RAR versions earlier than 2.0 do not set the solid flag + // in file header. They use only a global solid archive flag. hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); diff --git a/unrar/dll.rc b/unrar/dll.rc index 693afd57..51ce953e 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 21, 100, 778 -PRODUCTVERSION 6, 21, 100, 778 +FILEVERSION 6, 22, 1, 865 +PRODUCTVERSION 6, 22, 1, 865 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", "6.21.0\0" - VALUE "ProductVersion", "6.21.0\0" + VALUE "FileVersion", "6.22.1\0" + VALUE "ProductVersion", "6.22.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 65b50709..4d9e5120 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -846,6 +846,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE + // RAR 1.3 - 1.5 archives do not set per file solid flag. if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) Unp->DoUnpack(15,FileCount>1 && Arc.Solid); else @@ -1520,6 +1521,15 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi } if (HeaderType==HEAD_FILE) { + if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15) + { + // RAR versions earlier than 2.0 do not set per file solid flag. + // They have only the global archive solid flag, so we can't + // reliably analyze them here. + OpenNext=false; + break; + } + if (!Arc.FileHead.SplitBefore) { if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. diff --git a/unrar/version.hpp b/unrar/version.hpp index fa887fa0..9af752ed 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 21 -#define RARVER_BETA 0 -#define RARVER_DAY 16 -#define RARVER_MONTH 2 +#define RARVER_MINOR 22 +#define RARVER_BETA 1 +#define RARVER_DAY 14 +#define RARVER_MONTH 5 #define RARVER_YEAR 2023 From 094a536987b398a44a27a6d108d64e48d04088b4 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:05 +0000 Subject: [PATCH 18/76] Added unrar 6.2.8 --- unrar/archive.hpp | 1 - unrar/arcread.cpp | 19 +++++------------- unrar/dll.rc | 8 ++++---- unrar/extinfo.cpp | 10 ++-------- unrar/extract.cpp | 5 +++++ unrar/headers.hpp | 11 ----------- unrar/uiconsole.cpp | 1 + unrar/uowners.cpp | 48 ++------------------------------------------- unrar/version.hpp | 4 ++-- unrar/win32lnk.cpp | 2 +- unrar/win32stm.cpp | 1 + 11 files changed, 23 insertions(+), 87 deletions(-) diff --git a/unrar/archive.hpp b/unrar/archive.hpp index 35a399bc..c0019aef 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -107,7 +107,6 @@ class Archive:public File FileHeader SubHead; CommentHeader CommHead; ProtectHeader ProtectHead; - UnixOwnersHeader UOHead; EAHeader EAHead; StreamHeader StreamHead; diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 86488cd4..f5ef9aa5 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -476,19 +476,6 @@ size_t Archive::ReadHeader15() SubBlockHead.Level=Raw.Get1(); switch(SubBlockHead.SubType) { - case UO_HEAD: - *(SubBlockHeader *)&UOHead=SubBlockHead; - UOHead.OwnerNameSize=Raw.Get2(); - UOHead.GroupNameSize=Raw.Get2(); - if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName)) - UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1; - if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName)) - UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1; - Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize); - Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize); - UOHead.OwnerName[UOHead.OwnerNameSize]=0; - UOHead.GroupName[UOHead.GroupNameSize]=0; - break; case NTACL_HEAD: *(SubBlockHeader *)&EAHead=SubBlockHead; EAHead.UnpSize=Raw.Get4(); @@ -520,8 +507,12 @@ size_t Archive::ReadHeader15() ushort HeaderCRC=Raw.GetCRC15(false); // Old AV header does not have header CRC properly set. + // Old Unix owners header didn't include string fields into header size, + // but included them into CRC, so it couldn't be verified with generic + // approach here. if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && - ShortBlock.HeaderType!=HEAD3_AV) + ShortBlock.HeaderType!=HEAD3_AV && + (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD)) { bool Recovered=false; if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) diff --git a/unrar/dll.rc b/unrar/dll.rc index 51ce953e..5164df96 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 22, 1, 865 -PRODUCTVERSION 6, 22, 1, 865 +FILEVERSION 6, 22, 100, 880 +PRODUCTVERSION 6, 22, 100, 880 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", "6.22.1\0" - VALUE "ProductVersion", "6.22.1\0" + VALUE "FileVersion", "6.22.0\0" + VALUE "ProductVersion", "6.22.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extinfo.cpp b/unrar/extinfo.cpp index 0f25f312..4bf8046a 100644 --- a/unrar/extinfo.cpp +++ b/unrar/extinfo.cpp @@ -21,17 +21,11 @@ #ifndef SFX_MODULE void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) { +#ifdef _WIN_ALL if (Cmd->Test) return; switch(Arc.SubBlockHead.SubType) { -#ifdef _UNIX - case UO_HEAD: - if (Cmd->ProcessOwners) - ExtractUnixOwner20(Arc,Name); - break; -#endif -#ifdef _WIN_ALL case NTACL_HEAD: if (Cmd->ProcessOwners) ExtractACL20(Arc,Name); @@ -39,8 +33,8 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) case STREAM_HEAD: ExtractStreams20(Arc,Name); break; -#endif } +#endif } #endif diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 4d9e5120..f5e8fa44 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -806,6 +806,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { bool UpLink; LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); + + // Unix symlink can have its own owner data. + if (LinkSuccess) + SetFileHeaderExtra(Cmd,Arc,DestFileName); + ConvertSymlinkPaths|=LinkSuccess && UpLink; // We do not actually need to reset the cache here if we cache diff --git a/unrar/headers.hpp b/unrar/headers.hpp index 0941220f..5984f996 100644 --- a/unrar/headers.hpp +++ b/unrar/headers.hpp @@ -11,7 +11,6 @@ #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 #define SIZEOF_PROTECTHEAD 26 -#define SIZEOF_UOHEAD 18 #define SIZEOF_STREAMHEAD 26 #define VER_PACK 29U @@ -325,16 +324,6 @@ struct ProtectHeader:BlockHeader }; -struct UnixOwnersHeader:SubBlockHeader -{ - ushort OwnerNameSize; - ushort GroupNameSize; -/* dummy */ - char OwnerName[256]; - char GroupName[256]; -}; - - struct EAHeader:SubBlockHeader { uint UnpSize; diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index b1ac5132..b524c25f 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -183,6 +183,7 @@ void uiMsgStore::Msg() Log(NULL,St(MNeedAdmin)); break; case UIERROR_ARCBROKEN: + mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN. Log(Str[0],St(MErrBrokenArc)); break; case UIERROR_HEADERBROKEN: diff --git a/unrar/uowners.cpp b/unrar/uowners.cpp index 9f463085..5eb12790 100644 --- a/unrar/uowners.cpp +++ b/unrar/uowners.cpp @@ -1,56 +1,12 @@ -void ExtractUnixOwner20(Archive &Arc,const wchar *FileName) +void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { char NameA[NM]; WideToChar(FileName,NameA,ASIZE(NameA)); - if (Arc.BrokenHeader) - { - uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName); - ErrHandler.SetErrorCode(RARX_CRC); + if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL) return; - } - - struct passwd *pw; - errno=0; // Required by getpwnam specification if we need to check errno. - if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL) - { - uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName)); - ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(RARX_WARNING); - return; - } - uid_t OwnerID=pw->pw_uid; - - struct group *gr; - errno=0; // Required by getgrnam specification if we need to check errno. - if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL) - { - uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName)); - ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(RARX_CRC); - return; - } - uint Attr=GetFileAttr(FileName); - gid_t GroupID=gr->gr_gid; -#if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(NameA,OwnerID,GroupID)!=0) -#else - if (chown(NameA,OwnerID,GroupID)!=0) -#endif - { - uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); - ErrHandler.SetErrorCode(RARX_CREATE); - } - SetFileAttr(FileName,Attr); -} - - -void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) -{ - char NameA[NM]; - WideToChar(FileName,NameA,ASIZE(NameA)); char *OwnerName=(char *)&Arc.SubHead.SubData[0]; int OwnerSize=strlen(OwnerName)+1; diff --git a/unrar/version.hpp b/unrar/version.hpp index 9af752ed..47ee3872 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 #define RARVER_MINOR 22 -#define RARVER_BETA 1 -#define RARVER_DAY 14 +#define RARVER_BETA 0 +#define RARVER_DAY 29 #define RARVER_MONTH 5 #define RARVER_YEAR 2023 diff --git a/unrar/win32lnk.cpp b/unrar/win32lnk.cpp index 84ab63ff..759c4900 100644 --- a/unrar/win32lnk.cpp +++ b/unrar/win32lnk.cpp @@ -40,7 +40,7 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) PrivSet=true; } - const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024; + const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024; Array Buf(BufSize); REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; diff --git a/unrar/win32stm.cpp b/unrar/win32stm.cpp index 048fd86b..9dab728b 100644 --- a/unrar/win32stm.cpp +++ b/unrar/win32stm.cpp @@ -39,6 +39,7 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName) CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); + wcsncatz(StreamName,StoredName,ASIZE(StreamName)); FindData fd; From f57721dbb3ab01978d7e95509fca862f00d5f2a5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:05 +0000 Subject: [PATCH 19/76] Added unrar 6.2.9 --- unrar/dll.rc | 8 ++++---- unrar/getbits.cpp | 8 ++++---- unrar/pathfn.cpp | 2 +- unrar/recvol3.cpp | 11 +++++++++-- unrar/secpassword.cpp | 8 ++++---- unrar/version.hpp | 8 ++++---- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 5164df96..9cd0d7ec 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 22, 100, 880 -PRODUCTVERSION 6, 22, 100, 880 +FILEVERSION 6, 23, 1, 931 +PRODUCTVERSION 6, 23, 1, 931 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", "6.22.0\0" - VALUE "ProductVersion", "6.22.0\0" + VALUE "FileVersion", "6.23.1\0" + VALUE "ProductVersion", "6.23.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/getbits.cpp b/unrar/getbits.cpp index 8805f278..5d5ad2bb 100644 --- a/unrar/getbits.cpp +++ b/unrar/getbits.cpp @@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer) ExternalBuffer=false; if (AllocBuffer) { - // getbits*() attempt to read data from InAddr, ... InAddr+3 positions. - // So let's allocate 3 additional bytes for situation, when we need to + // getbits*() attempt to read data from InAddr, ... InAddr+4 positions. + // So let's allocate 4 additional bytes for situation, when we need to // read only 1 byte from the last position of buffer and avoid a crash - // from access to next 3 bytes, which contents we do not need. - size_t BufSize=MAX_SIZE+3; + // from access to next 4 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+4; InBuf=new byte[BufSize]; // Ensure that we get predictable results when accessing bytes in area diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 50b6b29d..6d3f56c0 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -746,7 +746,7 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u // Here we ensure that we have enough 'N' characters to fit all digits // of archive number. We'll replace them by actual number later // in this function. - if (NCount255) + if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255) continue; if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) { @@ -238,7 +238,14 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) wcsncpyz(PrevName,CurName,ASIZE(PrevName)); File *NewFile=new File; NewFile->TOpen(CurName); - SrcFile[FileNumber+P[0]-1]=NewFile; + + // This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255 + // checks above. Still we keep it here for better clarity and security. + int SrcPos=FileNumber+P[0]-1; + if (SrcPos<0 || SrcPos>=ASIZE(SrcFile)) + continue; + SrcFile[SrcPos]=NewFile; + FoundRecVolumes++; if (RecFileSize==0) diff --git a/unrar/secpassword.cpp b/unrar/secpassword.cpp index 42ed47d5..08da5497 100644 --- a/unrar/secpassword.cpp +++ b/unrar/secpassword.cpp @@ -70,7 +70,7 @@ void SecPassword::Clean() { PasswordSet=false; if (Password.size()>0) - cleandata(&Password[0],Password.size()); + cleandata(&Password[0],Password.size()*sizeof(Password[0])); } @@ -141,7 +141,7 @@ size_t SecPassword::Length() wchar Plain[MAXPASSWORD]; Get(Plain,ASIZE(Plain)); size_t Length=wcslen(Plain); - cleandata(Plain,ASIZE(Plain)); + cleandata(Plain,sizeof(Plain)); return Length; } @@ -156,8 +156,8 @@ bool SecPassword::operator == (SecPassword &psw) Get(Plain1,ASIZE(Plain1)); psw.Get(Plain2,ASIZE(Plain2)); bool Result=wcscmp(Plain1,Plain2)==0; - cleandata(Plain1,ASIZE(Plain1)); - cleandata(Plain2,ASIZE(Plain2)); + cleandata(Plain1,sizeof(Plain1)); + cleandata(Plain2,sizeof(Plain2)); return Result; } diff --git a/unrar/version.hpp b/unrar/version.hpp index 47ee3872..73f084e9 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 6 -#define RARVER_MINOR 22 -#define RARVER_BETA 0 -#define RARVER_DAY 29 -#define RARVER_MONTH 5 +#define RARVER_MINOR 23 +#define RARVER_BETA 1 +#define RARVER_DAY 19 +#define RARVER_MONTH 7 #define RARVER_YEAR 2023 From 294d87a695dfcb6b92a05a7d8a5bb0e78f1659a9 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:06 +0000 Subject: [PATCH 20/76] Added unrar 7.0.1 --- unrar/arccmt.cpp | 63 ++- unrar/archive.cpp | 33 +- unrar/archive.hpp | 33 +- unrar/arcread.cpp | 207 +++++----- unrar/blake2s.cpp | 32 +- unrar/blake2s_sse.cpp | 30 +- unrar/cmddata.cpp | 359 ++++++++++------- unrar/cmddata.hpp | 38 +- unrar/cmdfilter.cpp | 74 ++-- unrar/cmdmix.cpp | 2 +- unrar/compress.hpp | 15 +- unrar/consio.cpp | 159 ++++---- unrar/consio.hpp | 8 +- unrar/crc.cpp | 3 +- unrar/crypt.cpp | 2 +- unrar/crypt.hpp | 1 - unrar/crypt1.cpp | 13 - unrar/crypt5.cpp | 8 + unrar/dll.cpp | 82 ++-- unrar/dll.hpp | 9 +- unrar/dll.rc | 8 +- unrar/encname.cpp | 22 +- unrar/encname.hpp | 6 +- unrar/errhnd.cpp | 109 ++--- unrar/errhnd.hpp | 38 +- unrar/extinfo.cpp | 135 ++----- unrar/extinfo.hpp | 17 +- unrar/extract.cpp | 548 +++++++++++++------------ unrar/extract.hpp | 33 +- unrar/filcreat.cpp | 85 ++-- unrar/filcreat.hpp | 6 +- unrar/file.cpp | 87 ++-- unrar/file.hpp | 23 +- unrar/filefn.cpp | 337 ++++++++++------ unrar/filefn.hpp | 41 +- unrar/filestr.cpp | 57 ++- unrar/filestr.hpp | 2 +- unrar/find.cpp | 70 ++-- unrar/find.hpp | 10 +- unrar/getbits.cpp | 10 +- unrar/getbits.hpp | 23 +- unrar/hardlinks.cpp | 12 +- unrar/headers.cpp | 4 +- unrar/headers.hpp | 26 +- unrar/headers5.hpp | 34 +- unrar/isnt.cpp | 18 +- unrar/list.cpp | 109 +++-- unrar/loclang.hpp | 21 +- unrar/log.cpp | 23 +- unrar/log.hpp | 3 +- unrar/makefile | 103 +---- unrar/match.cpp | 90 +++-- unrar/match.hpp | 7 +- unrar/options.cpp | 1 + unrar/options.hpp | 28 +- unrar/os.hpp | 82 ++-- unrar/pathfn.cpp | 880 +++++++++++++++++++++++------------------ unrar/pathfn.hpp | 108 +++-- unrar/qopen.cpp | 12 +- unrar/qopen.hpp | 2 +- unrar/rar.cpp | 15 +- unrar/rar.hpp | 1 - unrar/rardefs.hpp | 8 + unrar/raros.hpp | 11 +- unrar/rawint.hpp | 61 ++- unrar/rawread.cpp | 12 +- unrar/rawread.hpp | 4 +- unrar/rdwrfn.cpp | 19 +- unrar/rdwrfn.hpp | 1 - unrar/recvol.cpp | 44 +-- unrar/recvol.hpp | 20 +- unrar/recvol3.cpp | 117 +++--- unrar/recvol5.cpp | 92 ++--- unrar/resource.cpp | 9 - unrar/resource.hpp | 2 - unrar/rijndael.cpp | 36 +- unrar/rijndael.hpp | 15 +- unrar/rs16.hpp | 3 + unrar/scantree.cpp | 165 ++++---- unrar/scantree.hpp | 23 +- unrar/secpassword.cpp | 14 +- unrar/secpassword.hpp | 2 + unrar/sha256.cpp | 10 + unrar/sha256.hpp | 1 + unrar/strfn.cpp | 261 +++++++----- unrar/strfn.hpp | 28 +- unrar/strlist.cpp | 62 ++- unrar/strlist.hpp | 13 +- unrar/system.cpp | 25 +- unrar/system.hpp | 3 +- unrar/timefn.cpp | 6 +- unrar/ui.hpp | 39 +- unrar/uicommon.cpp | 40 +- unrar/uiconsole.cpp | 59 ++- unrar/uisilent.cpp | 19 +- unrar/ulinks.cpp | 52 +-- unrar/unicode.cpp | 266 ++++++++++++- unrar/unicode.hpp | 16 +- unrar/unpack.cpp | 47 ++- unrar/unpack.hpp | 71 ++-- unrar/unpack15.cpp | 4 +- unrar/unpack20.cpp | 5 +- unrar/unpack30.cpp | 62 +-- unrar/unpack50.cpp | 94 +++-- unrar/unpack50frag.cpp | 22 +- unrar/unpack50mt.cpp | 44 ++- unrar/unpackinline.cpp | 40 +- unrar/uowners.cpp | 39 +- unrar/version.hpp | 8 +- unrar/volume.cpp | 89 +++-- unrar/win32acl.cpp | 18 +- unrar/win32lnk.cpp | 50 ++- unrar/win32stm.cpp | 114 +++--- 113 files changed, 3784 insertions(+), 2898 deletions(-) diff --git a/unrar/arccmt.cpp b/unrar/arccmt.cpp index 8b7e498f..74283b5c 100644 --- a/unrar/arccmt.cpp +++ b/unrar/arccmt.cpp @@ -1,6 +1,6 @@ static bool IsAnsiEscComment(const wchar *Data,size_t Size); -bool Archive::GetComment(Array *CmtData) +bool Archive::GetComment(std::wstring &CmtData) { if (!MainComment) return false; @@ -11,7 +11,7 @@ bool Archive::GetComment(Array *CmtData) } -bool Archive::DoGetComment(Array *CmtData) +bool Archive::DoGetComment(std::wstring &CmtData) { #ifndef SFX_MODULE uint CmtLength; @@ -101,10 +101,9 @@ bool Archive::DoGetComment(Array *CmtData) // 4x memory for OEM to UTF-8 output here. OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); #endif - CmtData->Alloc(UnpDataSize+1); - memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); - CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); - CmtData->Alloc(wcslen(CmtData->Addr(0))); +// CmtData.resize(UnpDataSize+1); + CharToWide((const char *)UnpData,CmtData); +// CmtData.resize(wcslen(CmtData->data())); } } } @@ -112,12 +111,12 @@ bool Archive::DoGetComment(Array *CmtData) { if (CmtLength==0) return false; - Array CmtRaw(CmtLength); - int ReadSize=Read(&CmtRaw[0],CmtLength); + std::vector CmtRaw(CmtLength); + int ReadSize=Read(CmtRaw.data(),CmtLength); if (ReadSize>=0 && (uint)ReadSize *CmtData) uiMsg(UIERROR_CMTBROKEN,FileName); return false; } - CmtData->Alloc(CmtLength+1); - CmtRaw.Push(0); +// CmtData.resize(CmtLength+1); + CmtRaw.push_back(0); #ifdef _WIN_ALL // If we ever decide to extend it to Android, we'll need to alloc // 4x memory for OEM to UTF-8 output here. - OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); + OemToCharA((char *)CmtRaw.data(),(char *)CmtRaw.data()); #endif - CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); - CmtData->Alloc(wcslen(CmtData->Addr(0))); + CharToWide((const char *)CmtRaw.data(),CmtData); +// CmtData->resize(wcslen(CmtData->data())); } #endif - return CmtData->Size() > 0; + return CmtData.size() > 0; } -bool Archive::ReadCommentData(Array *CmtData) +bool Archive::ReadCommentData(std::wstring &CmtData) { - Array CmtRaw; + std::vector CmtRaw; if (!ReadSubData(&CmtRaw,NULL,false)) return false; - size_t CmtSize=CmtRaw.Size(); - CmtRaw.Push(0); - CmtData->Alloc(CmtSize+1); + size_t CmtSize=CmtRaw.size(); + CmtRaw.push_back(0); +// CmtData->resize(CmtSize+1); if (Format==RARFMT50) - UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + UtfToWide((char *)CmtRaw.data(),CmtData); else if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) { - RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); - (*CmtData)[CmtSize/2]=0; - + CmtData=RawToWide(CmtRaw); } else { - CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + CharToWide((const char *)CmtRaw.data(),CmtData); } - CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. +// CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length. return true; } @@ -170,15 +167,15 @@ void Archive::ViewComment() { if (Cmd->DisableComment) return; - Array CmtBuf; - if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. + std::wstring CmtBuf; + if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments. { - size_t CmtSize=CmtBuf.Size(); - wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); - if (ChPtr!=NULL) - CmtSize=ChPtr-&CmtBuf[0]; + size_t CmtSize=CmtBuf.size(); + auto EndPos=CmtBuf.find(0x1A); + if (EndPos!=std::wstring::npos) + CmtSize=EndPos; mprintf(L"\n"); - OutComment(&CmtBuf[0],CmtSize); + OutComment(CmtBuf); } } diff --git a/unrar/archive.cpp b/unrar/archive.cpp index 25f0c3b7..90d857fe 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -39,7 +39,6 @@ Archive::Archive(CommandData *InitCmd) VolWrite=0; AddingFilesSize=0; AddingHeadersSize=0; - *FirstVolumeName=0; Splitting=false; NewArchive=false; @@ -74,7 +73,7 @@ void Archive::CheckArc(bool EnableBroken) #if !defined(SFX_MODULE) -void Archive::CheckOpen(const wchar *Name) +void Archive::CheckOpen(const std::wstring &Name) { TOpen(Name); CheckArc(false); @@ -82,7 +81,7 @@ void Archive::CheckOpen(const wchar *Name) #endif -bool Archive::WCheckOpen(const wchar *Name) +bool Archive::WCheckOpen(const std::wstring &Name) { if (!WOpen(Name)) return false; @@ -148,9 +147,9 @@ bool Archive::IsArchive(bool EnableBroken) } else { - Array Buffer(MAXSFXSIZE); + std::vector Buffer(MAXSFXSIZE); long CurPos=(long)Tell(); - int ReadSize=Read(&Buffer[0],Buffer.Size()-16); + int ReadSize=Read(Buffer.data(),Buffer.size()-16); for (int I=0;I0x10000000000ULL) + return 0; + uint64 Pow2=0x20000; // Power of 2 dictionary size. + for (;2*Pow2<=Size;Pow2*=2) + Flags+=FCI_DICT_BIT0; + if (Size==Pow2) + return Size; // If 'Size' is the power of 2, return it as is. + + // Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'. + uint64 Fraction=(Size-Pow2)/(Pow2/32); + Flags+=(uint)Fraction*FCI_DICT_FRACT0; + return Pow2+Fraction*(Pow2/32); +} diff --git a/unrar/archive.hpp b/unrar/archive.hpp index c0019aef..754a2b8f 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -27,7 +27,7 @@ class Archive:public File { private: void UpdateLatestTime(FileHeader *CurBlock); - void ConvertNameCase(wchar *Name); + void ConvertNameCase(std::wstring &Name); void ConvertFileHeader(FileHeader *hd); size_t ReadHeader14(); size_t ReadHeader15(); @@ -36,9 +36,9 @@ class Archive:public File void RequestArcPassword(RarCheckPassword *SelPwd); void UnexpEndArcMsg(); void BrokenHeaderMsg(); - void UnkEncVerMsg(const wchar *Name,const wchar *Info); - bool DoGetComment(Array *CmtData); - bool ReadCommentData(Array *CmtData); + void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info); + bool DoGetComment(std::wstring &CmtData); + bool ReadCommentData(std::wstring &CmtData); #if !defined(RAR_NOCRYPT) CryptData HeadersCrypt; @@ -67,9 +67,9 @@ class Archive:public File size_t SearchRR(); size_t ReadHeader(); void CheckArc(bool EnableBroken); - void CheckOpen(const wchar *Name); - bool WCheckOpen(const wchar *Name); - bool GetComment(Array *CmtData); + void CheckOpen(const std::wstring &Name); + bool WCheckOpen(const std::wstring &Name); + bool GetComment(std::wstring &CmtData); void ViewComment(); void SetLatestTime(RarTime *NewTime); void SeekToNext(); @@ -79,9 +79,9 @@ class Archive:public File void VolSubtractHeaderSize(size_t SubSize); uint FullHeaderSize(size_t Size); int64 GetStartPos(); - void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, + void AddSubData(const byte *SrcData,uint64 DataSize,File *SrcFile, const wchar *Name,uint Flags); - bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode); + bool ReadSubData(std::vector *UnpData,File *DestFile,bool TestMode); HEADER_TYPE GetHeaderType() {return CurHeaderType;} CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} @@ -89,13 +89,18 @@ class Archive:public File void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); #endif #ifdef USE_QOPEN - bool Open(const wchar *Name,uint Mode=FMF_READ); - int Read(void *Data,size_t Size); - void Seek(int64 Offset,int Method); - int64 Tell(); + bool Open(const std::wstring &Name,uint Mode=FMF_READ) override; + int Read(void *Data,size_t Size) override; + void Seek(int64 Offset,int Method) override; + int64 Tell() override; void QOpenUnload() {QOpen.Unload();} void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} #endif + static uint64 GetWinSize(uint64 Size,uint &Flags); + + // Needed to see wstring based Open from File. Otherwise compiler finds + // Open in Archive and doesn't check the base class overloads. + using File::Open; BaseBlock ShortBlock; MarkHeader MarkHead; @@ -140,7 +145,7 @@ class Archive:public File bool NewArchive; - wchar FirstVolumeName[NM]; + std::wstring FirstVolumeName; }; diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index f5ef9aa5..d493d71d 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -119,7 +119,7 @@ void Archive::BrokenHeaderMsg() } -void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info) +void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info) { uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); ErrHandler.SetErrorCode(RARX_WARNING); @@ -222,7 +222,7 @@ size_t Archive::ReadHeader15() { case HEAD_MAIN: MainHead.Reset(); - *(BaseBlock *)&MainHead=ShortBlock; + MainHead.SetBaseBlock(ShortBlock); MainHead.HighPosAV=Raw.Get2(); MainHead.PosAV=Raw.Get4(); @@ -248,7 +248,7 @@ size_t Archive::ReadHeader15() FileHeader *hd=FileBlock ? &FileHead:&SubHead; hd->Reset(); - *(BaseBlock *)hd=ShortBlock; + hd->SetBaseBlock(ShortBlock); hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; @@ -307,7 +307,7 @@ size_t Archive::ReadHeader15() if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) { hd->RedirType=FSREDIR_UNIXSYMLINK; - *hd->RedirName=0; + hd->RedirName.clear(); } hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; @@ -334,27 +334,26 @@ size_t Archive::ReadHeader15() if (hd->UnknownUnpSize) hd->UnpSize=INT64NDF; - char FileName[NM*4]; - size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); - Raw.GetB((byte *)FileName,ReadNameSize); - FileName[ReadNameSize]=0; + size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); + std::string FileName(ReadNameSize,0); + Raw.GetB((byte *)&FileName[0],ReadNameSize); if (FileBlock) { - *hd->FileName=0; + hd->FileName.clear(); if ((hd->Flags & LHD_UNICODE)!=0) { EncodeFileName NameCoder; - size_t Length=strlen(FileName); + size_t Length=strlen(FileName.data()); Length++; if (ReadNameSize>Length) - NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length, - ReadNameSize-Length,hd->FileName, - ASIZE(hd->FileName)); + NameCoder.Decode(FileName.data(),ReadNameSize, + (byte *)&FileName[Length], + ReadNameSize-Length,hd->FileName); } - if (*hd->FileName==0) - ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); + if (hd->FileName.empty()) + ArcCharToWide(FileName.data(),hd->FileName,ACTW_OEM); #ifndef SFX_MODULE ConvertNameCase(hd->FileName); @@ -363,7 +362,7 @@ size_t Archive::ReadHeader15() } else { - CharToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + CharToWide(FileName.data(),hd->FileName); // Calculate the size of optional data. int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); @@ -374,14 +373,15 @@ size_t Archive::ReadHeader15() { // Here we read optional additional fields for subheaders. // They are stored after the file name and before salt. - hd->SubData.Alloc(DataSize); - Raw.GetB(&hd->SubData[0],DataSize); + hd->SubData.resize(DataSize); + Raw.GetB(hd->SubData.data(),DataSize); } if (hd->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; } + if ((hd->Flags & LHD_SALT)!=0) Raw.GetB(hd->Salt,SIZE_SALT30); hd->mtime.SetDos(FileTime); @@ -441,7 +441,7 @@ size_t Archive::ReadHeader15() } break; case HEAD_ENDARC: - *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.SetBaseBlock(ShortBlock); EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; @@ -453,14 +453,14 @@ size_t Archive::ReadHeader15() break; #ifndef SFX_MODULE case HEAD3_CMT: - *(BaseBlock *)&CommHead=ShortBlock; + CommHead.SetBaseBlock(ShortBlock); CommHead.UnpSize=Raw.Get2(); CommHead.UnpVer=Raw.Get1(); CommHead.Method=Raw.Get1(); CommHead.CommCRC=Raw.Get2(); break; case HEAD3_PROTECT: - *(BaseBlock *)&ProtectHead=ShortBlock; + ProtectHead.SetBaseBlock(ShortBlock); ProtectHead.DataSize=Raw.Get4(); ProtectHead.Version=Raw.Get1(); ProtectHead.RecSectors=Raw.Get2(); @@ -469,7 +469,7 @@ size_t Archive::ReadHeader15() NextBlockPos+=ProtectHead.DataSize; break; case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. - *(BaseBlock *)&SubBlockHead=ShortBlock; + SubBlockHead.SetBaseBlock(ShortBlock); SubBlockHead.DataSize=Raw.Get4(); NextBlockPos+=SubBlockHead.DataSize; SubBlockHead.SubType=Raw.Get2(); @@ -490,10 +490,13 @@ size_t Archive::ReadHeader15() StreamHead.Method=Raw.Get1(); StreamHead.StreamCRC=Raw.Get4(); StreamHead.StreamNameSize=Raw.Get2(); - if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) - StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; - Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); - StreamHead.StreamName[StreamHead.StreamNameSize]=0; + + const size_t MaxStreamName20=260; // Maximum allowed stream name in RAR 2.x format. + if (StreamHead.StreamNameSize>MaxStreamName20) + StreamHead.StreamNameSize=MaxStreamName20; + + StreamHead.StreamName.resize(StreamHead.StreamNameSize); + Raw.GetB(&StreamHead.StreamName[0],StreamHead.StreamNameSize); break; } break; @@ -711,13 +714,12 @@ size_t Archive::ReadHeader50() { case HEAD_CRYPT: { - *(BaseBlock *)&CryptHead=ShortBlock; + CryptHead.SetBaseBlock(ShortBlock); uint CryptVersion=(uint)Raw.GetV(); if (CryptVersion>CRYPT_VERSION) { - wchar Info[20]; - swprintf(Info,ASIZE(Info),L"h%u",CryptVersion); - UnkEncVerMsg(FileName,Info); + UnkEncVerMsg(FileName,L"h" + std::to_wstring(CryptVersion)); + FailedHeaderDecryption=true; return 0; } uint EncFlags=(uint)Raw.GetV(); @@ -725,9 +727,8 @@ size_t Archive::ReadHeader50() CryptHead.Lg2Count=Raw.Get1(); if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) { - wchar Info[20]; - swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count); - UnkEncVerMsg(FileName,Info); + UnkEncVerMsg(FileName,L"hc" + std::to_wstring(CryptHead.Lg2Count)); + FailedHeaderDecryption=true; return 0; } @@ -739,12 +740,8 @@ size_t Archive::ReadHeader50() byte csum[SIZE_PSWCHECK_CSUM]; Raw.GetB(csum,SIZE_PSWCHECK_CSUM); - sha256_context ctx; - sha256_init(&ctx); - sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); - byte Digest[SHA256_DIGEST_SIZE]; - sha256_done(&ctx, Digest); + sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest); CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; } @@ -754,7 +751,7 @@ size_t Archive::ReadHeader50() case HEAD_MAIN: { MainHead.Reset(); - *(BaseBlock *)&MainHead=ShortBlock; + MainHead.SetBaseBlock(ShortBlock); uint ArcFlags=(uint)Raw.GetV(); Volume=(ArcFlags & MHFL_VOLUME)!=0; @@ -832,9 +829,14 @@ size_t Archive::ReadHeader50() // we may need to use the compression algorithm 15 in the future, // but it was already used in RAR 1.5 and Unpack needs to distinguish // them. - hd->UnpVer=(CompInfo & 0x3f) + 50; - if (hd->UnpVer!=50) // Only 5.0 compression is known now. - hd->UnpVer=VER_UNKNOWN; + uint UnpVer=(CompInfo & 0x3f); + if (UnpVer==0) + hd->UnpVer=50; + else + if (UnpVer==1) + hd->UnpVer=70; + else + hd->UnpVer=VER_UNKNOWN; hd->HostOS=(byte)Raw.GetV(); size_t NameSize=(size_t)Raw.GetV(); @@ -852,14 +854,20 @@ size_t Archive::ReadHeader50() hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; - hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); + if (hd->Dir || UnpVer>1) + hd->WinSize=0; + else + { + hd->WinSize=0x20000ULL<<((CompInfo>>10)&0x1f); + if (UnpVer==1) + hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f); + } - char FileName[NM*4]; - size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); - Raw.GetB((byte *)FileName,ReadNameSize); - FileName[ReadNameSize]=0; + size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); + std::string FileName(ReadNameSize,0); + Raw.GetB((byte *)&FileName[0],ReadNameSize); - UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + UtfToWide(FileName.data(),hd->FileName); // Should do it before converting names, because extra fields can // affect name processing, like in case of NTFS streams. @@ -884,7 +892,7 @@ size_t Archive::ReadHeader50() break; case HEAD_ENDARC: { - *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.SetBaseBlock(ShortBlock); uint ArcFlags=(uint)Raw.GetV(); EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; EndArcHead.StoreVolNumber=false; @@ -916,7 +924,7 @@ void Archive::RequestArcPassword(RarCheckPassword *CheckPwd) *PasswordA=0; if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) *PasswordA=0; - GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + CharToWide(PasswordA,PasswordW,ASIZE(PasswordW)); cleandata(PasswordA,sizeof(PasswordA)); } Cmd->Password.Set(PasswordW); @@ -991,19 +999,14 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) if ((Flags & MHEXTRA_METADATA_NAME)!=0) { uint64 NameSize=Raw->GetV(); - if (NameSize<0x10000) // Prevent excessive allocation. + if (NameSize>0 && NameSize NameU((size_t)NameSize); // UTF-8 name. + std::string NameU((size_t)NameSize,0); // UTF-8 name. Raw->GetB(&NameU[0],(size_t)NameSize); // If starts from 0, the name was longer than reserved space // when saving this extra field. if (NameU[0]!=0) - { - NameU.push_back(0); - std::vector NameW(NameU.size()*4); - UtfToWide(&NameU[0],&NameW[0],NameW.size()); - hd->OrigName.assign(&NameW[0]); - } + UtfToWide(&NameU[0],hd->OrigName); } } if ((Flags & MHEXTRA_METADATA_CTIME)!=0) @@ -1029,11 +1032,7 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) FileHeader *hd=(FileHeader *)bb; uint EncVersion=(uint)Raw->GetV(); if (EncVersion>CRYPT_VERSION) - { - wchar Info[20]; - swprintf(Info,ASIZE(Info),L"x%u",EncVersion); - UnkEncVerMsg(hd->FileName,Info); - } + UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion)); else { uint Flags=(uint)Raw->GetV(); @@ -1041,11 +1040,7 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; hd->Lg2Count=Raw->Get1(); if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) - { - wchar Info[20]; - swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count); - UnkEncVerMsg(hd->FileName,Info); - } + UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count)); Raw->GetB(hd->Salt,SIZE_SALT50); Raw->GetB(hd->InitV,SIZE_INITV); if (hd->UsePswCheck) @@ -1062,12 +1057,8 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) byte csum[SIZE_PSWCHECK_CSUM]; Raw->GetB(csum,SIZE_PSWCHECK_CSUM); - sha256_context ctx; - sha256_init(&ctx); - sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); - byte Digest[SHA256_DIGEST_SIZE]; - sha256_done(&ctx, Digest); + sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest); hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; @@ -1133,31 +1124,27 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) if (Version!=0) { hd->Version=true; - - wchar VerText[20]; - swprintf(VerText,ASIZE(VerText),L";%u",Version); - wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); + hd->FileName += L';' + std::to_wstring(Version); } } break; case FHEXTRA_REDIR: { - hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); + FILE_SYSTEM_REDIRECT RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); uint Flags=(uint)Raw->GetV(); - hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; size_t NameSize=(size_t)Raw->GetV(); - char UtfName[NM*4]; - *UtfName=0; - if (NameSize0 && NameSizeGetB(UtfName,NameSize); - UtfName[NameSize]=0; - } + std::string UtfName(NameSize,0); + hd->RedirType=RedirType; + hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; + Raw->GetB(&UtfName[0],NameSize); + UtfToWide(&UtfName[0],hd->RedirName); #ifdef _WIN_ALL - UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName)); + UnixSlashToDos(hd->RedirName,hd->RedirName); #endif - UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName)); + } } break; case FHEXTRA_UOWNER: @@ -1214,8 +1201,8 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) // We cannot allocate too much memory here, because above // we check FieldSize againt Raw size and we control that Raw size // is sensible when reading headers. - hd->SubData.Alloc((size_t)FieldSize); - Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); + hd->SubData.resize((size_t)FieldSize); + Raw->GetB(hd->SubData.data(),(size_t)FieldSize); } break; } @@ -1238,7 +1225,7 @@ size_t Archive::ReadHeader14() Raw.GetB(Mark,4); uint HeadSize=Raw.Get2(); if (HeadSize<7) - return false; + return 0; byte Flags=Raw.Get1(); NextBlockPos=CurBlockPos+HeadSize; CurHeaderType=HEAD_MAIN; @@ -1261,7 +1248,7 @@ size_t Archive::ReadHeader14() FileHead.FileHash.CRC32=Raw.Get2(); FileHead.HeadSize=Raw.Get2(); if (FileHead.HeadSize<21) - return false; + return 0; uint FileTime=Raw.Get4(); FileHead.FileAttr=Raw.Get1(); FileHead.Flags=Raw.Get1()|LONG_BLOCK; @@ -1285,12 +1272,13 @@ size_t Archive::ReadHeader14() Raw.Read(NameSize); - char FileName[NM]; - size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); - Raw.GetB((byte *)FileName,ReadNameSize); - FileName[ReadNameSize]=0; - IntToExt(FileName,FileName,ASIZE(FileName)); - CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); + // RAR 1.4 name size is stored in a single byte field and it can't + // exceed 255, so additional checks are not needed. + std::string FileName(NameSize,0); + Raw.GetB((byte *)&FileName[0],NameSize); + std::string NameA; + IntToExt(FileName,NameA); + CharToWide(NameA,FileHead.FileName); ConvertNameCase(FileHead.FileName); ConvertFileHeader(&FileHead); @@ -1304,7 +1292,7 @@ size_t Archive::ReadHeader14() #ifndef SFX_MODULE -void Archive::ConvertNameCase(wchar *Name) +void Archive::ConvertNameCase(std::wstring &Name) { if (Cmd->ConvertNames==NAMES_UPPERCASE) wcsupper(Name); @@ -1322,7 +1310,7 @@ bool Archive::IsArcDir() void Archive::ConvertAttributes() { -#if defined(_WIN_ALL) || defined(_EMX) +#ifdef _WIN_ALL if (FileHead.HSType!=HSYS_WINDOWS) FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; #endif @@ -1387,19 +1375,23 @@ void Archive::ConvertAttributes() void Archive::ConvertFileHeader(FileHeader *hd) { +/* if (hd->HSType==HSYS_UNKNOWN) if (hd->Dir) hd->FileAttr=0x10; else hd->FileAttr=0x20; +*/ #ifdef _WIN_ALL if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. - ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); + ConvertToPrecomposed(hd->FileName); #endif - for (wchar *s=hd->FileName;*s!=0;s++) + for (uint I=0;IFileName.size();I++) { + wchar *s=&hd->FileName[I]; + #ifdef _UNIX // Backslash is the invalid character for Windows file headers, // but it can present in Unix file names extracted in Unix. @@ -1407,7 +1399,7 @@ void Archive::ConvertFileHeader(FileHeader *hd) *s='_'; #endif -#if defined(_WIN_ALL) || defined(_EMX) +#ifdef _WIN_ALL // RAR 5.0 archives do not use '\' as path separator, so if we see it, // it means that it is a part of Unix file name, which we cannot // extract in Windows. @@ -1432,6 +1424,9 @@ void Archive::ConvertFileHeader(FileHeader *hd) if (*s=='/' || *s=='\\' && Format!=RARFMT50) *s=CPATHDIVIDER; } + + // Zeroes inside might be possible in broken Unicode names decoded with EncodeFileName::Decode. + TruncateAtZero(hd->FileName); // Ensure there are no zeroes inside of string. } @@ -1446,7 +1441,7 @@ int64 Archive::GetStartPos() } -bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) +bool Archive::ReadSubData(std::vector *UnpData,File *DestFile,bool TestMode) { if (BrokenHeader) { @@ -1454,7 +1449,7 @@ bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) ErrHandler.SetErrorCode(RARX_CRC); return false; } - if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) + if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK7:VER_UNPACK)) { uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); return false; @@ -1481,7 +1476,7 @@ bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) SubDataIO.SetTestMode(true); else { - UnpData->Alloc((size_t)SubHead.UnpSize); + UnpData->resize((size_t)SubHead.UnpSize); SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); } } @@ -1510,7 +1505,7 @@ bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); ErrHandler.SetErrorCode(RARX_CRC); if (UnpData!=NULL) - UnpData->Reset(); + UnpData->clear(); return false; } return true; diff --git a/unrar/blake2s.cpp b/unrar/blake2s.cpp index 317603da..ae9d4e02 100644 --- a/unrar/blake2s.cpp +++ b/unrar/blake2s.cpp @@ -2,6 +2,20 @@ #include "rar.hpp" +static const byte blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + #ifdef USE_SSE #include "blake2s_sse.cpp" #endif @@ -18,20 +32,6 @@ static const uint32 blake2s_IV[8] = 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL }; -static const byte blake2s_sigma[10][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , -}; - static inline void blake2s_set_lastnode( blake2s_state *S ) { S->f[1] = ~0U; @@ -134,11 +134,7 @@ void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); #ifdef USE_SSE -#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. - if (_SSE_Version>=SSE_SSE2) -#else if (_SSE_Version>=SSE_SSSE3) -#endif blake2s_compress_sse( S, S->buf ); else blake2s_compress( S, S->buf ); // Compress diff --git a/unrar/blake2s_sse.cpp b/unrar/blake2s_sse.cpp index 1a02f210..933fcad8 100644 --- a/unrar/blake2s_sse.cpp +++ b/unrar/blake2s_sse.cpp @@ -1,15 +1,14 @@ // Based on public domain code written in 2012 by Samuel Neves -extern const byte blake2s_sigma[10][16]; - // Initialization vector. static __m128i blake2s_IV_0_3, blake2s_IV_4_7; -#ifdef _WIN_64 -// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. +// Constants for cyclic rotation. static __m128i crotr8, crotr16; -#endif +#ifdef __GNUC__ +__attribute__((target("sse2"))) +#endif static void blake2s_init_sse() { // We cannot initialize these 128 bit variables in place when declaring @@ -24,28 +23,18 @@ static void blake2s_init_sse() blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); -#ifdef _WIN_64 crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); -#endif } #define LOAD(p) _mm_load_si128( (__m128i *)(p) ) #define STORE(p,r) _mm_store_si128((__m128i *)(p), r) -#ifdef _WIN_32 -// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient -// to not use _mm_shuffle_epi8 here. -#define mm_rotr_epi32(r, c) ( \ - _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) -#else #define mm_rotr_epi32(r, c) ( \ c==8 ? _mm_shuffle_epi8(r,crotr8) \ : c==16 ? _mm_shuffle_epi8(r,crotr16) \ : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) -#endif - #define G1(row1,row2,row3,row4,buf) \ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ @@ -73,14 +62,6 @@ static void blake2s_init_sse() row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); -#ifdef _WIN_64 - // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load - // from stack operations, which are slower than this code. - #define _mm_set_epi32(i3,i2,i1,i0) \ - _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ - _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) -#endif - // Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode // and about the same in x64 mode in our test. Perhaps depends on compiler. // We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather @@ -101,6 +82,9 @@ static void blake2s_init_sse() } +#ifdef __GNUC__ +__attribute__((target("ssse3"))) +#endif static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) { __m128i row[4]; diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 3a4ab99e..54b10f23 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -13,8 +13,18 @@ void CommandData::Init() { RAROptions::Init(); - *Command=0; - *ArcName=0; + Command.clear(); + ArcName.clear(); + ExtrPath.clear(); + TempPath.clear(); + SFXModule.clear(); + CommentFile.clear(); + ArcPath.clear(); + ExclArcPath.clear(); + LogName.clear(); + EmailTo.clear(); + UseStdin.clear(); + FileLists=false; NoMoreSwitches=false; @@ -30,57 +40,45 @@ void CommandData::Init() StoreArgs.Reset(); Password.Clean(); NextVolSizes.clear(); -} - - -// Return the pointer to next position in the string and store dynamically -// allocated command line parameter in Par. -static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) -{ - const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); - if (NextCmd==NULL) - return NULL; - size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. - *Par=(wchar *)malloc(ParSize*sizeof(wchar)); - if (*Par==NULL) - return NULL; - return GetCmdParam(CmdLine,*Par,ParSize); +#ifdef RARDLL + DllDestName.clear(); +#endif } #if !defined(SFX_MODULE) void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) { - *Command=0; + Command.clear(); NoMoreSwitches=false; #ifdef CUSTOM_CMDLINE_PARSER // In Windows we may prefer to implement our own command line parser // to avoid replacing \" by " in standard parser. Such replacing corrupts // destination paths like "dest path\" in extraction commands. - const wchar *CmdLine=GetCommandLine(); + std::wstring CmdLine=GetCommandLine(); + + std::wstring Param; + std::wstring::size_type Pos=0; - wchar *Par; for (bool FirstParam=true;;FirstParam=false) { - if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) + if (!GetCmdParam(CmdLine,Pos,Param)) break; if (!FirstParam) // First parameter is the executable name. if (Preprocess) - PreprocessArg(Par); + PreprocessArg(Param.data()); else - ParseArg(Par); - free(Par); + ParseArg(Param.data()); } #else - Array Arg; for (int I=1;I EnvStrW(strlen(EnvStr)+1); - CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size()); - ProcessSwitchesString(&EnvStrW[0]); + std::wstring EnvStr; + CharToWide(EnvVar,EnvStr); + ProcessSwitchesString(EnvStr); } } #endif @@ -201,7 +199,7 @@ void CommandData::PreprocessArg(const wchar *Arg) if (Arg[0]=='-' && Arg[1]==0) // Switch "--". NoMoreSwitches=true; if (wcsicomp(Arg,L"cfg-")==0) - ConfigDisabled=true; + ProcessSwitch(Arg); if (wcsnicomp(Arg,L"ilog",4)==0) { // Ensure that correct log file name is already set @@ -213,13 +211,13 @@ void CommandData::PreprocessArg(const wchar *Arg) { // Process -sc before reading any file lists. ProcessSwitch(Arg); - if (*LogName!=0) + if (!LogName.empty()) InitLogOptions(LogName,ErrlogCharset); } } else - if (*Command==0) - wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini. + if (Command.empty()) + Command=Arg; // Need for rar.ini. } #endif @@ -237,10 +235,10 @@ void CommandData::ReadConfig() Str++; if (wcsnicomp(Str,L"switches=",9)==0) ProcessSwitchesString(Str+9); - if (*Command!=0) + if (!Command.empty()) { wchar Cmd[16]; - wcsncpyz(Cmd,Command,ASIZE(Cmd)); + wcsncpyz(Cmd,Command.c_str(),ASIZE(Cmd)); wchar C0=toupperw(Cmd[0]); wchar C1=toupperw(Cmd[1]); if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V') @@ -260,14 +258,19 @@ void CommandData::ReadConfig() #if !defined(SFX_MODULE) -void CommandData::ProcessSwitchesString(const wchar *Str) +void CommandData::ProcessSwitchesString(const std::wstring &Str) { - wchar *Par; - while ((Str=AllocCmdParam(Str,&Par))!=NULL) + std::wstring Par; + std::wstring::size_type Pos=0; + while (GetCmdParam(Str,Pos,Par)) { - if (IsSwitch(*Par)) - ProcessSwitch(Par+1); - free(Par); + if (IsSwitch(Par[0])) + ProcessSwitch(&Par[1]); + else + { + mprintf(St(MSwSyntaxError),Par.c_str()); + ErrHandler.Exit(RARX_USERERROR); + } } } #endif @@ -330,13 +333,12 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; } break; - case 'N': // Reserved for archive name. - break; case 'O': AddArcOnly=true; break; case 'P': - wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath)); + // Convert slashes here than before every comparison. + SlashToNative(Switch+2,ArcPath); break; case 'S': SyncFiles=true; @@ -347,7 +349,14 @@ void CommandData::ProcessSwitch(const wchar *Switch) } break; case 'C': - if (Switch[2]==0) + if (Switch[2]!=0) + { + if (wcsicomp(Switch+1,L"FG-")==0) + ConfigDisabled=true; + else + BadSwitch(Switch); + } + else switch(toupperw(Switch[1])) { case '-': @@ -359,10 +368,15 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'L': ConvertNames=NAMES_LOWERCASE; break; + default: + BadSwitch(Switch); + break; } break; case 'D': - if (Switch[2]==0) + if (Switch[2]!=0) + BadSwitch(Switch); + else switch(toupperw(Switch[1])) { case 'S': @@ -374,6 +388,9 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'F': DeleteFiles=true; break; + default: + BadSwitch(Switch); + break; } break; case 'E': @@ -395,7 +412,11 @@ void CommandData::ProcessSwitch(const wchar *Switch) ExclPath=EXCL_ABSPATH; break; case '4': - wcsncpyz(ExclArcPath,Switch+3,ASIZE(ExclArcPath)); + // Convert slashes here than before every comparison. + SlashToNative(Switch+3,ArcPath); + break; + default: + BadSwitch(Switch); break; } break; @@ -431,7 +452,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) else if (!Password.IsSet()) { - uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); + uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL); eprintf(L"\n"); } break; @@ -443,7 +464,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'I': if (wcsnicomp(Switch+1,L"LOG",3)==0) { - wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); + LogName=Switch[4]!=0 ? Switch+4:DefLogName; break; } if (wcsnicomp(Switch+1,L"SND",3)==0) @@ -461,7 +482,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) } if (wcsnicomp(Switch+1,L"EML",3)==0) { - wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); + EmailTo=Switch[4]!=0 ? Switch+4:L"@"; break; } if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv. @@ -568,12 +589,14 @@ void CommandData::ProcessSwitch(const wchar *Switch) } switch(toupperw(*(Str++))) { - case 'T': Type=FILTER_PPM; break; +// case 'T': Type=FILTER_TEXT; break; case 'E': Type=FILTER_E8; break; case 'D': Type=FILTER_DELTA; break; - case 'A': Type=FILTER_AUDIO; break; - case 'C': Type=FILTER_RGB; break; - case 'R': Type=FILTER_ARM; break; +// case 'A': Type=FILTER_AUDIO; break; +// case 'C': Type=FILTER_RGB; break; +// case 'R': Type=FILTER_ARM; break; + case 'L': Type=FILTER_LONGRANGE; break; + case 'X': Type=FILTER_EXHAUSTIVE; break; } if (*Str=='+' || *Str=='-') State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE; @@ -586,6 +609,44 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'M': break; case 'D': + { + bool SetMaxDict=toupperw(Switch[2])=='X'; + + uint64 Size=atoiw(Switch+(SetMaxDict ? 3 : 2)); + wchar LastChar=toupperw(Switch[wcslen(Switch)-1]); + if (IsDigit(LastChar)) + LastChar=SetMaxDict ? 'G':'M'; // Treat -md128 as -md128m and -mdx32 as -mdx32g. + switch(LastChar) + { + case 'K': + Size*=1024; + break; + case 'M': + Size*=1024*1024; + break; + case 'G': + Size*=1024*1024*1024; + break; + default: + BadSwitch(Switch); + } + + // 2023.07.22: For 4 GB and less we also check that it is power of 2, + // so archives are compatible with RAR 5.0+. + // We allow Size>PACK_MAX_DICT here, so we can use -md[x] to unpack + // archives created by future versions with higher PACK_MAX_DICTþ + uint Flags; + if ((Size=Archive::GetWinSize(Size,Flags))==0 || + Size<=0x100000000ULL && !IsPow2(Size)) + BadSwitch(Switch); + else + if (SetMaxDict) + MaxWinSize=Size; + else + { + WinSize=Size; + } + } break; case 'E': if (toupperw(Switch[2])=='S' && Switch[3]==0) @@ -593,25 +654,20 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; case 'S': { - wchar StoreNames[1024]; - wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames)); - wchar *Names=StoreNames; - while (*Names!=0) + std::wstring StoreNames=(Switch[2]==0 ? DefaultStoreList:Switch+2); + size_t Pos=0; + while (Pos. + std::wstring ExclArcPath; // For -ep4 switch. + std::wstring LogName; + std::wstring EmailTo; + + // Read data from stdin and store in archive under a name specified here + // when archiving. Read an archive from stdin if any non-empty string + // is specified here when extracting. + std::wstring UseStdin; StringList FileArgs; StringList ExclArgs; @@ -69,6 +82,11 @@ class CommandData:public RAROptions SecPassword Password; std::vector NextVolSizes; + + +#ifdef RARDLL + std::wstring DllDestName; +#endif }; #endif diff --git a/unrar/cmdfilter.cpp b/unrar/cmdfilter.cpp index e0add14b..2a6ff6ee 100644 --- a/unrar/cmdfilter.cpp +++ b/unrar/cmdfilter.cpp @@ -1,7 +1,7 @@ // Return 'true' if we need to exclude the file from processing as result // of -x switch. If CheckInclList is true, we also check the file against // the include list created with -n switch. -bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList) +bool CommandData::ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList) { if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) return true; @@ -13,17 +13,15 @@ bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,b } -bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode) +bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode) { - wchar *Name=ConvertPath(CheckName,NULL,0); - wchar FullName[NM]; - wchar CurMask[NM]; - *FullName=0; + std::wstring Name,FullName,CurMask; + ConvertPath(&CheckName,&Name); Args->Rewind(); - while (Args->GetString(CurMask,ASIZE(CurMask))) + while (Args->GetString(CurMask)) { - wchar *LastMaskChar=PointToLastChar(CurMask); - bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + wchar LastMaskChar=GetLastChar(CurMask); + bool DirMask=IsPathDiv(LastMaskChar); // Mask for directories only. if (Dir) { @@ -33,16 +31,25 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo // We process the directory and have the directory exclusion mask. // So let's convert "mask\" to "mask" and process it normally. - *LastMaskChar=0; + CurMask.pop_back(); } else { - // REMOVED, we want -npath\* to match empty folders too. - // If mask has wildcards in name part and does not have the trailing - // '\' character, we cannot use it for directories. - - // if (IsWildcard(PointToName(CurMask))) - // continue; + // 2023.03.26: Previously we removed this code completely to let + // -npath\* include empty folders in 'path' too. But then we received + // an email from user not willing -x*.avi to exclude folders like + // dir.avi with non-avi files. Also rar.txt mentions that masks like + // *.avi exclude only files. Initially we wanted masks like -npath\* + // or -xpath\* to match the entire contents of path including empty + // folders and added the special check for "*" and "*.*". + // But this is not very straightforward, when *.* and *.avi are + // processed differently, especially taking into account that + // we can specify the exact folder name without wildcards to process + // it and masks like 'dir*\' can be used to exclude folders. + // So we decided to skip all usual wildcard masks for folders. + std::wstring Name=PointToName(CurMask); + if (IsWildcard(Name)/* && wcscmp(Name,L"*")!=0 && wcscmp(Name,L"*.*")!=0*/) + continue; } } else @@ -54,7 +61,7 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo // is excluded from further scanning. if (DirMask) - wcsncatz(CurMask,L"*",ASIZE(CurMask)); + CurMask+=L"*"; } #ifndef SFX_MODULE @@ -66,19 +73,20 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo // correctly. Moreover, removing "*\" from mask would break // the comparison, because now all names have the path. - if (*FullName==0) - ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); + if (FullName.empty()) + ConvertNameToFull(CheckName,FullName); if (CmpName(CurMask,FullName,MatchMode)) return true; } else #endif { - wchar NewName[NM+2],*CurName=Name; + std::wstring CurName=Name; // Important to convert before "*\" check below, so masks like // d:*\something are processed properly. - wchar *CmpMask=ConvertPath(CurMask,NULL,0); + size_t MaskOffset=ConvertPath(&CurMask,nullptr); + std::wstring CmpMask=CurMask.substr(MaskOffset); if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) { @@ -86,10 +94,9 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,boo // but also in the current directory. We convert the name // from 'name' to '.\name' to be matched by "*\" part even if it is // in current directory. - NewName[0]='.'; - NewName[1]=CPATHDIVIDER; - wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); - CurName=NewName; + CurName=L'.'; + CurName+=CPATHDIVIDER; + CurName+=Name; } if (CmpName(CmpMask,CurName,MatchMode)) @@ -277,10 +284,10 @@ bool CommandData::SizeCheck(int64 Size) // Return 0 if file must not be processed or a number of matched parameter otherwise. int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, - bool Flags,wchar *MatchedArg,uint MatchedArgSize) + bool Flags,std::wstring *MatchedArg) { - if (MatchedArg!=NULL && MatchedArgSize>0) - *MatchedArg=0; + if (MatchedArg!=NULL) + MatchedArg->clear(); bool Dir=FileHead.Dir; if (ExclCheck(FileHead.FileName,Dir,false,true)) return 0; @@ -295,17 +302,22 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy if (!Dir && SizeCheck(FileHead.UnpSize)) return 0; #endif - wchar *ArgName; + std::wstring ArgName; FileArgs.Rewind(); - for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) + for (int StringCount=1;FileArgs.GetString(ArgName);StringCount++) + { + // Ensure that both parameters of CmpName are either C++ strings or + // pointers, so we avoid time consuming string construction for one of + // parameters in this expensive loop. if (CmpName(ArgName,FileHead.FileName,MatchType)) { if (ExactMatch!=NULL) *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; if (MatchedArg!=NULL) - wcsncpyz(MatchedArg,ArgName,MatchedArgSize); + *MatchedArg=ArgName; return StringCount; } + } return 0; } diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 6bd1e1ac..e70b7d00 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -103,7 +103,7 @@ void CommandData::OutHelp(RAR_EXIT ExitCode) if (CmpMSGID(Help[I],MCHelpSwOW)) continue; #endif -#if !defined(_WIN_ALL) && !defined(_EMX) +#ifndef _WIN_ALL if (CmpMSGID(Help[I],MCHelpSwAC)) continue; #endif diff --git a/unrar/compress.hpp b/unrar/compress.hpp index 4ef8570f..018909a0 100644 --- a/unrar/compress.hpp +++ b/unrar/compress.hpp @@ -21,10 +21,12 @@ class PackDef static const uint LOW_DIST_REP_COUNT = 16; static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ - static const uint DC = 64; + static const uint DCB = 64; // Base distance codes up to 4 GB. + static const uint DCX = 80; // Extended distance codes up to 1 TB. static const uint LDC = 16; static const uint RC = 44; - static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; + static const uint HUFF_TABLE_SIZEB = NC + DCB + RC + LDC; + static const uint HUFF_TABLE_SIZEX = NC + DCX + RC + LDC; static const uint BC = 20; static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ @@ -43,10 +45,6 @@ class PackDef // Largest alphabet size among all values listed above. static const uint LARGEST_TABLE_SIZE = 306; - enum { - CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, - CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA - }; }; @@ -54,7 +52,10 @@ enum FilterType { // These values must not be changed, because we use them directly // in RAR5 compression and decompression code. FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, - FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_TEXT, + + // These values can be changed. + FILTER_LONGRANGE,FILTER_EXHAUSTIVE,FILTER_NONE }; #endif diff --git a/unrar/consio.cpp b/unrar/consio.cpp index fa35d614..74502d3a 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -5,8 +5,6 @@ static MESSAGE_TYPE MsgStream=MSG_STDOUT; static RAR_CHARSET RedirectCharset=RCH_DEFAULT; static bool ProhibitInput=false; -const int MaxMsgSize=2*NM+2048; - static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; #ifdef _WIN_ALL @@ -71,44 +69,39 @@ void ProhibitConsoleInput() #ifndef SILENT static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { - // This buffer is for format string only, not for entire output, - // so it can be short enough. - wchar fmtw[1024]; - PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); + // No need for PrintfPrepareFmt here, vwstrprintf calls it. + std::wstring s=vwstrprintf(fmt,arglist); #ifdef _WIN_ALL - safebuf wchar Msg[MaxMsgSize]; if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) { HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); - vswprintf(Msg,ASIZE(Msg),fmtw,arglist); DWORD Written; if (RedirectCharset==RCH_UNICODE) - WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL); + WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL); else { // Avoid Unicode for redirect in Windows, it does not work with pipes. - safebuf char MsgA[MaxMsgSize]; + std::string MsgA; if (RedirectCharset==RCH_UTF8) - WideToUtf(Msg,MsgA,ASIZE(MsgA)); + WideToUtf(s,MsgA); else - WideToChar(Msg,MsgA,ASIZE(MsgA)); + WideToChar(s,MsgA); if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) - CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding. + CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding. // We already converted \n to \r\n above, so we use WriteFile instead // of C library to avoid unnecessary additional conversion. - WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL); + WriteFile(hOut,MsgA.data(),(DWORD)MsgA.size(),&Written,NULL); } return; } // MSVC2008 vfwprintf writes every character to console separately // and it is too slow. We use direct WriteConsole call instead. - vswprintf(Msg,ASIZE(Msg),fmtw,arglist); HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); DWORD Written; - WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL); + WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL); #else - vfwprintf(dest,fmtw,arglist); + fputws(s.c_str(),dest); // We do not use setbuf(NULL) in Unix (see comments in InitConsole). fflush(dest); #endif @@ -160,13 +153,11 @@ static void QuitIfInputProhibited() } -static void GetPasswordText(wchar *Str,uint MaxLength) +static void GetPasswordText(std::wstring &Str) { - if (MaxLength==0) - return; QuitIfInputProhibited(); if (StdinRedirected) - getwstr(Str,MaxLength); // Read from pipe or redirected file. + getwstr(Str); // Read from pipe or redirected file. else { #ifdef _WIN_ALL @@ -175,50 +166,47 @@ static void GetPasswordText(wchar *Str,uint MaxLength) GetConsoleMode(hConIn,&ConInMode); SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. + std::vector Buf(MAXPASSWORD); + // We prefer ReadConsole to ReadFile, so we can read Unicode input. DWORD Read=0; - ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); - Str[Read]=0; + ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL); + Buf[Read]=0; + Str=Buf.data(); + cleandata(Buf.data(),Buf.size()*sizeof(Buf[0])); + SetConsoleMode(hConIn,ConInMode); - // If entered password is longer than MAXPASSWORD and truncated, - // read its unread part anyway, so it isn't read later as the second - // password for -p switch. Low level FlushConsoleInputBuffer doesn't help + // 2023.03.12: Previously we checked for presence of "\n" in entered + // passwords, supposing that truncated strings do not include it. + // We did it to read the rest of excessively long string, so it is not + // read later as the second password for -p switch. But this "\n" check + // doesn't seem to work in Windows 10 anymore and "\r" is present even + // in truncated strings. Also we increased MAXPASSWORD, so it is larger + // than MAXPASSWORD_RAR. Thus we removed this check as not working + // and not that necessary. Low level FlushConsoleInputBuffer doesn't help // for high level ReadConsole, which in line input mode seems to store // the rest of string in its own internal buffer. - if (wcschr(Str,'\r')==NULL) // If '\r' is missing, the password was truncated. - while (true) - { - wchar Trail[64]; - DWORD TrailRead=0; - // Use ASIZE(Trail)-1 to reserve the space for trailing 0. - ReadConsole(hConIn,Trail,ASIZE(Trail)-1,&TrailRead,NULL); - Trail[TrailRead]=0; - if (TrailRead==0 || wcschr(Trail,'\r')!=NULL) - break; - } - #else - char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters. -#if defined(_EMX) || defined (__VMS) - fgets(StrA,ASIZE(StrA)-1,stdin); + std::vector StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters. +#ifdef __VMS + fgets(StrA.data(),StrA.size()-1,stdin); #elif defined(__sun) - strncpyz(StrA,getpassphrase(""),ASIZE(StrA)); + strncpyz(StrA.data(),getpassphrase(""),StrA.size()); #else - strncpyz(StrA,getpass(""),ASIZE(StrA)); + strncpyz(StrA.data(),getpass(""),StrA.size()); #endif - CharToWide(StrA,Str,MaxLength); - cleandata(StrA,sizeof(StrA)); + CharToWide(StrA.data(),Str); + cleandata(StrA.data(),StrA.size()*sizeof(StrA[0])); #endif } - Str[MaxLength-1]=0; RemoveLF(Str); } #endif #ifndef SILENT -bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password) { if (!StdinRedirected) uiAlarm(UIALARM_QUESTION); @@ -229,33 +217,33 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword * if (Type==UIPASSWORD_GLOBAL) eprintf(L"\n%s: ",St(MAskPsw)); else - eprintf(St(MAskPswFor),FileName); + eprintf(St(MAskPswFor),FileName.c_str()); - wchar PlainPsw[MAXPASSWORD+1]; - GetPasswordText(PlainPsw,ASIZE(PlainPsw)); - if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) + std::wstring PlainPsw; + GetPasswordText(PlainPsw); + if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL) return false; - if (wcslen(PlainPsw)>=MAXPASSWORD) + if (PlainPsw.size()>=MAXPASSWORD) { - PlainPsw[MAXPASSWORD-1]=0; + PlainPsw.erase(MAXPASSWORD-1); uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); } if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) { eprintf(St(MReAskPsw)); - wchar CmpStr[MAXPASSWORD]; - GetPasswordText(CmpStr,ASIZE(CmpStr)); - if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0) + std::wstring CmpStr; + GetPasswordText(CmpStr); + if (CmpStr.empty() || PlainPsw!=CmpStr) { eprintf(St(MNotMatchPsw)); - cleandata(PlainPsw,sizeof(PlainPsw)); - cleandata(CmpStr,sizeof(CmpStr)); + cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); + cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); continue; } - cleandata(CmpStr,sizeof(CmpStr)); + cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); } - Password->Set(PlainPsw); - cleandata(PlainPsw,sizeof(PlainPsw)); + Password->Set(PlainPsw.c_str()); + cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); break; } return true; @@ -264,14 +252,17 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword * #ifndef SILENT -bool getwstr(wchar *str,size_t n) +bool getwstr(std::wstring &str) { // Print buffered prompt title function before waiting for input. fflush(stderr); QuitIfInputProhibited(); - *str=0; + str.clear(); + + const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name. + #if defined(_WIN_ALL) // fgetws does not work well with non-English text in Windows, // so we do not use it. @@ -279,11 +270,11 @@ bool getwstr(wchar *str,size_t n) { // fgets does not work well with pipes in Windows in our test. // Let's use files. - Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. + std::vector StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t. File SrcFile; SrcFile.SetHandleType(FILE_HANDLESTD); SrcFile.SetLineInputMode(true); - int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); + int ReadSize=SrcFile.Read(&StrA[0],StrA.size()-1); if (ReadSize<=0) { // Looks like stdin is a null device. We can enter to infinite loop @@ -297,19 +288,23 @@ bool getwstr(wchar *str,size_t n) // 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. + CharToWide(&StrA[0],str); + cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords. } else { + std::vector Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t. DWORD ReadSize=0; - if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0) + if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],(DWORD)Buf.size()-1,&ReadSize,NULL)==0) return false; - str[ReadSize]=0; + Buf[ReadSize]=0; + str=Buf.data(); } #else - if (fgetws(str,n,stdin)==NULL) + 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. + str=Buf.data(); #endif RemoveLF(str); return true; @@ -361,8 +356,8 @@ int Ask(const wchar *AskStr) eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]); } eprintf(L" "); - wchar Str[50]; - getwstr(Str,ASIZE(Str)); + std::wstring Str; + getwstr(Str); wchar Ch=toupperw(Str[0]); for (int I=0;I[{key};"{string}"p used to redefine // a keyboard key on some terminals. @@ -389,18 +384,16 @@ static bool IsCommentUnsafe(const wchar *Data,size_t Size) } -void OutComment(const wchar *Comment,size_t Size) +void OutComment(const std::wstring &Comment) { - if (IsCommentUnsafe(Comment,Size)) + if (IsCommentUnsafe(Comment)) return; const size_t MaxOutSize=0x400; - for (size_t I=0;I0 && ((size_t)Data & 7);Size--,Data++) StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); diff --git a/unrar/crypt.cpp b/unrar/crypt.cpp index d14c78f8..3a9342cd 100644 --- a/unrar/crypt.cpp +++ b/unrar/crypt.cpp @@ -111,7 +111,7 @@ void GetRnd(byte *RndBuf,size_t BufSize) HCRYPTPROV hProvider = 0; if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; + Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) != FALSE; CryptReleaseContext(hProvider, 0); } #elif defined(_UNIX) diff --git a/unrar/crypt.hpp b/unrar/crypt.hpp index 9dc02159..f7d97e8a 100644 --- a/unrar/crypt.hpp +++ b/unrar/crypt.hpp @@ -103,7 +103,6 @@ class CryptData bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt, byte *HashKey,byte *PswCheck); - void SetAV15Encryption(); void SetCmt13Encryption(); void EncryptBlock(byte *Buf,size_t Size); void DecryptBlock(byte *Buf,size_t Size); diff --git a/unrar/crypt1.cpp b/unrar/crypt1.cpp index 14263937..93a6e815 100644 --- a/unrar/crypt1.cpp +++ b/unrar/crypt1.cpp @@ -1,5 +1,3 @@ -extern uint CRCTab[256]; - void CryptData::SetKey13(const char *Password) { Key13[0]=Key13[1]=Key13[2]=0; @@ -30,17 +28,6 @@ void CryptData::SetKey15(const char *Password) } -void CryptData::SetAV15Encryption() -{ - InitCRC32(CRCTab); - Method=CRYPT_RAR15; - Key15[0]=0x4765; - Key15[1]=0x9021; - Key15[2]=0x7382; - Key15[3]=0x5215; -} - - void CryptData::SetCmt13Encryption() { Method=CRYPT_RAR13; diff --git a/unrar/crypt5.cpp b/unrar/crypt5.cpp index 5ed65af8..bb9b2bab 100644 --- a/unrar/crypt5.cpp +++ b/unrar/crypt5.cpp @@ -133,7 +133,15 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, byte *PswCheck) { if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) + { + // Initialize these fields to prevent uninitialized data access warnings + // by analyzing tools when accessing returned data. + if (HashKey!=nullptr) + memset(HashKey,0,SHA256_DIGEST_SIZE); + if (PswCheck!=nullptr) + memset(PswCheck,0,SIZE_PSWCHECK); return; + } byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; bool Found=false; diff --git a/unrar/dll.cpp b/unrar/dll.cpp index 31818e49..cc5c5672 100644 --- a/unrar/dll.cpp +++ b/unrar/dll.cpp @@ -44,22 +44,21 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) Data->Cmd.FileArgs.AddString(L"*"); Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0; - char AnsiArcName[NM]; - *AnsiArcName=0; - if (r->ArcName!=NULL) + std::string AnsiArcName; + if (r->ArcName!=nullptr) { - strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); + AnsiArcName=r->ArcName; #ifdef _WIN_ALL if (!AreFileApisANSI()) - { - OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); - AnsiArcName[ASIZE(AnsiArcName)-1]=0; - } + IntToExt(r->ArcName,AnsiArcName); #endif } - wchar ArcName[NM]; - GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); + std::wstring ArcName; + if (r->ArcNameW!=nullptr && *r->ArcNameW!=0) + ArcName=r->ArcNameW; + else + CharToWide(AnsiArcName,ArcName); Data->Cmd.AddArcName(ArcName); Data->Cmd.Overwrite=OVERWRITE_ALL; @@ -113,35 +112,35 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) if (Data->Arc.FirstVolume) r->Flags|=ROADF_FIRSTVOLUME; - Array CmtDataW; - if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) + std::wstring CmtDataW; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW)) { if (r->CmtBufW!=NULL) { - CmtDataW.Push(0); - size_t Size=wcslen(&CmtDataW[0])+1; +// CmtDataW.push_back(0); + size_t Size=wcslen(CmtDataW.data())+1; r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; r->CmtSize=(uint)Min(Size,r->CmtBufSize); - memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW)); + memcpy(r->CmtBufW,CmtDataW.data(),(r->CmtSize-1)*sizeof(*r->CmtBufW)); r->CmtBufW[r->CmtSize-1]=0; } else if (r->CmtBuf!=NULL) { - Array CmtData(CmtDataW.Size()*4+1); - memset(&CmtData[0],0,CmtData.Size()); - WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); - size_t Size=strlen(&CmtData[0])+1; + std::vector CmtData(CmtDataW.size()*4+1); + WideToChar(&CmtDataW[0],&CmtData[0],CmtData.size()-1); + size_t Size=strlen(CmtData.data())+1; r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; r->CmtSize=(uint)Min(Size,r->CmtBufSize); - memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + memcpy(r->CmtBuf,CmtData.data(),r->CmtSize-1); r->CmtBuf[r->CmtSize-1]=0; } } else r->CmtState=r->CmtSize=0; + Data->Extract.ExtractArchiveInit(Data->Arc); return (HANDLE)Data; } @@ -183,8 +182,7 @@ int PASCAL RARCloseArchive(HANDLE hArcData) int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) { - struct RARHeaderDataEx X; - memset(&X,0,sizeof(X)); + struct RARHeaderDataEx X{}; int Code=RARReadHeaderEx(hArcData,&X); @@ -242,14 +240,18 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) else return Code; } - wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); + wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW)); WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); + if (D->ArcNameEx!=nullptr) + wcsncpyz(D->ArcNameEx,Data->Arc.FileName.c_str(),D->ArcNameExSize); - wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); + wcsncpyz(D->FileNameW,hd->FileName.c_str(),ASIZE(D->FileNameW)); WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); #ifdef _WIN_ALL CharToOemA(D->FileName,D->FileName); #endif + if (D->FileNameEx!=nullptr) + wcsncpyz(D->FileNameEx,hd->FileName.c_str(),D->FileNameExSize); D->Flags=0; if (hd->SplitBefore) @@ -311,7 +313,7 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) // this RedirNameSize check sometimes later. if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && D->RedirNameSize>0 && D->RedirNameSize<100000) - wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); + wcsncpyz(D->RedirName,hd->RedirName.c_str(),D->RedirNameSize); D->DirTarget=hd->DirTarget; } catch (RAR_EXIT ErrCode) @@ -346,45 +348,43 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa { Data->Cmd.DllOpMode=Operation; - *Data->Cmd.ExtrPath=0; - *Data->Cmd.DllDestName=0; + Data->Cmd.ExtrPath.clear(); + Data->Cmd.DllDestName.clear(); if (DestPath!=NULL) { - char ExtrPathA[NM]; - strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); + std::string ExtrPathA=DestPath; #ifdef _WIN_ALL // We must not apply OemToCharBuffA directly to DestPath, // because we do not know DestPath length and OemToCharBuffA // does not stop at 0. - OemToCharA(ExtrPathA,ExtrPathA); + IntToExt(ExtrPathA,ExtrPathA); #endif - CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); - AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + CharToWide(ExtrPathA,Data->Cmd.ExtrPath); + AddEndSlash(Data->Cmd.ExtrPath); } if (DestName!=NULL) { - char DestNameA[NM]; - strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); + std::string DestNameA=DestName; #ifdef _WIN_ALL // We must not apply OemToCharBuffA directly to DestName, // because we do not know DestName length and OemToCharBuffA // does not stop at 0. - OemToCharA(DestNameA,DestNameA); + IntToExt(DestNameA,DestNameA); #endif - CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); + CharToWide(DestNameA,Data->Cmd.DllDestName); } if (DestPathW!=NULL) { - wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); - AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + Data->Cmd.ExtrPath=DestPathW; + AddEndSlash(Data->Cmd.ExtrPath); } if (DestNameW!=NULL) - wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); + Data->Cmd.DllDestName=DestNameW; - wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command)); + Data->Cmd.Command=Operation==RAR_EXTRACT ? L"X":L"T"; Data->Cmd.Test=Operation!=RAR_EXTRACT; bool Repeat=false; Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); @@ -456,7 +456,7 @@ void PASCAL RARSetPassword(HANDLE hArcData,char *Password) #ifndef RAR_NOCRYPT DataSet *Data=(DataSet *)hArcData; wchar PasswordW[MAXPASSWORD]; - GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + CharToWide(Password,PasswordW,ASIZE(PasswordW)); Data->Cmd.Password.Set(PasswordW); cleandata(PasswordW,sizeof(PasswordW)); #endif diff --git a/unrar/dll.hpp b/unrar/dll.hpp index c785ff18..06ef7022 100644 --- a/unrar/dll.hpp +++ b/unrar/dll.hpp @@ -19,6 +19,7 @@ #define ERAR_MISSING_PASSWORD 22 #define ERAR_EREFERENCE 23 #define ERAR_BAD_PASSWORD 24 +#define ERAR_LARGE_DICT 25 #define RAR_OM_LIST 0 #define RAR_OM_EXTRACT 1 @@ -108,7 +109,11 @@ struct RARHeaderDataEx unsigned int CtimeHigh; unsigned int AtimeLow; unsigned int AtimeHigh; - unsigned int Reserved[988]; + wchar_t *ArcNameEx; + unsigned int ArcNameExSize; + wchar_t *FileNameEx; + unsigned int FileNameExSize; + unsigned int Reserved[982]; }; @@ -157,7 +162,7 @@ struct RAROpenArchiveDataEx enum UNRARCALLBACK_MESSAGES { UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, - UCM_NEEDPASSWORDW + UCM_NEEDPASSWORDW,UCM_LARGEDICT }; typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); diff --git a/unrar/dll.rc b/unrar/dll.rc index 9cd0d7ec..f672364d 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 23, 1, 931 -PRODUCTVERSION 6, 23, 1, 931 +FILEVERSION 7, 0, 1, 1026 +PRODUCTVERSION 7, 0, 1, 1026 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", "6.23.1\0" - VALUE "ProductVersion", "6.23.1\0" + VALUE "FileVersion", "7.0.1\0" + VALUE "ProductVersion", "7.0.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/encname.cpp b/unrar/encname.cpp index e1ba1ed7..d602090e 100644 --- a/unrar/encname.cpp +++ b/unrar/encname.cpp @@ -11,12 +11,13 @@ EncodeFileName::EncodeFileName() -void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize, - wchar *NameW,size_t MaxDecSize) +void EncodeFileName::Decode(const char *Name,size_t NameSize, + const byte *EncName,size_t EncSize, + std::wstring &NameW) { size_t EncPos=0,DecPos=0; byte HighByte=EncPos=EncSize) break; + // We need DecPos also for ASCII "Name", so resize() instead of push_back(). + NameW.resize(DecPos+1); NameW[DecPos++]=EncName[EncPos++]; break; case 1: if (EncPos>=EncSize) break; + NameW.resize(DecPos+1); NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); break; case 2: if (EncPos+1>=EncSize) break; + NameW.resize(DecPos+1); NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); EncPos+=2; break; @@ -51,17 +56,22 @@ void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncS if (EncPos>=EncSize) break; byte Correction=EncName[EncPos++]; - for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos0 && DecPos0 && DecPos &EncName); byte Flags; uint FlagBits; @@ -12,8 +12,8 @@ class EncodeFileName size_t DestSize; public: EncodeFileName(); - size_t Encode(char *Name,wchar *NameW,byte *EncName); - void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize); + void Encode(const std::string &Name,const std::wstring &NameW,std::vector &EncName); + void Decode(const char *Name,size_t NameSize,const byte *EncName,size_t EncSize,std::wstring &NameW); }; #endif diff --git a/unrar/errhnd.cpp b/unrar/errhnd.cpp index 97193e5a..b07e2f66 100644 --- a/unrar/errhnd.cpp +++ b/unrar/errhnd.cpp @@ -26,7 +26,7 @@ void ErrorHandler::MemoryError() } -void ErrorHandler::OpenError(const wchar *FileName) +void ErrorHandler::OpenError(const std::wstring &FileName) { #ifndef SILENT OpenErrorMsg(FileName); @@ -35,7 +35,7 @@ void ErrorHandler::OpenError(const wchar *FileName) } -void ErrorHandler::CloseError(const wchar *FileName) +void ErrorHandler::CloseError(const std::wstring &FileName) { if (!UserBreak) { @@ -51,7 +51,7 @@ void ErrorHandler::CloseError(const wchar *FileName) } -void ErrorHandler::ReadError(const wchar *FileName) +void ErrorHandler::ReadError(const std::wstring &FileName) { #ifndef SILENT ReadErrorMsg(FileName); @@ -62,13 +62,13 @@ void ErrorHandler::ReadError(const wchar *FileName) } -void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit) +void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit) { SetErrorCode(RARX_READ); #if !defined(SILENT) && !defined(SFX_MODULE) if (!Silent) { - uiMsg(UIERROR_FILEREAD,UINULL,FileName); + uiMsg(UIERROR_FILEREAD,L"",FileName); SysErrMsg(); if (ReadErrIgnoreAll) Ignore=true; @@ -88,7 +88,7 @@ void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry, } -void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &FileName) { #ifndef SILENT WriteErrorMsg(ArcName,FileName); @@ -100,7 +100,7 @@ void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName) #ifdef _WIN_ALL -void ErrorHandler::WriteErrorFAT(const wchar *FileName) +void ErrorHandler::WriteErrorFAT(const std::wstring &FileName) { SysErrMsg(); uiMsg(UIERROR_NTFSREQUIRED,FileName); @@ -111,7 +111,7 @@ void ErrorHandler::WriteErrorFAT(const wchar *FileName) #endif -bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull) +bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull) { #ifndef SILENT if (!Silent) @@ -129,7 +129,7 @@ bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull) } -void ErrorHandler::SeekError(const wchar *FileName) +void ErrorHandler::SeekError(const std::wstring &FileName) { if (!UserBreak) { @@ -144,13 +144,16 @@ void ErrorHandler::SeekError(const wchar *FileName) void ErrorHandler::GeneralErrMsg(const wchar *fmt,...) { +#ifndef RARDLL va_list arglist; va_start(arglist,fmt); - wchar Msg[1024]; - vswprintf(Msg,ASIZE(Msg),fmt,arglist); + + std::wstring Msg=vwstrprintf(fmt,arglist); uiMsg(UIERROR_GENERALERRMSG,Msg); SysErrMsg(); + va_end(arglist); +#endif } @@ -161,13 +164,13 @@ void ErrorHandler::MemoryErrorMsg() } -void ErrorHandler::OpenErrorMsg(const wchar *FileName) +void ErrorHandler::OpenErrorMsg(const std::wstring &FileName) { - OpenErrorMsg(NULL,FileName); + OpenErrorMsg(L"",FileName); } -void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_FILEOPEN,ArcName,FileName); SysErrMsg(); @@ -179,13 +182,13 @@ void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) } -void ErrorHandler::CreateErrorMsg(const wchar *FileName) +void ErrorHandler::CreateErrorMsg(const std::wstring &FileName) { - CreateErrorMsg(NULL,FileName); + CreateErrorMsg(L"",FileName); } -void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_FILECREATE,ArcName,FileName); SysErrMsg(); @@ -193,13 +196,13 @@ void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName) } -void ErrorHandler::ReadErrorMsg(const wchar *FileName) +void ErrorHandler::ReadErrorMsg(const std::wstring &FileName) { - ReadErrorMsg(NULL,FileName); + ReadErrorMsg(L"",FileName); } -void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_FILEREAD,ArcName,FileName); SysErrMsg(); @@ -207,7 +210,7 @@ void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) } -void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_FILEWRITE,ArcName,FileName); SysErrMsg(); @@ -215,21 +218,21 @@ void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) } -void ErrorHandler::ArcBrokenMsg(const wchar *ArcName) +void ErrorHandler::ArcBrokenMsg(const std::wstring &ArcName) { uiMsg(UIERROR_ARCBROKEN,ArcName); SetErrorCode(RARX_CRC); } -void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_CHECKSUM,ArcName,FileName); SetErrorCode(RARX_CRC); } -void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName) +void ErrorHandler::UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName) { uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName); ErrHandler.SetErrorCode(RARX_FATAL); @@ -332,33 +335,44 @@ void ErrorHandler::Throw(RAR_EXIT Code) if (Code==RARX_USERBREAK && !EnableBreak) return; #if !defined(SILENT) - // Do not write "aborted" when just displaying online help. - if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) - mprintf(L"\n%s\n",St(MProgAborted)); + if (Code!=RARX_SUCCESS) + if (Code==RARX_USERERROR) // Do not write "aborted" when just displaying the online help. + mprintf(L"\n"); // For consistency with other errors, which print the final "\n". + else + mprintf(L"\n%s\n",St(MProgAborted)); #endif SetErrorCode(Code); throw Code; } -bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) +bool ErrorHandler::GetSysErrMsg(std::wstring &Msg) { #ifndef SILENT #ifdef _WIN_ALL int ErrType=GetLastError(); if (ErrType!=0) - return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, - NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), - Msg,(DWORD)Size,NULL)!=0; + { + wchar *Buf=nullptr; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM| + FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), + (LPTSTR)&Buf,0,NULL)!=0) + { + Msg=Buf; + LocalFree(Buf); + return true; + } + } #endif -#if defined(_UNIX) || defined(_EMX) +#ifdef _UNIX if (errno!=0) { char *err=strerror(errno); if (err!=NULL) { - CharToWide(err,Msg,Size); + CharToWide(err,Msg); return true; } } @@ -371,31 +385,26 @@ bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) void ErrorHandler::SysErrMsg() { #ifndef SILENT - wchar Msg[1024]; - if (!GetSysErrMsg(Msg,ASIZE(Msg))) + std::wstring Msg; + if (!GetSysErrMsg(Msg)) return; #ifdef _WIN_ALL - wchar *CurMsg=Msg; - while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines. + // Print string with \r\n as several strings to multiple lines. + size_t Pos=0; + while (Pos!=std::wstring::npos) { - while (*CurMsg=='\r' || *CurMsg=='\n') - CurMsg++; - if (*CurMsg==0) + while (Msg[Pos]=='\r' || Msg[Pos]=='\n') + Pos++; + if (Pos==Msg.size()) break; - wchar *EndMsg=wcschr(CurMsg,'\r'); - if (EndMsg==NULL) - EndMsg=wcschr(CurMsg,'\n'); - if (EndMsg!=NULL) - { - *EndMsg=0; - EndMsg++; - } + size_t EndPos=Msg.find_first_of(L"\r\n",Pos); + std::wstring CurMsg=Msg.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos); uiMsg(UIERROR_SYSERRMSG,CurMsg); - CurMsg=EndMsg; + Pos=EndPos; } #endif -#if defined(_UNIX) || defined(_EMX) +#ifdef _UNIX uiMsg(UIERROR_SYSERRMSG,Msg); #endif diff --git a/unrar/errhnd.hpp b/unrar/errhnd.hpp index 06f4f616..3b558b10 100644 --- a/unrar/errhnd.hpp +++ b/unrar/errhnd.hpp @@ -33,26 +33,26 @@ class ErrorHandler ErrorHandler(); void Clean(); void MemoryError(); - void OpenError(const wchar *FileName); - void CloseError(const wchar *FileName); - void ReadError(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); - void SeekError(const wchar *FileName); + void OpenError(const std::wstring &FileName); + void CloseError(const std::wstring &FileName); + void ReadError(const std::wstring &FileName); + void AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit); + void WriteError(const std::wstring &ArcName,const std::wstring &FileName); + void WriteErrorFAT(const std::wstring &FileName); + bool AskRepeatWrite(const std::wstring &FileName,bool DiskFull); + void SeekError(const std::wstring &FileName); void GeneralErrMsg(const wchar *fmt,...); void MemoryErrorMsg(); - void OpenErrorMsg(const wchar *FileName); - void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); - void CreateErrorMsg(const wchar *FileName); - void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); - void ReadErrorMsg(const wchar *FileName); - void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); - void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); - void ArcBrokenMsg(const wchar *ArcName); - void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); - void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); + void OpenErrorMsg(const std::wstring &FileName); + void OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); + void CreateErrorMsg(const std::wstring &FileName); + void CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); + void ReadErrorMsg(const std::wstring &FileName); + void ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); + void WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName); + void ArcBrokenMsg(const std::wstring &ArcName); + void ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName); + void UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName); void Exit(RAR_EXIT ExitCode); void SetErrorCode(RAR_EXIT Code); RAR_EXIT GetErrorCode() {return ExitCode;} @@ -60,7 +60,7 @@ class ErrorHandler void SetSignalHandlers(bool Enable); void Throw(RAR_EXIT Code); void SetSilent(bool Mode) {Silent=Mode;} - bool GetSysErrMsg(wchar *Msg,size_t Size); + bool GetSysErrMsg(std::wstring &Msg); void SysErrMsg(); int GetSystemErrorCode(); void SetSystemErrorCode(int Code); diff --git a/unrar/extinfo.cpp b/unrar/extinfo.cpp index 4bf8046a..b3fe7495 100644 --- a/unrar/extinfo.cpp +++ b/unrar/extinfo.cpp @@ -19,7 +19,7 @@ // RAR2 service header extra records. #ifndef SFX_MODULE -void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name) { #ifdef _WIN_ALL if (Cmd->Test) @@ -40,12 +40,12 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) // RAR3 and RAR5 service header extra records. -void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) +void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name) { #ifdef _UNIX if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) - ExtractUnixOwner30(Arc,Name); + ExtractUnixOwner30(Arc,Name.c_str()); #endif #ifdef _WIN_ALL if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) @@ -57,7 +57,7 @@ void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) // Extra data stored directly in file header. -void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name) { #ifdef _UNIX if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) @@ -68,36 +68,34 @@ void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) -// Calculate a number of path components except \. and \.. -static int CalcAllowedDepth(const wchar *Name) +// Calculate the number of path components except \. and \.. +static int CalcAllowedDepth(const std::wstring &Name) { int AllowedDepth=0; - while (*Name!=0) - { - if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) + for (size_t I=0;I=ASIZE(Path)) - return true; // It should not be that long, skip. - wcsncpyz(Path,Name,ASIZE(Path)); - for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) - if (IsPathDiv(*s)) + if (Path.empty()) // So we can safely use Path.size()-1 below. + return false; + for (size_t I=Path.size()-1;I>0;I--) + if (IsPathDiv(Path[I])) { - *s=0; + Path.erase(I); FindData FD; if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) return true; @@ -106,69 +104,7 @@ static bool LinkInPath(const wchar *Name) } -// Delete symbolic links in file path, if any, and replace them by directories. -// Prevents extracting files outside of destination folder with symlink chains. -bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked) -{ - // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like - // "lnk1/../dir", but converts the path to "dir". In Unix we need to call - // this function to prevent placing unpacked files outside of destination - // folder if previously we unpacked "dir/lnk1" -> "..", - // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". - // We may still need this function to prevent abusing symlink chains - // in link source path if we remove detection of such chains - // in IsRelativeSymlinkSafe. This function seems to make other symlink - // related safety checks redundant, but for now we prefer to keep them too. - // - // 2022.12.01: the performance impact is minimized after adding the check - // against the previous path and enabling this verification only after - // extracting a symlink with ".." in target. So we enabled it for Windows - // as well for extra safety. -//#ifdef _UNIX - wchar Path[NM]; - if (wcslen(SrcName)>=ASIZE(Path)) - return false; // It should not be that long, skip. - wcsncpyz(Path,SrcName,ASIZE(Path)); - - size_t SkipLength=wcslen(SkipPart); - - if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0) - SkipLength=0; // Parameter validation, not really needed now. - - // Do not check parts already checked in previous path to improve performance. - for (uint I=0;Path[I]!=0 && ISkipLength) - SkipLength=I; - - wchar *Name=Path; - if (SkipLength>0) - { - // Avoid converting symlinks in destination path part specified by user. - Name+=SkipLength; - while (IsPathDiv(*Name)) - Name++; - } - - for (wchar *s=Path+wcslen(Path)-1;s>Name;s--) - if (IsPathDiv(*s)) - { - *s=0; - FindData FD; - if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) -#ifdef _WIN_ALL - if (!DelDir(Path)) -#else - if (!DelFile(Path)) -#endif - return false; // Couldn't delete the symlink to replace it with directory. - } - LastChecked=SrcName; -//#endif - return true; -} - - -bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) +bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName) { // Catch root dir based /path/file paths also as stuff like \\?\. // Do not check PrepSrcName here, it can be root based if destination path @@ -178,14 +114,13 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr // Number of ".." in link target. int UpLevels=0; - for (int Pos=0;*TargetName!=0;Pos++) + for (uint Pos=0;PosExtrPath); - if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) + size_t ExtrPathLength=Cmd->ExtrPath.size(); + if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0) { - PrepSrcName+=ExtrPathLength; - while (IsPathDiv(*PrepSrcName)) - PrepSrcName++; + while (IsPathDiv(PrepSrcName[ExtrPathLength])) + ExtrPathLength++; + PrepSrcName.erase(0,ExtrPathLength); } int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); @@ -220,7 +155,7 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr } -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink) +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink) { // Returning true in Uplink indicates that link target might include ".." // and enables additional checks. It is ok to falsely return true here, @@ -230,20 +165,20 @@ bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wcha UpLink=true; // Assume the target might include potentially unsafe "..". #if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL) if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows. - UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL; + UpLink=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos; #endif #if defined(SAVE_LINKS) && defined(_UNIX) // For RAR 3.x archives we process links even in test mode to skip link data. if (Arc.Format==RARFMT15) - return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink); + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName.c_str(),UpLink); if (Arc.Format==RARFMT50) - return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); + return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead); #elif defined(_WIN_ALL) // RAR 5.0 archives store link information in file header, so there is // no need to additionally test it if we do not create a file. if (Arc.Format==RARFMT50) - return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); + return CreateReparsePoint(Cmd,LinkName.c_str(),&Arc.FileHead); #endif return false; } diff --git a/unrar/extinfo.hpp b/unrar/extinfo.hpp index d8551d46..11b89fef 100644 --- a/unrar/extinfo.hpp +++ b/unrar/extinfo.hpp @@ -1,24 +1,23 @@ #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ -bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked); -bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); -bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink); +bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink); #ifdef _UNIX -void SetUnixOwner(Archive &Arc,const wchar *FileName); +void SetUnixOwner(Archive &Arc,const std::wstring &FileName); #endif -bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); +bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting); -void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); +std::wstring GetStreamNameNTFS(Archive &Arc); #ifdef _WIN_ALL bool SetPrivilege(LPCTSTR PrivName); #endif -void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); -void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); -void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name); +void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name); +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name); #endif diff --git a/unrar/extract.cpp b/unrar/extract.cpp index f5e8fa44..67842a72 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -4,30 +4,22 @@ CmdExtract::CmdExtract(CommandData *Cmd) { CmdExtract::Cmd=Cmd; - *ArcName=0; - *DestFileName=0; - ArcAnalyzed=false; - Analyze=new AnalyzeData; - memset(Analyze,0,sizeof(*Analyze)); + Analyze={}; TotalFileCount=0; // Common for all archives involved. Set here instead of DoExtract() - // to use in unrar.dll too. Allows to avoid LinksToDirs() calls - // and save CPU time in no symlinks including ".." in target were extracted. -#if defined(_WIN_ALL) - // We can't expand symlink path components in another symlink target - // in Windows. We can't create symlinks in Android now. Even though we do not - // really need LinksToDirs() calls in these systems, we still call it - // for extra safety, but only if symlink with ".." in target was extracted. - ConvertSymlinkPaths=false; -#else + // to use in unrar.dll too. // We enable it by default in Unix to care about the case when several // archives are unpacked to same directory with several independent RAR runs. // Worst case performance penalty for a lot of small files seems to be ~3%. + // 2023.09.15: Windows performance impact seems to be negligible, + // less than 0.5% when extracting mix of small files and folders. + // So for extra security we enabled it for Windows too, even though + // unlike Unix, Windows doesn't expand lnk1 in symlink targets like + // "lnk1/../dir", but converts such path to "dir". ConvertSymlinkPaths=true; -#endif Unp=new Unpack(&DataIO); #ifdef RAR_SMP @@ -40,24 +32,21 @@ CmdExtract::~CmdExtract() { FreeAnalyzeData(); delete Unp; - delete Analyze; } void CmdExtract::FreeAnalyzeData() { - for (size_t I=0;ICommand[0]); - if (*Cmd->UseStdin==0) + if (Cmd->UseStdin.empty()) { FindData FD; - while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + while (Cmd->GetArcName(ArcName)) if (FindFile::FastFind(ArcName,&FD)) DataIO.TotalArcSize+=FD.Size; } Cmd->ArcNames.Rewind(); - while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + while (Cmd->GetArcName(ArcName)) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. @@ -101,7 +90,7 @@ void CmdExtract::DoExtract() if (TotalFileCount==0 && Cmd->Command[0]!='I' && ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. { - if (!PasswordCancelled) + if (!SuppressNoFilesMessage) uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); // Other error codes may explain a reason of "no files extracted" clearer, @@ -150,7 +139,7 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc) EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); - if (*Cmd->UseStdin!=0) + if (!Cmd->UseStdin.empty()) { Arc.SetHandleType(FILE_HANDLESTD); #ifdef USE_QOPEN @@ -159,8 +148,10 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() } else { -#if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache. - if (*Cmd->Command=='T' || Cmd->Test) + // We commented out "&& !defined(WINRAR)", because WinRAR GUI code resets + // the cache for usual test command, but not for test after archiving. +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->Command[0]=='T' || Cmd->Test) ResetFileCache(ArcName); // Reset the file cache when testing an archive. #endif if (!Arc.WOpen(ArcName)) @@ -172,8 +163,8 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() #if !defined(SFX_MODULE) && !defined(RARDLL) if (CmpExt(ArcName,L"rev")) { - wchar FirstVolName[NM]; - VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); + std::wstring FirstVolName; + VolNameToFirstName(ArcName,FirstVolName,true); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present @@ -187,7 +178,7 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() } #endif - mprintf(St(MNotRAR),ArcName); + mprintf(St(MNotRAR),ArcName.c_str()); #ifndef SFX_MODULE if (CmpExt(ArcName,L"rar")) @@ -202,8 +193,8 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() #ifndef SFX_MODULE if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName) { - wchar FirstVolName[NM]; - VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); + std::wstring FirstVolName; + VolNameToFirstName(ArcName,FirstVolName,Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present @@ -219,7 +210,7 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() int64 VolumeSetSize=0; // Total size of volumes after the current volume. #ifndef SFX_MODULE - if (!ArcAnalyzed && *Cmd->UseStdin==0) + if (!ArcAnalyzed && Cmd->UseStdin.empty()) { AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering); ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT. @@ -231,10 +222,10 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() #ifndef SFX_MODULE // Try to speed up extraction for independent solid volumes by starting // extraction from non-first volume if we can. - if (*Analyze->StartName!=0) + if (!Analyze.StartName.empty()) { - wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName)); - *Analyze->StartName=0; + ArcName=Analyze.StartName; + Analyze.StartName.clear(); UseExactVolName=true; return EXTRACT_ARC_REPEAT; @@ -244,15 +235,14 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. - wchar NextName[NM]; - wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); + std::wstring NextName=Arc.FileName; while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. - NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + NextVolumeName(NextName,!Arc.NewNumbering); FindData FD; if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; @@ -264,11 +254,11 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() ExtractArchiveInit(Arc); - if (*Cmd->Command=='T' || *Cmd->Command=='I') + if (Cmd->Command[0]=='T' || Cmd->Command[0]=='I') Cmd->Test=true; - if (*Cmd->Command=='I') + if (Cmd->Command[0]=='I') { Cmd->DisablePercentage=true; } @@ -276,10 +266,10 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive() uiStartArchiveExtract(!Cmd->Test,ArcName); #ifndef SFX_MODULE - if (Analyze->StartPos!=0) + if (Analyze.StartPos!=0) { - Arc.Seek(Analyze->StartPos,SEEK_SET); - Analyze->StartPos=0; + Arc.Seek(Analyze.StartPos,SEEK_SET); + Analyze.StartPos=0; } #endif @@ -340,8 +330,8 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (HeaderType==HEAD_FILE) { // Unlike Arc.FileName, ArcName might store an old volume name here. - if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos && - (*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0)) + if (Analyze.EndPos!=0 && Analyze.EndPos==Arc.CurBlockPos && + (Analyze.EndName.empty() || Analyze.EndName==Arc.FileName)) return false; } else @@ -380,32 +370,30 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // when reading an archive. But we prefer to do it here, because this // function is called directly in unrar.dll, so we fix bad parameters // passed to dll. Also we want to see real negative sizes in the listing - // of corrupt archive. To prevent uninitialized data access perform + // of corrupt archive. To prevent the uninitialized data access, perform // these checks after rejecting zero length and non-file headers above. if (Arc.FileHead.PackSize<0) Arc.FileHead.PackSize=0; if (Arc.FileHead.UnpSize<0) Arc.FileHead.UnpSize=0; - // 2022.03.20: We might remove this check in the future. - // It duplicates Analyze->EndPos and Analyze->EndName in all cases except - // volumes on removable media. + // This check duplicates Analyze.EndPos and Analyze.EndName + // in all cases except volumes on removable media. if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) return false; int MatchType=MATCH_WILDSUBPATH; bool EqualNames=false; - wchar MatchedArg[NM]; - int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg)); - bool MatchFound=MatchNumber!=0; + std::wstring MatchedArg; + bool MatchFound=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,&MatchedArg)!=0; #ifndef SFX_MODULE if (Cmd->ExclPath==EXCL_BASEPATH) { - wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); - *PointToName(Cmd->ArcPath)=0; + Cmd->ArcPath=MatchedArg; + GetPathWithSep(Cmd->ArcPath,Cmd->ArcPath); if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. - *Cmd->ArcPath=0; + Cmd->ArcPath.clear(); } #endif if (MatchFound && !EqualNames) @@ -416,13 +404,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) #if !defined(SFX_MODULE) && !defined(RARDLL) if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName) { - wchar CurVolName[NM]; - wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); - GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName)); + std::wstring StartVolName; + GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,StartVolName); - if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) + if (StartVolName!=ArcName && FileExist(StartVolName)) { - wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction". + ArcName=StartVolName; + Cmd->ArcName=ArcName; // For GUI "Delete archive after extraction". // If first volume name does not match the current name and if such // volume name really exists, let's unpack from this first volume. Repeat=true; @@ -439,12 +427,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } } #endif - wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); } #endif - wchar ArcFileName[NM]; - ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName)); + std::wstring ArcFileName; + ConvertPath(&Arc.FileHead.FileName,&ArcFileName); if (Arc.FileHead.Version) { @@ -490,10 +477,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) bool RefTarget=false; if (!MatchFound) - for (size_t I=0;ITest) // While harmless, it is useless for 't'. { @@ -504,11 +491,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // targets and it is possible that first target isn't unpacked // for some reason. Also targets might have associated service blocks // like ACLs. All this would complicate processing a lot. - wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); - AddEndSlash(DestFileName,ASIZE(DestFileName)); - wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); - MkTemp(DestFileName,ASIZE(DestFileName)); - MatchedRef->TmpName=wcsdup(DestFileName); + DestFileName=!Cmd->TempPath.empty() ? Cmd->TempPath:Cmd->ExtrPath; + AddEndSlash(DestFileName); + DestFileName+=L"__tmp_reference_source_"; + MkTemp(DestFileName); + MatchedRef.TmpName=DestFileName; } RefTarget=true; // Need it even for 't' to test the reference source. break; @@ -520,7 +507,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) else MatchFound=false; // Skip only the current file for non-solid archive. - if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0) + if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=false) { // First common call of uiStartFileExtract. It is done before overwrite // prompts, so if SkipSolid state is changed below, we'll need to make @@ -529,10 +516,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) return false; if (!RefTarget) - ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); + ExtrPrepareName(Arc,ArcFileName,DestFileName); // DestFileName can be set empty in case of excessive -ap switch. - ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; + ExtrFile=!SkipSolid && !DestFileName.empty() && !Arc.FileHead.SplitBefore; if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) { @@ -584,7 +571,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) #else if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL)) { - PasswordCancelled=true; + SuppressNoFilesMessage=true; return false; } #endif @@ -641,13 +628,25 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) else DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); + // Per file symlink conversion flag. Can be turned off in unrar.dll. + bool CurConvertSymlinkPaths=ConvertSymlinkPaths; + #ifdef RARDLL - if (*Cmd->DllDestName!=0) - wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); + if (!Cmd->DllDestName.empty()) + { + DestFileName=Cmd->DllDestName; + + // If unrar.dll sets the entire destination pathname, there is no + // destination path and we can't convert symlinks, because we would + // risk converting important user or system symlinks in this case. + // If DllDestName is set, it turns off our path processing and app + // invoking the library cares about everything including safety. + CurConvertSymlinkPaths=false; + } #endif if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && - ConvertSymlinkPaths) + CurConvertSymlinkPaths) ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); File CurFile; @@ -655,13 +654,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) { + if (Cmd->SkipSymLinks && (Arc.FileHead.RedirType==FSREDIR_UNIXSYMLINK || + Arc.FileHead.RedirType==FSREDIR_WINSYMLINK || Arc.FileHead.RedirType==FSREDIR_JUNCTION)) + ExtrFile=false; + if (ExtrFile && Command!='P' && !Cmd->Test) { // Overwrite prompt for symbolic and hard links and when we move // a temporary file to the file reference instead of copying it. bool UserReject=false; - if (FileExist(DestFileName) && !UserReject) - FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + if (FileExist(DestFileName)) + FileCreate(Cmd,NULL,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); if (UserReject) ExtrFile=false; } @@ -680,7 +683,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } else if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). + { + // Check the dictionary size before creating a file and issuing + // any overwrite prompts. + if (!CheckWinLimit(Arc,ArcFileName)) + return false; ExtrFile=ExtrCreateFile(Arc,CurFile); + } if (!ExtrFile && Arc.Solid) { @@ -694,6 +703,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // a solid archive. if (!uiStartFileExtract(ArcFileName,false,false,true)) return false; + // Check the dictionary size also for skipping files. + if (!CheckWinLimit(Arc,ArcFileName)) + return false; } if (ExtrFile) { @@ -716,21 +728,21 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) FileCount++; if (Command!='I' && !Cmd->DisableNames) if (SkipSolid) - mprintf(St(MExtrSkipFile),ArcFileName); + mprintf(St(MExtrSkipFile),ArcFileName.c_str()); else switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. { case 'T': - mprintf(St(MExtrTestFile),ArcFileName); + mprintf(St(MExtrTestFile),ArcFileName.c_str()); break; #ifndef SFX_MODULE case 'P': - mprintf(St(MExtrPrinting),ArcFileName); + mprintf(St(MExtrPrinting),ArcFileName.c_str()); break; #endif case 'X': case 'E': - mprintf(St(MExtrFile),DestFileName); + mprintf(St(MExtrFile),DestFileName.c_str()); break; } if (!Cmd->DisablePercentage && !Cmd->DisableNames) @@ -778,7 +790,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) { - wchar RedirName[NM]; + std::wstring RedirName; // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning @@ -787,17 +799,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // We must perform this conversion before ConvertPath call, // so paths mixing different slashes like \dir1/dir2\file are // processed correctly. - SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + SlashToNative(Arc.FileHead.RedirName,RedirName); - ConvertPath(RedirName,RedirName,ASIZE(RedirName)); + ConvertPath(&RedirName,&RedirName); - wchar NameExisting[NM]; - ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); - if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. + std::wstring NameExisting; + ExtrPrepareName(Arc,RedirName,NameExisting); + if (FileCreateMode && !NameExisting.empty()) // *NameExisting can be empty in case of excessive -ap switch. if (Type==FSREDIR_HARDLINK) - LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); + LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting); else - LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize); + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,Arc.FileHead.UnpSize); } else if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) @@ -807,10 +819,6 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) bool UpLink; LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); - // Unix symlink can have its own owner data. - if (LinkSuccess) - SetFileHeaderExtra(Cmd,Arc,DestFileName); - ConvertSymlinkPaths|=LinkSuccess && UpLink; // We do not actually need to reset the cache here if we cache @@ -848,7 +856,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) UnstoreFile(DataIO,Arc.FileHead.UnpSize); else { - Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + try + { + Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + } + catch (std::bad_alloc) + { + if (Arc.FileHead.WinSize>0x1000000) + uiMsg(UIERROR_EXTRDICTOUTMEM,Arc.FileName,uint(Arc.FileHead.WinSize/0x100000)); + throw; + } + Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE // RAR 1.3 - 1.5 archives do not set per file solid flag. @@ -916,24 +934,34 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (SkipSolid) 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 || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && - (!BrokenFile || Cmd->KeepBroken)) + (!LinkEntry || LinkSuccess) && (!BrokenFile || Cmd->KeepBroken)) { + // Set everything for usual files and file references. + bool SetAll=!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY; + + // Set time and adjust size for usual files and references. + // Symlink time requires the special treatment and it is set directly + // after creating a symlink. + bool SetTimeAndSize=SetAll; + + // Set file attributes for usual files, references and hard links. + // Hard link shares the 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 too. So we set link attributes to restore both target + // and link attributes if PrepareToDelete() has changed them. + bool SetAttr=SetAll || Arc.FileHead.RedirType==FSREDIR_HARDLINK; + + // Call SetFileHeaderExtra to set Unix user and group for usual files, + // references and symlinks. Unix symlink can have its own owner data. + bool SetExtra=SetAll || Arc.FileHead.RedirType==FSREDIR_UNIXSYMLINK; + // 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 (!SetAttrOnly) + if (SetTimeAndSize) { // We could preallocate more space that really written to broken file // or file with crafted header. @@ -946,28 +974,33 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); CurFile.Close(); + } + if (SetExtra) SetFileHeaderExtra(Cmd,Arc,DestFileName); + if (SetTimeAndSize) CurFile.SetCloseFileTime( Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); - } + 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; + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) + SetFileCompression(DestFileName,true); + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; #endif - 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(); + 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; @@ -993,42 +1026,44 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) { - Array Buffer(File::CopyBufferSize()); + std::vector Buffer(File::CopyBufferSize()); while (true) { - int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + int ReadSize=DataIO.UnpRead(Buffer.data(),Buffer.size()); if (ReadSize<=0) break; int WriteSize=ReadSize0) { - DataIO.UnpWrite(&Buffer[0],WriteSize); + DataIO.UnpWrite(Buffer.data(),WriteSize); DestUnpSize-=WriteSize; } } } -bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) +bool CmdExtract::ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize) { File Existing; if (!Existing.Open(NameExisting)) { + std::wstring TmpExisting=NameExisting; // NameExisting is 'const', so copy it here. + bool OpenFailed=true; // If we couldn't find the existing file, check if match is present // in temporary reference sources list. - for (size_t I=0;IDllError=ERAR_EREFERENCE; @@ -1066,20 +1100,20 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName } } - Array Buffer(0x100000); + std::vector Buffer(0x100000); int64 CopySize=0; while (true) { Wait(); - int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); + int ReadSize=Existing.Read(Buffer.data(),Buffer.size()); if (ReadSize==0) break; // Update only the current file progress in WinRAR, set the total to 0 // to keep it as is. It looks better for WinRAR. uiExtractProgress(CopySize,UnpSize,0,0); - New.Write(&Buffer[0],ReadSize); + New.Write(Buffer.data(),ReadSize); CopySize+=ReadSize; } @@ -1087,7 +1121,7 @@ bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName } -void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) +void CmdExtract::ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName) { if (Cmd->Test) { @@ -1095,15 +1129,15 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De // This check also allows to avoid issuing "Attempting to correct... // Renaming..." messages in MakeNameCompatible() below for problematic // names like aux.txt when testing an archive. - wcsncpyz(DestName,ArcFileName,DestSize); + DestName=ArcFileName; return; } - wcsncpyz(DestName,Cmd->ExtrPath,DestSize); + DestName=Cmd->ExtrPath; - if (*Cmd->ExtrPath!=0) + if (!Cmd->ExtrPath.empty()) { - wchar LastChar=*PointToLastChar(Cmd->ExtrPath); + wchar LastChar=GetLastChar(Cmd->ExtrPath); // We need IsPathDiv check here to correctly handle Unix forward slash // in the end of destination path in Windows: rar x arc dest/ // so we call IsPathDiv first instead of just calling AddEndSlash, @@ -1112,7 +1146,7 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) { // Destination path can be without trailing slash if it come from GUI shell. - AddEndSlash(DestName,DestSize); + AddEndSlash(DestName); } } @@ -1122,38 +1156,40 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De switch(Cmd->AppendArcNameToPath) { case APPENDARCNAME_DESTPATH: // To subdir of destination path. - wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); - SetExt(DestName,NULL,DestSize); + DestName+=PointToName(Arc.FirstVolumeName); + RemoveExt(DestName); break; case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir. - wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); - SetExt(DestName,NULL,DestSize); + DestName=Arc.FirstVolumeName; + RemoveExt(DestName); break; case APPENDARCNAME_OWNDIR: // To archive own dir. - wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + DestName=Arc.FirstVolumeName; RemoveNameFromPath(DestName); break; } - AddEndSlash(DestName,DestSize); + AddEndSlash(DestName); } #endif - + // We need to modify the name below and ArcFileName is const. + std::wstring CurName=ArcFileName; #ifndef SFX_MODULE - wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath; - size_t ArcPathLength=wcslen(ArcPath); + std::wstring &ArcPath=!Cmd->ExclArcPath.empty() ? Cmd->ExclArcPath:Cmd->ArcPath; + size_t ArcPathLength=ArcPath.size(); if (ArcPathLength>0) { - size_t NameLength=wcslen(ArcFileName); - if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 && + size_t NameLength=CurName.size(); + if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,CurName,ArcPathLength)==0 && (IsPathDiv(ArcPath[ArcPathLength-1]) || - IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) + IsPathDiv(CurName[ArcPathLength]) || CurName[ArcPathLength]==0)) { - ArcFileName+=Min(ArcPathLength,NameLength); - while (IsPathDiv(*ArcFileName)) - ArcFileName++; - if (*ArcFileName==0) // Excessive -ap switch. + size_t Pos=Min(ArcPathLength,NameLength); + while (PosExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); - // We do not use any user specified destination paths when extracting - // absolute paths in -ep3 mode. if (AbsPaths) - *DestName=0; + { + // We do not use a user specified destination path when extracting + // absolute paths in -ep3 mode. + wchar DiskLetter=toupperw(CurName[0]); + if (CurName[1]=='_' && IsPathDiv(CurName[2]) && DiskLetter>='A' && DiskLetter<='Z') + DestName=CurName.substr(0,1) + L':' + CurName.substr(2); + else + if (CurName[0]=='_' && CurName[1]=='_') + DestName=std::wstring(2,CPATHDIVIDER) + CurName.substr(2); + else + AbsPaths=false; // Apply the destination path even with -ep3 for not absolute path. + } if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - wcsncatz(DestName,PointToName(ArcFileName),DestSize); - else - wcsncatz(DestName,ArcFileName,DestSize); + CurName=PointToName(CurName); + if (!AbsPaths) + DestName+=CurName; #ifdef _WIN_ALL // Must do after Cmd->ArcPath processing above, so file name and arc path // trailing spaces are in sync. if (!Cmd->AllowIncompatNames) - MakeNameCompatible(DestName,DestSize); + MakeNameCompatible(DestName); #endif - - wchar DiskLetter=toupperw(DestName[0]); - - if (AbsPaths) - { - if (DestName[1]=='_' && IsPathDiv(DestName[2]) && - DiskLetter>='A' && DiskLetter<='Z') - DestName[1]=':'; - else - if (DestName[0]=='_' && DestName[1]=='_') - { - // Convert __server\share to \\server\share. - DestName[0]=CPATHDIVIDER; - DestName[1]=CPATHDIVIDER; - } - } } @@ -1216,7 +1245,7 @@ bool CmdExtract::ExtrDllGetPassword() *PasswordA=0; if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) *PasswordA=0; - GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + CharToWide(PasswordA,PasswordW,ASIZE(PasswordW)); cleandata(PasswordA,sizeof(PasswordA)); } Cmd->Password.Set(PasswordW); @@ -1232,7 +1261,7 @@ bool CmdExtract::ExtrDllGetPassword() #ifndef RARDLL -bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd) +bool CmdExtract::ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd) { if (!Cmd->Password.IsSet()) { @@ -1248,7 +1277,7 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckP else if (!GlobalPassword && !Arc.FileHead.Solid) { - eprintf(St(MUseCurPsw),ArcFileName); + eprintf(St(MUseCurPsw),ArcFileName.c_str()); switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) { case -1: @@ -1289,13 +1318,13 @@ void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) #endif -void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) +void CmdExtract::ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName) { if (Cmd->Test) { if (!Cmd->DisableNames) { - mprintf(St(MExtrTestFile),ArcFileName); + mprintf(St(MExtrTestFile),ArcFileName.c_str()); mprintf(L" %s",St(MOk)); } return; @@ -1311,7 +1340,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) // File with name same as this directory exists. Propose user // to overwrite it. bool UserReject; - FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + FileCreate(Cmd,NULL,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); DirExist=false; } if (!DirExist) @@ -1321,17 +1350,15 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName)) { uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); - wchar OrigName[ASIZE(DestFileName)]; - wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + std::wstring OrigName=DestFileName; MakeNameUsable(DestFileName,true); #ifndef SFX_MODULE uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); - if (!DirExist) + if (!DirExist && (Cmd->AbsoluteLinks || !ConvertSymlinkPaths || + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink))) { - if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) - LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); CreatePath(DestFileName,true,Cmd->DisableNames); MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); } @@ -1342,7 +1369,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) { if (!Cmd->DisableNames) { - mprintf(St(MCreatDir),DestFileName); + mprintf(St(MCreatDir),DestFileName.c_str()); mprintf(L" %s",St(MOk)); } PrevProcessed=true; @@ -1392,7 +1419,7 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) bool UserReject; // Specify "write only" mode to avoid OpenIndiana NAS problems // with SetFileTime and read+write files. - if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + if (!FileCreate(Cmd,&CurFile,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) { Success=false; if (!UserReject) @@ -1408,23 +1435,24 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) { uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); - wchar OrigName[ASIZE(DestFileName)]; - wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + std::wstring OrigName=DestFileName; MakeNameUsable(DestFileName,true); - if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) - LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); - CreatePath(DestFileName,true,Cmd->DisableNames); - if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + if (Cmd->AbsoluteLinks || !ConvertSymlinkPaths || + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink)) { + CreatePath(DestFileName,true,Cmd->DisableNames); + if (FileCreate(Cmd,&CurFile,DestFileName,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { #ifndef SFX_MODULE - uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); #endif - Success=true; + Success=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); } - else - ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); } } } @@ -1433,11 +1461,11 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) } -bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) +bool CmdExtract::CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName) { bool WrongVer; - if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. - WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; + if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 and 7.0 archives. + WrongVer=Arc.FileHead.UnpVer>VER_UNPACK7; else { #ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. @@ -1470,7 +1498,7 @@ bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) // But it would be slower for solid archives than scaning headers // in first pass and extracting everything in second, as implemented now. // -void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering) +void CmdExtract::AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering) { FreeAnalyzeData(); // If processing non-first archive in multiple archives set. @@ -1480,11 +1508,11 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi return; // No need to check further for * and *.* masks. // Start search from first volume if all volumes preceding current are available. - wchar NextName[NM]; + std::wstring NextName; if (Volume) - GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName)); + GetFirstVolIfFullSet(ArcName,NewNumbering,NextName); else - wcsncpyz(NextName,ArcName,ASIZE(NextName)); + NextName=ArcName; bool MatchFound=false; bool PrevMatched=false; @@ -1493,7 +1521,7 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi bool FirstVolume=true; // We shall set FirstFile once for all volumes and not for each volume. - // So we do not reuse the outdated Analyze->StartPos from previous volume + // So we do not reuse the outdated Analyze.StartPos from previous volume // if extracted file resides completely in the beginning of current one. bool FirstFile=true; @@ -1507,8 +1535,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi // If we couldn't open trailing volumes, we can't set early exit // parameters. It is possible that some volume are on removable media // and will be provided by user when extracting. - *Analyze->EndName=0; - Analyze->EndPos=0; + Analyze.EndName.clear(); + Analyze.EndPos=0; } break; } @@ -1543,24 +1571,24 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi // if we set StartName for first volume or StartPos for first // archived file. if (!FirstVolume) - wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); + Analyze.StartName=NextName; // We shall set FirstFile once for all volumes for this code // to work properly. Alternatively we could append - // "|| Analyze->StartPos!=0" to the condition, so we do not reuse - // the outdated Analyze->StartPos value from previous volume. + // "|| Analyze.StartPos!=0" to the condition, so we do not reuse + // the outdated Analyze.StartPos value from previous volume. if (!FirstFile) - Analyze->StartPos=Arc.CurBlockPos; + Analyze.StartPos=Arc.CurBlockPos; } - if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) + if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0) { MatchFound = true; PrevMatched = true; // Reset the previously set early exit position, if any, because // we found a new matched file. - Analyze->EndPos=0; + Analyze.EndPos=0; // Matched file reference pointing at maybe non-matched source file. // Even though we know RedirName, we can't check if source file @@ -1569,8 +1597,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi if (Arc.FileHead.RedirType==FSREDIR_FILECOPY) { bool AlreadyAdded=false; - for (size_t I=0;IEndName,NextName,ASIZE(Analyze->EndName)); - Analyze->EndPos=Arc.CurBlockPos; + Analyze.EndName=NextName; + Analyze.EndPos=Arc.CurBlockPos; } PrevMatched=false; } @@ -1620,7 +1648,7 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi if (Volume && OpenNext) { - NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + NextVolumeName(NextName,!Arc.NewNumbering); FirstVolume=false; // Needed for multivolume archives. Added in case some 'break' @@ -1635,8 +1663,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi // If file references are present, we can't reliably skip in semi-solid // archives, because reference source can be present in skipped data. - if (RefList.Size()!=0) - memset(Analyze,0,sizeof(*Analyze)); + if (RefList.size()!=0) + Analyze={}; } #endif @@ -1644,26 +1672,46 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi #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) +void CmdExtract::GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName) { - 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)); + std::wstring FirstVolName; + VolNameToFirstName(SrcName,FirstVolName,NewNumbering); + std::wstring NextName=FirstVolName; + std::wstring ResultName=SrcName; while (true) { - if (wcscmp(SrcName,NextName)==0) + if (SrcName==NextName) { - wcsncpyz(ResultName,FirstVolName,DestSize); + ResultName=FirstVolName; // Reached the specified volume starting from the first. break; } if (!FileExist(NextName)) break; - NextVolumeName(NextName,ASIZE(NextName),!NewNumbering); + NextVolumeName(NextName,!NewNumbering); } - wcsncpyz(DestName,ResultName,DestSize); + DestName=ResultName; } +#endif + -#endif \ No newline at end of file +bool CmdExtract::CheckWinLimit(Archive &Arc,std::wstring &ArcFileName) +{ + if (Arc.FileHead.WinSize<=Cmd->MaxWinSize || Arc.FileHead.WinSize<=Cmd->WinSize) + return true; + if (uiDictLimit(Cmd,ArcFileName,Arc.FileHead.WinSize,Max(Cmd->MaxWinSize,Cmd->WinSize))) + { + // No more prompts when extracting other files. Important for GUI versions, + // where we might not have [Max]WinSize set permanently when extracting. + Cmd->MaxWinSize=Arc.FileHead.WinSize; + } + else + { + ErrHandler.SetErrorCode(RARX_FATAL); +#ifdef RARDLL + Cmd->DllError=ERAR_LARGE_DICT; +#endif + Arc.SeekToNext(); + return false; + } + return true; +} diff --git a/unrar/extract.hpp b/unrar/extract.hpp index 18396c5b..4400057a 100644 --- a/unrar/extract.hpp +++ b/unrar/extract.hpp @@ -8,41 +8,42 @@ class CmdExtract private: struct ExtractRef { - wchar *RefName; - wchar *TmpName; + std::wstring RefName; + std::wstring TmpName; uint64 RefCount; }; - Array RefList; + std::vector RefList; struct AnalyzeData { - wchar StartName[NM]; + std::wstring StartName; uint64 StartPos; - wchar EndName[NM]; + std::wstring EndName; uint64 EndPos; - } *Analyze; + } Analyze; bool ArcAnalyzed; void FreeAnalyzeData(); EXTRACT_ARC_CODE ExtractArchive(); - bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize); - void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); + bool ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize); + void ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName); #ifdef RARDLL bool ExtrDllGetPassword(); #else - bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd); + bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); #endif - void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); + void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName); bool ExtrCreateFile(Archive &Arc,File &CurFile); - bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); + bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName); #ifndef SFX_MODULE - void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering); - void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); + void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering); + void GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName); #endif + bool CheckWinLimit(Archive &Arc,std::wstring &ArcFileName); RarTime StartTime; // Time when extraction started. @@ -65,12 +66,12 @@ class CmdExtract // any wrong password hints. bool AnySolidDataUnpackedWell; - wchar ArcName[NM]; + std::wstring ArcName; bool GlobalPassword; bool PrevProcessed; // If previous file was successfully extracted or tested. - wchar DestFileName[NM]; - bool PasswordCancelled; + std::wstring DestFileName; + bool SuppressNoFilesMessage; // In Windows it is set to true if at least one symlink with ".." // in target was extracted. diff --git a/unrar/filcreat.cpp b/unrar/filcreat.cpp index d58e4f6f..facd86a5 100644 --- a/unrar/filcreat.cpp +++ b/unrar/filcreat.cpp @@ -3,7 +3,7 @@ // If NewFile==NULL, we delete created file after user confirmation. // It is useful if we need to overwrite an existing folder or file, // but need user confirmation for that. -bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) @@ -22,14 +22,14 @@ bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, // Maybe our long name matches the short name of existing file. // Let's check if we can change the short name. - if (UpdateExistingShortName(Name)) + if (UpdateExistingShortName(Name.c_str())) continue; } // Allow short name check again. It is necessary, because rename and // autorename below can change the name, so we need to check it again. ShortNameChanged=false; #endif - UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); + UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); if (Choice==UIASKREP_R_REPLACE) break; @@ -56,85 +56,70 @@ bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, } -bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) -{ - wchar NewName[NM]; - size_t NameLength=wcslen(Name); - wchar *Ext=GetExt(Name); - if (Ext==NULL) - Ext=Name+NameLength; - for (uint FileVer=1;;FileVer++) - { - swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); - if (!FileExist(NewName)) - { - wcsncpyz(Name,NewName,MaxNameSize); - break; - } - if (FileVer>=1000000) - return false; - } - return true; -} - - #if defined(_WIN_ALL) // If we find a file, which short name is equal to 'Name', we try to change // its short name, while preserving the long name. It helps when unpacking // an archived file, which long name is equal to short name of already // existing file. Otherwise we would overwrite the already existing file, // even though its long name does not match the name of unpacking file. -bool UpdateExistingShortName(const wchar *Name) +bool UpdateExistingShortName(const std::wstring &Name) { - wchar LongPathName[NM]; - DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); - if (Res==0 || Res>=ASIZE(LongPathName)) + DWORD Res=GetLongPathName(Name.c_str(),NULL,0); + if (Res==0) return false; - wchar ShortPathName[NM]; - Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); - if (Res==0 || Res>=ASIZE(ShortPathName)) + std::vector LongPathBuf(Res); + Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size()); + if (Res==0 || Res>=LongPathBuf.size()) return false; - wchar *LongName=PointToName(LongPathName); - wchar *ShortName=PointToName(ShortPathName); + Res=GetShortPathName(Name.c_str(),NULL,0); + if (Res==0) + return false; + std::vector ShortPathBuf(Res); + Res=GetShortPathName(Name.c_str(),ShortPathBuf.data(),(DWORD)ShortPathBuf.size()); + if (Res==0 || Res>=ShortPathBuf.size()) + return false; + std::wstring LongPathName=LongPathBuf.data(); + std::wstring ShortPathName=ShortPathBuf.data(); + + std::wstring LongName=PointToName(LongPathName); + std::wstring ShortName=PointToName(ShortPathName); // We continue only if file has a short name, which does not match its // long name, and this short name is equal to name of file which we need // to create. - if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || + if (ShortName.empty() || wcsicomp(LongName,ShortName)==0 || wcsicomp(PointToName(Name),ShortName)!=0) return false; // Generate the temporary new name for existing file. - wchar NewName[NM]; - *NewName=0; - for (int I=0;I<10000 && *NewName==0;I+=123) + std::wstring NewName; + for (uint I=0;I<10000 && NewName.empty();I+=123) { // Here we copy the path part of file to create. We'll make the temporary // file in the same folder. - wcsncpyz(NewName,Name,ASIZE(NewName)); + NewName=Name; // Here we set the random name part. - swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I)); // If such file is already exist, try next random name. if (FileExist(NewName)) - *NewName=0; + NewName.clear(); } // If we could not generate the name not used by any other file, we return. - if (*NewName==0) + if (NewName.empty()) return false; // FastFind returns the name without path, but we need the fully qualified // name for renaming, so we use the path from file to create and long name // from existing file. - wchar FullName[NM]; - wcsncpyz(FullName,Name,ASIZE(FullName)); - SetName(FullName,LongName,ASIZE(FullName)); + std::wstring FullName=Name; + SetName(FullName,LongName); // Rename the existing file to randomly generated name. Normally it changes // the short name too. - if (!MoveFile(FullName,NewName)) + if (!MoveFile(FullName.c_str(),NewName.c_str())) return false; // Now we need to create the temporary empty file with same name as @@ -149,7 +134,7 @@ bool UpdateExistingShortName(const wchar *Name) // Now we rename the existing file from temporary name to original long name. // Since its previous short name is occupied by another file, it should // get another short name. - MoveFile(NewName,FullName); + MoveFile(NewName.c_str(),FullName.c_str()); if (Created) { @@ -157,9 +142,9 @@ bool UpdateExistingShortName(const wchar *Name) KeepShortFile.Close(); KeepShortFile.Delete(); } - // We successfully changed the short name. Maybe sometimes we'll simplify - // this function by use of SetFileShortName Windows API call. - // But SetFileShortName is not available in older Windows. + // We successfully changed the short name. We do not use the simpler + // SetFileShortName Windows API call, because it requires SE_RESTORE_NAME + // privilege. return true; } #endif diff --git a/unrar/filcreat.hpp b/unrar/filcreat.hpp index 456a4a4a..ad95feef 100644 --- a/unrar/filcreat.hpp +++ b/unrar/filcreat.hpp @@ -1,14 +1,12 @@ #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ -bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, +bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name, bool *UserReject,int64 FileSize=INT64NDF, RarTime *FileTime=NULL,bool WriteOnly=false); -bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); - #if defined(_WIN_ALL) -bool UpdateExistingShortName(const wchar *Name); +bool UpdateExistingShortName(const std::wstring &Name); #endif #endif diff --git a/unrar/file.cpp b/unrar/file.cpp index 7bf60fd4..2d0d785c 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -3,7 +3,6 @@ File::File() { hFile=FILE_BAD_HANDLE; - *FileName=0; NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; @@ -40,12 +39,12 @@ void File::operator = (File &SrcFile) LastWrite=SrcFile.LastWrite; HandleType=SrcFile.HandleType; TruncatedAfterReadError=SrcFile.TruncatedAfterReadError; - wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); + FileName=SrcFile.FileName; SrcFile.SkipClose=true; } -bool File::Open(const wchar *Name,uint Mode) +bool File::Open(const std::wstring &Name,uint Mode) { ErrorType=FILE_SUCCESS; FileHandle hNewFile; @@ -63,17 +62,17 @@ bool File::Open(const wchar *Name,uint Mode) FindData FD; if (PreserveAtime) Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. - hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + hNewFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); DWORD LastError; if (hNewFile==FILE_BAD_HANDLE) { LastError=GetLastError(); - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) { - hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + hNewFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); // For archive names longer than 260 characters first CreateFile // (without \\?\) fails and sets LastError to 3 (access denied). @@ -112,10 +111,10 @@ bool File::Open(const wchar *Name,uint Mode) if (PreserveAtime) flags|=O_NOATIME; #endif - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); + std::string NameA; + WideToChar(Name,NameA); - int handle=open(NameA,flags); + int handle=open(NameA.c_str(),flags); #ifdef LOCK_EX #ifdef _OSF_SOURCE @@ -148,7 +147,7 @@ bool File::Open(const wchar *Name,uint Mode) if (Success) { hFile=hNewFile; - wcsncpyz(FileName,Name,ASIZE(FileName)); + FileName=Name; TruncatedAfterReadError=false; } return Success; @@ -156,7 +155,7 @@ bool File::Open(const wchar *Name,uint Mode) #if !defined(SFX_MODULE) -void File::TOpen(const wchar *Name) +void File::TOpen(const std::wstring &Name) { if (!WOpen(Name)) ErrHandler.Exit(RARX_OPEN); @@ -164,7 +163,7 @@ void File::TOpen(const wchar *Name) #endif -bool File::WOpen(const wchar *Name) +bool File::WOpen(const std::wstring &Name) { if (Open(Name)) return true; @@ -173,7 +172,7 @@ bool File::WOpen(const wchar *Name) } -bool File::Create(const wchar *Name,uint Mode) +bool File::Create(const std::wstring &Name,uint Mode) { // OpenIndiana based NAS and CIFS shares fail to set the file time if file // was created in read+write mode and some data was written and not flushed @@ -188,40 +187,40 @@ bool File::Create(const wchar *Name,uint Mode) // Windows automatically removes dots and spaces in the end of file name, // So we detect such names and process them with \\?\ prefix. - wchar *LastChar=PointToLastChar(Name); - bool Special=*LastChar=='.' || *LastChar==' '; + wchar LastChar=GetLastChar(Name); + bool Special=LastChar=='.' || LastChar==' '; if (Special && (Mode & FMF_STANDARDNAMES)==0) hFile=FILE_BAD_HANDLE; else - hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + hFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); if (hFile==FILE_BAD_HANDLE) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + hFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); } #else - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); #ifdef FILE_USE_OPEN - hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); + std::string NameA; + WideToChar(Name,NameA); + hFile=open(NameA.c_str(),(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); #else - hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); + hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY); #endif #endif NewFile=true; HandleType=FILE_HANDLENORMAL; SkipClose=false; - wcsncpyz(FileName,Name,ASIZE(FileName)); + FileName=Name; return hFile!=FILE_BAD_HANDLE; } #if !defined(SFX_MODULE) -void File::TCreate(const wchar *Name,uint Mode) +void File::TCreate(const std::wstring &Name,uint Mode) { if (!WCreate(Name,Mode)) ErrHandler.Exit(RARX_FATAL); @@ -229,7 +228,7 @@ void File::TCreate(const wchar *Name,uint Mode) #endif -bool File::WCreate(const wchar *Name,uint Mode) +bool File::WCreate(const std::wstring &Name,uint Mode) { if (Create(Name,Mode)) return true; @@ -250,7 +249,7 @@ bool File::Close() // We use the standard system handle for stdout in Windows // and it must not be closed here. if (HandleType==FILE_HANDLENORMAL) - Success=CloseHandle(hFile)==TRUE; + Success=CloseHandle(hFile)!=FALSE; #else #ifdef FILE_USE_OPEN Success=close(hFile)!=-1; @@ -280,16 +279,16 @@ bool File::Delete() } -bool File::Rename(const wchar *NewName) +bool File::Rename(const std::wstring &NewName) { // No need to rename if names are already same. - bool Success=wcscmp(FileName,NewName)==0; + bool Success=(NewName==FileName); if (!Success) Success=RenameFile(FileName,NewName); if (Success) - wcsncpyz(FileName,NewName,ASIZE(FileName)); + FileName=NewName; return Success; } @@ -327,13 +326,13 @@ bool File::Write(const void *Data,size_t Size) const size_t MaxSize=0x4000; for (size_t I=0;IIsSet(); bool seta=fta!=NULL && fta->IsSet(); if (setm || seta) { - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); + std::string NameA; + WideToChar(Name,NameA); #ifdef UNIX_TIME_NS timespec times[2]; @@ -720,7 +719,7 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; times[1].tv_sec=setm ? ftm->GetUnix() : 0; times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; - utimensat(AT_FDCWD,NameA,times,0); + utimensat(AT_FDCWD,NameA.c_str(),times,0); #else utimbuf ut; if (setm) @@ -731,7 +730,7 @@ void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) ut.actime=fta->GetUnix(); else ut.actime=ut.modtime; // Need to set something, cannot left it 0. - utime(NameA,&ut); + utime(NameA.c_str(),&ut); #endif } #endif @@ -802,15 +801,15 @@ bool File::IsDevice() #ifndef SFX_MODULE int64 File::Copy(File &Dest,int64 Length) { - Array Buffer(File::CopyBufferSize()); + std::vector Buffer(File::CopyBufferSize()); int64 CopySize=0; bool CopyAll=(Length==INT64NDF); while (CopyAll || Length>0) { Wait(); - size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); - byte *Buf=&Buffer[0]; + size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size(); + byte *Buf=Buffer.data(); int ReadSize=Read(Buf,SizeToRead); if (ReadSize==0) break; diff --git a/unrar/file.hpp b/unrar/file.hpp index 5f55de96..0f0ccb67 100644 --- a/unrar/file.hpp +++ b/unrar/file.hpp @@ -83,12 +83,9 @@ class File protected: bool OpenShared; // Set by 'Archive' class. public: - wchar FileName[NM]; + std::wstring FileName; FILE_ERRORTYPE ErrorType; - - byte *SeekBuf; // To read instead of seek for stdin files. - static const size_t SeekBufSize=0x10000; public: File(); virtual ~File(); @@ -96,15 +93,15 @@ class File // Several functions below are 'virtual', because they are redefined // by Archive for QOpen and by MultiFile for split files in WinRAR. - virtual bool Open(const wchar *Name,uint Mode=FMF_READ); - void TOpen(const wchar *Name); - bool WOpen(const wchar *Name); - bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); - void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); - bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + virtual bool Open(const std::wstring &Name,uint Mode=FMF_READ); + void TOpen(const std::wstring &Name); + bool WOpen(const std::wstring &Name); + bool Create(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); virtual bool Close(); // 'virtual' for MultiFile class. bool Delete(); - bool Rename(const wchar *NewName); + bool Rename(const std::wstring &NewName); bool Write(const void *Data,size_t Size); virtual int Read(void *Data,size_t Size); int DirectRead(void *Data,size_t Size); @@ -118,13 +115,13 @@ class File void Flush(); void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); - static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); + static void SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta); #ifdef _UNIX static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta); #endif void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. - int64 FileLength(); + virtual int64 FileLength(); // 'virtual' for MultiFile class. void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} void SetLineInputMode(bool Mode) {LineInput=Mode;} FILE_HANDLETYPE GetHandleType() {return HandleType;} diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index aaef305b..14316ae3 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -1,18 +1,18 @@ #include "rar.hpp" -MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) +MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr) { #ifdef _WIN_ALL // Windows automatically removes dots and spaces in the end of directory // name. So we detect such names and process them with \\?\ prefix. - wchar *LastChar=PointToLastChar(Name); - bool Special=*LastChar=='.' || *LastChar==' '; - BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); + wchar LastChar=GetLastChar(Name); + bool Special=LastChar=='.' || LastChar==' '; + BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL); if (RetCode==0 && !FileExist(Name)) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - RetCode=CreateDirectory(LongName,NULL); + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + RetCode=CreateDirectory(LongName.c_str(),NULL); } if (RetCode!=0) // Non-zero return code means success for CreateDirectory. { @@ -25,10 +25,10 @@ MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) return MKDIR_BADPATH; return MKDIR_ERROR; #elif defined(_UNIX) - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); + std::string NameA; + WideToChar(Name,NameA); mode_t uattr=SetAttr ? (mode_t)Attr:0777; - int ErrCode=mkdir(NameA,uattr); + int ErrCode=mkdir(NameA.c_str(),uattr); if (ErrCode==-1) return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; return MKDIR_SUCCESS; @@ -38,12 +38,19 @@ MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) } -bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) +// Simplified version of MakeDir(). +bool CreateDir(const std::wstring &Name) { - if (Path==NULL || *Path==0) + return MakeDir(Name,false,0)==MKDIR_SUCCESS; +} + + +bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent) +{ + if (Path.empty()) return false; -#if defined(_WIN_ALL) || defined(_EMX) +#ifdef _WIN_ALL uint DirAttr=0; #else uint DirAttr=0777; @@ -51,42 +58,36 @@ bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) bool Success=true; - for (const wchar *s=Path;*s!=0;s++) + for (size_t I=0;I=ASIZE(DirName)) - break; - // Process all kinds of path separators, so user can enter Unix style - // path in Windows or Windows in Unix. s>Path check avoids attempting + // path in Windows or Windows in Unix. I>0 check avoids attempting // creating an empty directory for paths starting from path separator. - if (IsPathDiv(*s) && s>Path) + if (IsPathDiv(Path[I]) && I>0) { #ifdef _WIN_ALL // We must not attempt to create "D:" directory, because first // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine // to create "D:" directory. - if (s==Path+2 && Path[1]==':') + if (I==2 && Path[1]==':') continue; #endif - wcsncpy(DirName,Path,s-Path); - DirName[s-Path]=0; - + std::wstring DirName=Path.substr(0,I); Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; if (Success && !Silent) { - mprintf(St(MCreatDir),DirName); + mprintf(St(MCreatDir),DirName.c_str()); mprintf(L" %s",St(MOk)); } } } - if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) + if (!SkipLastName && !IsPathDiv(GetLastChar(Path))) Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; return Success; } -void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) +void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta) { #if defined(_WIN_ALL) bool sm=ftm!=NULL && ftm->IsSet(); @@ -98,13 +99,13 @@ void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) if (ResetAttr) SetFileAttr(Name,0); - HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + hFile=CreateFile(LongName.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); } @@ -122,18 +123,18 @@ void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) if (ResetAttr) SetFileAttr(Name,DirAttr); #endif -#if defined(_UNIX) || defined(_EMX) +#ifdef _UNIX File::SetCloseFileTimeByName(Name,ftm,fta); #endif } -bool IsRemovable(const wchar *Name) +bool IsRemovable(const std::wstring &Name) { #if defined(_WIN_ALL) - wchar Root[NM]; - GetPathRoot(Name,Root,ASIZE(Root)); - int Type=GetDriveType(*Root!=0 ? Root:NULL); + std::wstring Root; + GetPathRoot(Name,Root); + int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str()); return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; #else return false; @@ -142,25 +143,25 @@ bool IsRemovable(const wchar *Name) #ifndef SFX_MODULE -int64 GetFreeDisk(const wchar *Name) +int64 GetFreeDisk(const std::wstring &Name) { #ifdef _WIN_ALL - wchar Root[NM]; - GetFilePath(Name,Root,ASIZE(Root)); + std::wstring Root; + GetPathWithSep(Name,Root); ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; - if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) && uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); return 0; #elif defined(_UNIX) - wchar Root[NM]; - GetFilePath(Name,Root,ASIZE(Root)); - char RootA[NM]; - WideToChar(Root,RootA,ASIZE(RootA)); + std::wstring Root; + GetPathWithSep(Name,Root); + std::string RootA; + WideToChar(Root,RootA); struct statvfs sfs; - if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) + if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0) return 0; int64 FreeSize=sfs.f_bsize; FreeSize=FreeSize*sfs.f_bavail; @@ -175,26 +176,27 @@ int64 GetFreeDisk(const wchar *Name) #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) // Return 'true' for FAT and FAT32, so we can adjust the maximum supported // file size to 4 GB for these file systems. -bool IsFAT(const wchar *Name) +bool IsFAT(const std::wstring &Name) { - wchar Root[NM]; - GetPathRoot(Name,Root,ASIZE(Root)); + std::wstring Root; + GetPathRoot(Name,Root); wchar FileSystem[MAX_PATH+1]; - if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) + // Root can be empty, when we create volumes with -v in the current folder. + if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; return false; } #endif -bool FileExist(const wchar *Name) +bool FileExist(const std::wstring &Name) { #ifdef _WIN_ALL return GetFileAttr(Name)!=0xffffffff; #elif defined(ENABLE_ACCESS) - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); - return access(NameA,0)==0; + std::string NameA; + WideToChar(Name,NameA); + return access(NameA.c_str(),0)==0; #else FindData FD; return FindFile::FastFind(Name,&FD); @@ -202,7 +204,7 @@ bool FileExist(const wchar *Name) } -bool WildFileExist(const wchar *Name) +bool WildFileExist(const std::wstring &Name) { if (IsWildcard(Name)) { @@ -261,66 +263,63 @@ bool IsDeleteAllowed(uint FileAttr) } -void PrepareToDelete(const wchar *Name) +void PrepareToDelete(const std::wstring &Name) { -#if defined(_WIN_ALL) || defined(_EMX) +#ifdef _WIN_ALL SetFileAttr(Name,0); #endif #ifdef _UNIX - if (Name!=NULL) - { - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); - chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); - } + std::string NameA; + WideToChar(Name,NameA); + chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR); #endif } -uint GetFileAttr(const wchar *Name) +uint GetFileAttr(const std::wstring &Name) { #ifdef _WIN_ALL - DWORD Attr=GetFileAttributes(Name); + DWORD Attr=GetFileAttributes(Name.c_str()); if (Attr==0xffffffff) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - Attr=GetFileAttributes(LongName); + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + Attr=GetFileAttributes(LongName.c_str()); } return Attr; #else - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); + std::string NameA; + WideToChar(Name,NameA); struct stat st; - if (stat(NameA,&st)!=0) + if (stat(NameA.c_str(),&st)!=0) return 0; return st.st_mode; #endif } -bool SetFileAttr(const wchar *Name,uint Attr) +bool SetFileAttr(const std::wstring &Name,uint Attr) { #ifdef _WIN_ALL - bool Success=SetFileAttributes(Name,Attr)!=0; + bool Success=SetFileAttributes(Name.c_str(),Attr)!=0; if (!Success) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - Success=SetFileAttributes(LongName,Attr)!=0; + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + Success=SetFileAttributes(LongName.c_str(),Attr)!=0; } return Success; #elif defined(_UNIX) - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); - return chmod(NameA,(mode_t)Attr)==0; + std::string NameA; + WideToChar(Name,NameA); + return chmod(NameA.c_str(),(mode_t)Attr)==0; #else return false; #endif } -wchar *MkTemp(wchar *Name,size_t MaxSize) +wchar* MkTemp(wchar *Name,size_t MaxSize) { size_t Length=wcslen(Name); @@ -344,7 +343,11 @@ wchar *MkTemp(wchar *Name,size_t MaxSize) { uint Ext=Random%50000+Attempt; wchar RndText[50]; - swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); + // 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); @@ -355,6 +358,40 @@ wchar *MkTemp(wchar *Name,size_t MaxSize) } +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"; + if (!FileExist(NewName)) + { + Name=NewName; + break; + } + } + return true; +} + + #if !defined(SFX_MODULE) void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) { @@ -370,8 +407,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, SrcFile->Seek(0,SEEK_SET); const size_t BufSize=0x100000; - Array Data(BufSize); - + std::vector Data(BufSize); DataHash HashCRC,HashBlake2; HashCRC.Init(HASH_CRC32,Threads); @@ -386,7 +422,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, SizeToRead=BufSize; // Then always attempt to read the entire buffer. else SizeToRead=(size_t)Min((int64)BufSize,Size); - int ReadSize=SrcFile->Read(&Data[0],SizeToRead); + int ReadSize=SrcFile->Read(Data.data(),SizeToRead); if (ReadSize==0) break; TotalRead+=ReadSize; @@ -410,9 +446,9 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, } if (CRC32!=NULL) - HashCRC.Update(&Data[0],ReadSize); + HashCRC.Update(Data.data(),ReadSize); if (Blake2!=NULL) - HashBlake2.Update(&Data[0],ReadSize); + HashBlake2.Update(Data.data(),ReadSize); if (Size!=INT64NDF) Size-=ReadSize; @@ -434,79 +470,78 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size, #endif -bool RenameFile(const wchar *SrcName,const wchar *DestName) +bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName) { #ifdef _WIN_ALL - bool Success=MoveFile(SrcName,DestName)!=0; + bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0; if (!Success) { - wchar LongName1[NM],LongName2[NM]; - if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && - GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) - Success=MoveFile(LongName1,LongName2)!=0; + std::wstring LongName1,LongName2; + if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2)) + Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0; } return Success; #else - char SrcNameA[NM],DestNameA[NM]; - WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); - WideToChar(DestName,DestNameA,ASIZE(DestNameA)); - bool Success=rename(SrcNameA,DestNameA)==0; + std::string SrcNameA,DestNameA; + WideToChar(SrcName,SrcNameA); + WideToChar(DestName,DestNameA); + bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0; return Success; #endif } -bool DelFile(const wchar *Name) +bool DelFile(const std::wstring &Name) { #ifdef _WIN_ALL - bool Success=DeleteFile(Name)!=0; + bool Success=DeleteFile(Name.c_str())!=0; if (!Success) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - Success=DeleteFile(LongName)!=0; + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + Success=DeleteFile(LongName.c_str())!=0; } return Success; #else - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); - bool Success=remove(NameA)==0; + std::string NameA; + WideToChar(Name,NameA); + bool Success=remove(NameA.c_str())==0; return Success; #endif } -bool DelDir(const wchar *Name) +bool DelDir(const std::wstring &Name) { #ifdef _WIN_ALL - bool Success=RemoveDirectory(Name)!=0; + bool Success=RemoveDirectory(Name.c_str())!=0; if (!Success) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - Success=RemoveDirectory(LongName)!=0; + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + Success=RemoveDirectory(LongName.c_str())!=0; } return Success; #else - char NameA[NM]; - WideToChar(Name,NameA,ASIZE(NameA)); - bool Success=rmdir(NameA)==0; + std::string NameA; + WideToChar(Name,NameA); + bool Success=rmdir(NameA.c_str())==0; return Success; #endif } #if defined(_WIN_ALL) && !defined(SFX_MODULE) -bool SetFileCompression(const wchar *Name,bool State) +bool SetFileCompression(const std::wstring &Name,bool State) { - HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, + HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hFile==INVALID_HANDLE_VALUE) { - wchar LongName[NM]; - if (GetWinLongPath(Name,LongName,ASIZE(LongName))) - hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, + std::wstring LongName; + if (GetWinLongPath(Name,LongName)) + hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); } @@ -521,11 +556,11 @@ bool SetFileCompression(const wchar *Name,bool State) } -void ResetFileCache(const wchar *Name) +void ResetFileCache(const std::wstring &Name) { // To reset file cache in Windows it is enough to open it with // FILE_FLAG_NO_BUFFERING and then close it. - HANDLE hSrc=CreateFile(Name,GENERIC_READ, + HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); if (hSrc!=INVALID_HANDLE_VALUE) @@ -542,3 +577,69 @@ void ResetFileCache(const wchar *Name) + + +// Delete symbolic links in file path, if any, and replace them by directories. +// Prevents extracting files outside of destination folder with symlink chains. +bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked) +{ + // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like + // "lnk1/../dir", but converts the path to "dir". In Unix we need to call + // this function to prevent placing unpacked files outside of destination + // folder if previously we unpacked "dir/lnk1" -> "..", + // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". + // We may still need this function to prevent abusing symlink chains + // in link source path if we remove detection of such chains + // in IsRelativeSymlinkSafe. This function seems to make other symlink + // related safety checks redundant, but for now we prefer to keep them too. + // + // 2022.12.01: the performance impact is minimized after adding the check + // against the previous path and enabling this verification only after + // extracting a symlink with ".." in target. So we enabled it for Windows + // as well for extra safety. +//#ifdef _UNIX + std::wstring Path=SrcName; + + size_t SkipLength=SkipPart.size(); + + if (SkipLength>0 && Path.rfind(SkipPart,0)!=0) + SkipLength=0; // Parameter validation, not really needed now. + + // Do not check parts already checked in previous path to improve performance. + for (size_t I=0;ISkipLength) + SkipLength=I; + + // Avoid converting symlinks in destination path part specified by user. + while (SkipLength0) + for (size_t I=Path.size()-1;I>SkipLength;I--) + if (IsPathDiv(Path[I])) + { + Path.erase(I); + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) + { +#ifdef _WIN_ALL + // Normally Windows symlinks to directory look like a directory + // and are deleted with DelDir(). It is possible to create + // a file-like symlink pointing at directory, which can be deleted + // only with && DelFile, but such symlink isn't really functional. + // Here we prefer to fail deleting such symlink and skip extracting + // a file. + if (!DelDir(Path)) +#else + if (!DelFile(Path)) +#endif + { + ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory. + return false; // Couldn't delete the symlink to replace it with directory. + } + } + } + LastChecked=SrcName; +//#endif + return true; +} diff --git a/unrar/filefn.hpp b/unrar/filefn.hpp index 53d86653..4e061973 100644 --- a/unrar/filefn.hpp +++ b/unrar/filefn.hpp @@ -3,47 +3,52 @@ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; -MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); -bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent); -void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); -bool IsRemovable(const wchar *Name); +MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr); +bool CreateDir(const std::wstring &Name); +bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent); +void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const std::wstring &Name); #ifndef SFX_MODULE -int64 GetFreeDisk(const wchar *Name); +int64 GetFreeDisk(const std::wstring &Name); #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) -bool IsFAT(const wchar *Root); +bool IsFAT(const std::wstring &Root); #endif -bool FileExist(const wchar *Name); -bool WildFileExist(const wchar *Name); +bool FileExist(const std::wstring &Name); +bool WildFileExist(const std::wstring &Name); bool IsDir(uint Attr); bool IsUnreadable(uint Attr); bool IsLink(uint Attr); -void SetSFXMode(const wchar *FileName); -void EraseDiskContents(const wchar *FileName); +void SetSFXMode(const std::wstring &FileName); +void EraseDiskContents(const std::wstring &FileName); bool IsDeleteAllowed(uint FileAttr); -void PrepareToDelete(const wchar *Name); -uint GetFileAttr(const wchar *Name); -bool SetFileAttr(const wchar *Name,uint Attr); +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); enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); -bool RenameFile(const wchar *SrcName,const wchar *DestName); -bool DelFile(const wchar *Name); -bool DelDir(const wchar *Name); +bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName); +bool DelFile(const std::wstring &Name); +bool DelDir(const std::wstring &Name); #if defined(_WIN_ALL) && !defined(SFX_MODULE) -bool SetFileCompression(const wchar *Name,bool State); -void ResetFileCache(const wchar *Name); +bool SetFileCompression(const std::wstring &Name,bool State); +void ResetFileCache(const std::wstring &Name); #endif +// Keep it here and not in extinfo.cpp, because it is invoked from Zip.SFX too. +bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked); + #endif diff --git a/unrar/filestr.cpp b/unrar/filestr.cpp index a5d29d74..ec45654d 100644 --- a/unrar/filestr.cpp +++ b/unrar/filestr.cpp @@ -1,7 +1,7 @@ #include "rar.hpp" bool ReadTextFile( - const wchar *Name, + const std::wstring &Name, StringList *List, bool Config, bool AbortOnError, @@ -10,17 +10,15 @@ bool ReadTextFile( bool SkipComments, bool ExpandEnvStr) { - wchar FileName[NM]; - *FileName=0; + std::wstring FileName; - if (Name!=NULL) - if (Config) - GetConfigName(Name,FileName,ASIZE(FileName),true,false); - else - wcsncpyz(FileName,Name,ASIZE(FileName)); + if (Config) + GetConfigName(Name,FileName,true,false); + else + FileName=Name; File SrcFile; - if (*FileName!=0) + if (!FileName.empty()) { bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); @@ -34,36 +32,36 @@ bool ReadTextFile( else SrcFile.SetHandleType(FILE_HANDLESTD); - uint DataSize=0,ReadSize; + size_t DataSize=0,ReadSize; const int ReadBlock=4096; - Array Data(ReadBlock); + std::vector Data(ReadBlock); while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) { DataSize+=ReadSize; - Data.Add(ReadSize); // Always have ReadBlock available for next data. + Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data. } // Set to really read size, so we can zero terminate it correctly. - Data.Alloc(DataSize); + Data.resize(DataSize); int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; if (SrcCharset==RCH_DEFAULT) - SrcCharset=DetectTextEncoding(&Data[0],DataSize); + SrcCharset=DetectTextEncoding(Data.data(),DataSize); - Array DataW; + std::vector DataW(ReadBlock); if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) { - Data.Push(0); // Zero terminate. + Data.push_back(0); // Zero terminate. #if defined(_WIN_ALL) if (SrcCharset==RCH_OEM) - OemToCharA((char *)&Data[0],(char *)&Data[0]); + OemToCharA((char *)Data.data(),(char *)Data.data()); #endif - DataW.Alloc(Data.Size()); - CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); + DataW.resize(Data.size()); + CharToWide((char *)Data.data(),DataW.data(),DataW.size()); } if (SrcCharset==RCH_UNICODE) @@ -75,8 +73,8 @@ bool ReadTextFile( LittleEndian=1; } - DataW.Alloc(Data.Size()/2+1); - size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. + DataW.resize(Data.size()/2+1); + size_t End=Data.size() & ~1; // We need even bytes number for UTF-16. for (size_t I=Start;IAddString(ExpName); + Expanded=true; } #endif if (!Expanded && *CurStr!=0) diff --git a/unrar/filestr.hpp b/unrar/filestr.hpp index febd0a29..3c281304 100644 --- a/unrar/filestr.hpp +++ b/unrar/filestr.hpp @@ -2,7 +2,7 @@ #define _RAR_FILESTR_ bool ReadTextFile( - const wchar *Name, + const std::wstring &Name, StringList *List, bool Config, bool AbortOnError=false, diff --git a/unrar/find.cpp b/unrar/find.cpp index c9f2c576..64d933eb 100644 --- a/unrar/find.cpp +++ b/unrar/find.cpp @@ -2,7 +2,6 @@ FindFile::FindFile() { - *FindMask=0; FirstCall=true; #ifdef _WIN_ALL hFind=INVALID_HANDLE_VALUE; @@ -24,9 +23,9 @@ FindFile::~FindFile() } -void FindFile::SetMask(const wchar *Mask) +void FindFile::SetMask(const std::wstring &Mask) { - wcsncpyz(FindMask,Mask,ASIZE(FindMask)); + FindMask=Mask; FirstCall=true; } @@ -34,7 +33,7 @@ void FindFile::SetMask(const wchar *Mask) bool FindFile::Next(FindData *fd,bool GetSymLink) { fd->Error=false; - if (*FindMask==0) + if (FindMask.empty()) return false; #ifdef _WIN_ALL if (FirstCall) @@ -48,14 +47,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink) #else if (FirstCall) { - wchar DirName[NM]; - wcsncpyz(DirName,FindMask,ASIZE(DirName)); + std::wstring DirName; + DirName=FindMask; RemoveNameFromPath(DirName); - if (*DirName==0) - wcsncpyz(DirName,L".",ASIZE(DirName)); - char DirNameA[NM]; - WideToChar(DirName,DirNameA,ASIZE(DirNameA)); - if ((dirp=opendir(DirNameA))==NULL) + if (DirName.empty()) + DirName=L"."; + std::string DirNameA; + WideToChar(DirName,DirNameA); + if ((dirp=opendir(DirNameA.c_str()))==NULL) { fd->Error=(errno!=ENOENT); return false; @@ -63,32 +62,31 @@ bool FindFile::Next(FindData *fd,bool GetSymLink) } while (1) { - wchar Name[NM]; + std::wstring Name; struct dirent *ent=readdir(dirp); if (ent==NULL) return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; - if (!CharToWide(ent->d_name,Name,ASIZE(Name))) - uiMsg(UIERROR_INVALIDNAME,UINULL,Name); + if (!CharToWide(std::string(ent->d_name),Name)) + uiMsg(UIERROR_INVALIDNAME,L"",Name); if (CmpName(FindMask,Name,MATCH_NAMES)) { - wchar FullName[NM]; - wcsncpyz(FullName,FindMask,ASIZE(FullName)); - *PointToName(FullName)=0; - if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) + std::wstring FullName=FindMask; + FullName.erase(GetNamePos(FullName)); + if (FullName.size()+Name.size()>=MAXPATHSIZE) { uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); return false; } - wcsncatz(FullName,Name,ASIZE(FullName)); + FullName+=Name; if (!FastFind(FullName,fd,GetSymLink)) { ErrHandler.OpenErrorMsg(FullName); continue; } - wcsncpyz(fd->Name,FullName,ASIZE(fd->Name)); + fd->Name=FullName; break; } } @@ -98,14 +96,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink) fd->IsLink=IsLink(fd->FileAttr); FirstCall=false; - wchar *NameOnly=PointToName(fd->Name); - if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) + std::wstring NameOnly=PointToName(fd->Name); + if (NameOnly==L"." || NameOnly==L"..") return Next(fd); return true; } -bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) +bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink) { fd->Error=false; #ifndef _UNIX @@ -118,16 +116,16 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) return false; FindClose(hFind); #elif defined(_UNIX) - char FindMaskA[NM]; - WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); + std::string FindMaskA; + WideToChar(FindMask,FindMaskA); struct stat st; if (GetSymLink) { #ifdef SAVE_LINKS - if (lstat(FindMaskA,&st)!=0) + if (lstat(FindMaskA.c_str(),&st)!=0) #else - if (stat(FindMaskA,&st)!=0) + if (stat(FindMaskA.c_str(),&st)!=0) #endif { fd->Error=(errno!=ENOENT); @@ -135,7 +133,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) } } else - if (stat(FindMaskA,&st)!=0) + if (stat(FindMaskA.c_str(),&st)!=0) { fd->Error=(errno!=ENOENT); return false; @@ -145,7 +143,7 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime); - wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); + fd->Name=FindMask; #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); @@ -156,17 +154,17 @@ bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) #ifdef _WIN_ALL -HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) +HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd) { WIN32_FIND_DATA FindData; if (hFind==INVALID_HANDLE_VALUE) { - hFind=FindFirstFile(Mask,&FindData); + hFind=FindFirstFile(Mask.c_str(),&FindData); if (hFind==INVALID_HANDLE_VALUE) { - wchar LongMask[NM]; - if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) - hFind=FindFirstFile(LongMask,&FindData); + std::wstring LongMask; + if (GetWinLongPath(Mask,LongMask)) + hFind=FindFirstFile(LongMask.c_str(),&FindData); } if (hFind==INVALID_HANDLE_VALUE) { @@ -190,8 +188,8 @@ HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) if (hFind!=INVALID_HANDLE_VALUE) { - wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); - SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); + fd->Name=Mask; + SetName(fd->Name,FindData.cFileName); fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); fd->FileAttr=FindData.dwFileAttributes; fd->ftCreationTime=FindData.ftCreationTime; diff --git a/unrar/find.hpp b/unrar/find.hpp index 250637f8..6812def7 100644 --- a/unrar/find.hpp +++ b/unrar/find.hpp @@ -7,7 +7,7 @@ enum FINDDATA_FLAGS { struct FindData { - wchar Name[NM]; + std::wstring Name; uint64 Size; uint FileAttr; bool IsDir; @@ -28,10 +28,10 @@ class FindFile { private: #ifdef _WIN_ALL - static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); + static HANDLE Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd); #endif - wchar FindMask[NM]; + std::wstring FindMask; bool FirstCall; #ifdef _WIN_ALL HANDLE hFind; @@ -41,9 +41,9 @@ class FindFile public: FindFile(); ~FindFile(); - void SetMask(const wchar *Mask); + void SetMask(const std::wstring &Mask); bool Next(FindData *fd,bool GetSymLink=false); - static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); + static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false); }; #endif diff --git a/unrar/getbits.cpp b/unrar/getbits.cpp index 5d5ad2bb..407b633a 100644 --- a/unrar/getbits.cpp +++ b/unrar/getbits.cpp @@ -5,11 +5,11 @@ BitInput::BitInput(bool AllocBuffer) ExternalBuffer=false; if (AllocBuffer) { - // getbits*() attempt to read data from InAddr, ... InAddr+4 positions. - // So let's allocate 4 additional bytes for situation, when we need to + // getbits*() attempt to read data from InAddr, ... InAddr+8 positions. + // So let's allocate 8 additional bytes for situation, when we need to // read only 1 byte from the last position of buffer and avoid a crash - // from access to next 4 bytes, which contents we do not need. - size_t BufSize=MAX_SIZE+4; + // from access to next 8 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+8; InBuf=new byte[BufSize]; // Ensure that we get predictable results when accessing bytes in area @@ -17,7 +17,7 @@ BitInput::BitInput(bool AllocBuffer) memset(InBuf,0,BufSize); } else - InBuf=NULL; + InBuf=nullptr; } diff --git a/unrar/getbits.hpp b/unrar/getbits.hpp index 00acbea9..65fb25a1 100644 --- a/unrar/getbits.hpp +++ b/unrar/getbits.hpp @@ -34,8 +34,7 @@ class BitInput uint getbits() { #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) - uint32 BitField=*(uint32*)(InBuf+InAddr); - BitField=ByteSwap32(BitField); + uint32 BitField=RawGetBE4(InBuf+InAddr); BitField >>= (16-InBit); #else uint BitField=(uint)InBuf[InAddr] << 16; @@ -51,19 +50,21 @@ class BitInput // Bit at (InAddr,InBit) has the highest position in returning data. uint getbits32() { -#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) - uint32 BitField=*(uint32*)(InBuf+InAddr); - BitField=ByteSwap32(BitField); -#else - uint BitField=(uint)InBuf[InAddr] << 24; - BitField|=(uint)InBuf[InAddr+1] << 16; - BitField|=(uint)InBuf[InAddr+2] << 8; - BitField|=(uint)InBuf[InAddr+3]; -#endif + uint BitField=RawGetBE4(InBuf+InAddr); BitField <<= InBit; BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); return BitField & 0xffffffff; } + + // Return 64 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint64 getbits64() + { + uint64 BitField=RawGetBE8(InBuf+InAddr); + BitField <<= InBit; + BitField|=(uint)InBuf[InAddr+8] >> (8-InBit); + return BitField; + } void faddbits(uint Bits); uint fgetbits(); diff --git a/unrar/hardlinks.cpp b/unrar/hardlinks.cpp index 171b5fa0..5080da05 100644 --- a/unrar/hardlinks.cpp +++ b/unrar/hardlinks.cpp @@ -1,4 +1,4 @@ -bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting) { if (!FileExist(NameExisting)) { @@ -10,7 +10,7 @@ bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t CreatePath(NameNew,true,Cmd->DisableNames); #ifdef _WIN_ALL - bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; + bool Success=CreateHardLink(NameNew.c_str(),NameExisting.c_str(),NULL)!=0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); @@ -19,10 +19,10 @@ bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t } return Success; #elif defined(_UNIX) - char NameExistingA[NM],NameNewA[NM]; - WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); - WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); - bool Success=link(NameExistingA,NameNewA)==0; + std::string NameExistingA,NameNewA; + WideToChar(NameExisting,NameExistingA); + WideToChar(NameNew,NameNewA); + bool Success=link(NameExistingA.c_str(),NameNewA.c_str())==0; if (!Success) { uiMsg(UIERROR_HLINKCREATE,NameNew); diff --git a/unrar/headers.cpp b/unrar/headers.cpp index b441376c..b2d00d3b 100644 --- a/unrar/headers.cpp +++ b/unrar/headers.cpp @@ -2,7 +2,7 @@ void FileHeader::Reset(size_t SubDataSize) { - SubData.Alloc(SubDataSize); + SubData.resize(SubDataSize); BaseBlock::Reset(); FileHash.Init(HASH_NONE); mtime.Reset(); @@ -37,6 +37,7 @@ void FileHeader::Reset(size_t SubDataSize) } +/* FileHeader& FileHeader::operator = (FileHeader &hd) { SubData.Reset(); @@ -45,6 +46,7 @@ FileHeader& FileHeader::operator = (FileHeader &hd) SubData=hd.SubData; return *this; } +*/ void MainHeader::Reset() diff --git a/unrar/headers.hpp b/unrar/headers.hpp index 5984f996..8697e388 100644 --- a/unrar/headers.hpp +++ b/unrar/headers.hpp @@ -15,8 +15,10 @@ #define VER_PACK 29U #define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive. +#define VER_PACK7 70U // It is stored as 1, but we subtract 70 when saving an archive. #define VER_UNPACK 29U #define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive. +#define VER_UNPACK7 70U // It is stored as 1, but we add 50 when reading an archive. #define VER_UNKNOWN 9999U // Just some large value. #define MHD_VOLUME 0x0001U @@ -82,7 +84,7 @@ enum HEADER_TYPE { }; -// RAR 2.9 and earlier. +// RAR 2.9 and earlier service haeders, mostly outdated and not supported. enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; @@ -147,6 +149,14 @@ struct BaseBlock { SkipIfUnknown=false; } + + // We use it to assign this block data to inherited blocks. + // Such function seems to be cleaner than '(BaseBlock&)' cast or adding + // 'using BaseBlock::operator=;' to every inherited header. + void SetBaseBlock(BaseBlock &Src) + { + *this=Src; + } }; @@ -184,9 +194,9 @@ struct FileHeader:BlockHeader uint FileAttr; uint SubFlags; }; - wchar FileName[NM]; + std::wstring FileName; - Array SubData; + std::vector SubData; RarTime mtime; RarTime ctime; @@ -226,7 +236,7 @@ struct FileHeader:BlockHeader bool Dir; bool CommentInHeader; // RAR 2.0 file comment. bool Version; // name.ext;ver file name containing the version number. - size_t WinSize; + uint64 WinSize; bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. @@ -239,7 +249,7 @@ struct FileHeader:BlockHeader HOST_SYSTEM_TYPE HSType; FILE_SYSTEM_REDIRECT RedirType; - wchar RedirName[NM]; + std::wstring RedirName; bool DirTarget; bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; @@ -256,10 +266,10 @@ struct FileHeader:BlockHeader bool CmpName(const wchar *Name) { - return(wcscmp(FileName,Name)==0); + return FileName==Name; } - FileHeader& operator = (FileHeader &hd); +// FileHeader& operator = (FileHeader &hd); }; @@ -340,7 +350,7 @@ struct StreamHeader:SubBlockHeader byte Method; uint StreamCRC; ushort StreamNameSize; - char StreamName[260]; + std::string StreamName; }; diff --git a/unrar/headers5.hpp b/unrar/headers5.hpp index 50f5955d..c5c3fe3f 100644 --- a/unrar/headers5.hpp +++ b/unrar/headers5.hpp @@ -42,20 +42,26 @@ // RAR 5.0 file compression flags. -#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. -#define FCI_ALGO_BIT1 0x0002 // 0 .. 63. -#define FCI_ALGO_BIT2 0x0004 -#define FCI_ALGO_BIT3 0x0008 -#define FCI_ALGO_BIT4 0x0010 -#define FCI_ALGO_BIT5 0x0020 -#define FCI_SOLID 0x0040 // Solid flag. -#define FCI_METHOD_BIT0 0x0080 // Compression method. -#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). -#define FCI_METHOD_BIT2 0x0200 -#define FCI_DICT_BIT0 0x0400 // Dictionary size. -#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. -#define FCI_DICT_BIT2 0x1000 -#define FCI_DICT_BIT3 0x2000 +#define FCI_ALGO_BIT0 0x00000001 // Version of compression algorithm. +#define FCI_ALGO_BIT1 0x00000002 // 0 .. 63. +#define FCI_ALGO_BIT2 0x00000004 +#define FCI_ALGO_BIT3 0x00000008 +#define FCI_ALGO_BIT4 0x00000010 +#define FCI_ALGO_BIT5 0x00000020 +#define FCI_SOLID 0x00000040 // Solid flag. +#define FCI_METHOD_BIT0 0x00000080 // Compression method. +#define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used). +#define FCI_METHOD_BIT2 0x00000200 +#define FCI_DICT_BIT0 0x00000400 // Dictionary size. +#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT2 0x00001000 +#define FCI_DICT_BIT3 0x00002000 +#define FCI_DICT_BIT4 0x00004000 +#define FCI_DICT_FRACT0 0x00008000 // Dictionary fraction in 1/32 of size. +#define FCI_DICT_FRACT1 0x00010000 +#define FCI_DICT_FRACT2 0x00020000 +#define FCI_DICT_FRACT3 0x00040000 +#define FCI_DICT_FRACT4 0x00080000 // Main header extra field values. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index 3cc876b9..a1e95e22 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -62,30 +62,24 @@ static bool WMI_IsWindows10() hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); - if (FAILED(hres)) + if (FAILED(hres) || pEnumerator==NULL) { pSvc->Release(); pLoc->Release(); return false; } + bool Win10=false; + IWbemClassObject *pclsObj = NULL; ULONG uReturn = 0; - - bool Win10=false; - while (pEnumerator!=NULL) + pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + if (pclsObj!=NULL && uReturn>0) { - HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); - - if (uReturn==0) - break; - VARIANT vtProp; - - hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); + pclsObj->Get(L"Name", 0, &vtProp, 0, 0); Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL; VariantClear(&vtProp); - pclsObj->Release(); } diff --git a/unrar/list.cpp b/unrar/list.cpp index e4444e13..15a3fb4d 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -1,7 +1,6 @@ #include "rar.hpp" static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames); -static void ListSymLink(Archive &Arc); static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); static void ListOldSubHeader(Archive &Arc); static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); @@ -15,8 +14,8 @@ void ListArchive(CommandData *Cmd) bool Bare=(Cmd->Command[1]=='B'); bool Verbose=(Cmd->Command[0]=='V'); - wchar ArcName[NM]; - while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + std::wstring ArcName; + while (Cmd->GetArcName(ArcName)) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. @@ -35,33 +34,31 @@ void ListArchive(CommandData *Cmd) if (!Bare) { Arc.ViewComment(); - mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName.c_str()); mprintf(L"\n%s: ",St(MListDetails)); - uint SetCount=0; const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); + mprintf(L"%s", Fmt); if (Arc.Solid) - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); + mprintf(L", %s", St(MListSolid)); if (Arc.SFXSize>0) - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); + mprintf(L", %s", St(MListSFX)); if (Arc.Volume) if (Arc.Format==RARFMT50) { // RAR 5.0 archives store the volume number in main header, // so it is already available now. - if (SetCount++ > 0) - mprintf(L", "); + mprintf(L", "); mprintf(St(MVolumeNumber),Arc.VolNumber+1); } else - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); + mprintf(L", %s", St(MListVolume)); if (Arc.Protected) - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); + mprintf(L"%, %s", St(MListRR)); if (Arc.Locked) - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); + mprintf(L", %s", St(MListLock)); if (Arc.Encrypted) - mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + mprintf(L", %s", St(MListEncHead)); if (!Arc.MainHead.OrigName.empty()) mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str()); @@ -100,7 +97,7 @@ void ListArchive(CommandData *Cmd) switch(HeaderType) { case HEAD_FILE: - FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0; + FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0; if (FileMatched) { ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames); @@ -155,7 +152,7 @@ void ListArchive(CommandData *Cmd) ArcCount++; #ifndef NOVOLUME - if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || + if (Cmd->VolSize==VOLSIZE_AUTO && (Arc.FileHead.SplitAfter || Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && MergeArchive(Arc,NULL,false,Cmd->Command[0])) Arc.Seek(0,SEEK_SET); @@ -166,7 +163,7 @@ void ListArchive(CommandData *Cmd) else { if (Cmd->ArcNames.ItemsCount()<2 && !Bare) - mprintf(St(MNotRAR),Arc.FileName); + mprintf(St(MNotRAR),Arc.FileName.c_str()); break; } } @@ -219,7 +216,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (DisableNames) return; - wchar *Name=hd.FileName; + const wchar *Name=hd.FileName.c_str(); RARFORMAT Format=Arc.Format; if (Bare) @@ -266,9 +263,8 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) { mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); - wchar StreamName[NM]; - GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); - mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); + std::wstring StreamName=GetStreamNameNTFS(Arc); + mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName.c_str()); } else { @@ -292,25 +288,27 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (hd.RedirType!=FSREDIR_NONE) if (Format==RARFMT15) { - char LinkTargetA[NM]; + std::string LinkTargetA; if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. - strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); + LinkTargetA="*<-?->"; } else { - int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); - Arc.Read(LinkTargetA,DataSize); - LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; + size_t DataSize=(size_t)Min(hd.PackSize,MAXPATHSIZE); + std::vector Buf(DataSize+1); + Arc.Read(Buf.data(),DataSize); + Buf[DataSize] = 0; + LinkTargetA=Buf.data(); } - wchar LinkTarget[NM]; - CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); - mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); + std::wstring LinkTarget; + CharToWide(LinkTargetA,LinkTarget); + mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget.c_str()); } else - mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); + mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName.c_str()); } if (!hd.Dir) { @@ -341,11 +339,11 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo hd.FileHash.CRC32); if (hd.FileHash.Type==HASH_BLAKE2) { - wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; - BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); + std::wstring BlakeStr; + BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,BlakeStr); mprintf(L"\n%12ls: %ls", hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", - BlakeStr); + BlakeStr.c_str()); } const wchar *HostOS=L""; @@ -362,11 +360,22 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (*HostOS!=0) mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS); - mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo), + std::wstring WinSize; + if (!hd.Dir) + if (hd.WinSize%1073741824==0) + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"G"; + else + if (hd.WinSize%1048576==0) + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"M"; + else + if (hd.WinSize>=1024) + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"K"; + else + WinSize=L" -md=?(>2G)"; + + mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo), Format==RARFMT15 ? L"1.5":L"5.0", - hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method, - hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, - hd.WinSize>=0x100000 ? L"M":L"K"); + hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,WinSize.c_str()); if (hd.Solid || hd.Encrypted) { @@ -379,7 +388,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (hd.Version) { - uint Version=ParseVersionFileName(Name,false); + uint Version=ParseVersionFileName(hd.FileName,false); if (Version!=0) mprintf(L"\n%12ls: %u",St(MListFileVer),Version); } @@ -388,13 +397,13 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo { mprintf(L"\n%12ls: ",L"Unix owner"); if (*hd.UnixOwnerName!=0) - mprintf(L"%ls",GetWide(hd.UnixOwnerName)); + mprintf(L"%ls",GetWide(hd.UnixOwnerName).c_str()); else if (hd.UnixOwnerNumeric) mprintf(L"#%d",hd.UnixOwnerID); mprintf(L":"); if (*hd.UnixGroupName!=0) - mprintf(L"%ls",GetWide(hd.UnixGroupName)); + mprintf(L"%ls",GetWide(hd.UnixGroupName).c_str()); else if (hd.UnixGroupNumeric) mprintf(L"#%d",hd.UnixGroupID); @@ -427,26 +436,6 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo mprintf(L"%ls",Name); } -/* -void ListSymLink(Archive &Arc) -{ - if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) - if (Arc.FileHead.Encrypted) - { - // Link data are encrypted. We would need to ask for password - // and initialize decryption routine to display the link target. - mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); - } - else - { - char FileName[NM]; - uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); - Arc.Read(FileName,DataSize); - FileName[DataSize]=0; - mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); - } -} -*/ void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) { diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 8c7c0881..a4342b98 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -94,9 +94,8 @@ #define MCHelpSwKB L"\n kb Keep broken extracted files" #define MCHelpSwLog L"\n log[f][=name] Write names to log file" #define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)" -#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" #define MCHelpSwMC L"\n mc Set advanced compression parameters" -#define MCHelpSwMD L"\n md[k,m,g] Dictionary size in KB, MB or GB" +#define MCHelpSwMD L"\n md[x][kmg] Dictionary size in KB, MB or GB" #define MCHelpSwME L"\n me[par] Set encryption parameters" #define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store" #define MCHelpSwMT L"\n mt Set the number of threads" @@ -107,7 +106,7 @@ #define MCHelpSwOC L"\n oc Set NTFS Compressed attribute" #define MCHelpSwOH L"\n oh Save hard links as the link instead of the file" #define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references" -#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]" +#define MCHelpSwOL L"\n ol[a,-] Process symbolic links as the link [absolute paths, skip]" #define 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" @@ -126,8 +125,8 @@ #define MCHelpSwSC L"\n sc[obj] Specify the character set" #define MCHelpSwSFX L"\n sfx[name] Create SFX archive" #define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" -#define MCHelpSwSL L"\n sl Process files with size less than specified" -#define MCHelpSwSM L"\n sm Process files with size more than specified" +#define MCHelpSwSL L"\n sl[u] Process files with size less than specified" +#define MCHelpSwSM L"\n sm[u] Process files with size more than specified" #define MCHelpSwT L"\n t Test files after archiving" #define MCHelpSwTK L"\n tk Keep original archive time" #define MCHelpSwTL L"\n tl Set archive time to latest file" @@ -139,10 +138,9 @@ #define MCHelpSwU L"\n u Update files" #define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes" #define MCHelpSwVUnr L"\n v List all volumes" -#define MCHelpSwVn L"\n v[k,b] Create volumes with size=*1000 [*1024, *1]" +#define MCHelpSwVn L"\n v[u] Create volumes with size in [bBkKmMgGtT] units" #define MCHelpSwVD L"\n vd Erase disk contents before creating volume" #define MCHelpSwVER L"\n ver[n] File version control" -#define MCHelpSwVN L"\n vn Use the old style volume naming scheme" #define MCHelpSwVP L"\n vp Pause before each volume" #define MCHelpSwW L"\n w Assign work directory" #define MCHelpSwX L"\n x Exclude specified file" @@ -284,7 +282,6 @@ #define MListHostOS L"Host OS" #define MListFileVer L"File version" #define MListService L"Service" -#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s" #define MListNTACLHead L"\n NTFS security data" #define MListStrmHead L"\n NTFS stream: %s" #define MListUnkHead L"\n Unknown subheader type: 0x%04x" @@ -296,7 +293,7 @@ #define MRprBuild L"\nBuilding %s" #define MRprOldFormat L"\nCannot repair archive with old format" #define MRprFind L"\nFound %s" -#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?" +#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid?" #define MRprNoFiles L"\nNo files found" #define MLogUnexpEOF L"\nUnexpected end of archive" #define MRepAskReconst L"\nReconstruct archive structure ?" @@ -330,6 +327,7 @@ #define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name" #define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" #define MUnknownOption L"\nERROR: Unknown option: %s" +#define MSwSyntaxError L"\nERROR: '-' is expected in the beginning of: %s" #define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored" #define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored" #define MSubHeadDataCRC L"\nERROR: Corrupt %s data block" @@ -386,6 +384,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 MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u MB 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 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" @@ -399,3 +399,6 @@ #define MOrigName L"Original name" #define MOriginalTime L"Original time" #define MFileRenamed L"\n%s is renamed to %s" +#define MDictNotAllowed L"\n%u GB dictionary exceeds %u GB limit and needs more than %u GB memory to unpack." +#define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway." +#define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data." diff --git a/unrar/log.cpp b/unrar/log.cpp index 8bbe8ee0..074ecdb2 100644 --- a/unrar/log.cpp +++ b/unrar/log.cpp @@ -1,13 +1,14 @@ #include "rar.hpp" -static wchar LogName[NM]; -static RAR_CHARSET LogCharset=RCH_DEFAULT; -void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet) +void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet) +{ +} + + +void CloseLogOptions() { - wcsncpyz(LogName,LogFileName,ASIZE(LogName)); - LogCharset=CSet; } @@ -19,17 +20,13 @@ void Log(const wchar *ArcName,const wchar *fmt,...) uiAlarm(UIALARM_ERROR); - // This buffer is for format string only, not for entire output, - // so it can be short enough. - wchar fmtw[1024]; - PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); - - safebuf wchar Msg[2*NM+1024]; va_list arglist; va_start(arglist,fmt); - vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + + std::wstring s=vwstrprintf(fmt,arglist); + va_end(arglist); - eprintf(L"%ls",Msg); + eprintf(L"%ls",s.c_str()); ErrHandler.SetSystemErrorCode(Code); } #endif diff --git a/unrar/log.hpp b/unrar/log.hpp index 008ef11a..22eaa8e6 100644 --- a/unrar/log.hpp +++ b/unrar/log.hpp @@ -1,7 +1,8 @@ #ifndef _RAR_LOG_ #define _RAR_LOG_ -void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet); +void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet); +void CloseLogOptions(); #ifdef SILENT inline void Log(const wchar *ArcName,const wchar *fmt,...) {} diff --git a/unrar/makefile b/unrar/makefile index 55af49be..6561bf00 100644 --- a/unrar/makefile +++ b/unrar/makefile @@ -3,7 +3,7 @@ # Linux using GCC CXX=c++ -CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else +CXXFLAGS=-march=native -O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else LIBFLAGS=-fPIC DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP STRIP=strip @@ -11,107 +11,6 @@ AR=ar LDFLAGS=-pthread DESTDIR=/usr -# Linux using LCC -#CXX=lcc -#CXXFLAGS=-O2 -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# CYGWIN using GCC -#CXX=c++ -#CXXFLAGS=-O2 -#LIBFLAGS= -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP -#STRIP=strip -#AR=ar -#LDFLAGS=-pthread -#DESTDIR=/usr - -# HP UX using aCC -#CXX=aCC -#CXXFLAGS=-AA +O2 +Onolimit -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# IRIX using GCC -#CXX=g++ -#CXXFLAGS=-O2 -#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1 -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# IRIX using MIPSPro (experimental) -#CXX=CC -#CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std -#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# AIX using xlC (IBM VisualAge C++ 5.0) -#CXX=xlC -#CXXFLAGS=-O -qinline -qro -qroconst -qmaxmem=16384 -qcpluscmt -#DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API -#LIBS=-lbsd -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# Solaris using CC -#CXX=CC -#CXXFLAGS=-fast -erroff=wvarhidemem -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -#STRIP=strip -#AR=ar -#DESTDIR=/usr - -# Solaris using GCC (optimized for UltraSPARC 1 CPU) -#CXX=g++ -#CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32 -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -#STRIP=/usr/ccs/bin/strip -#AR=/usr/ccs/bin/ar -#DESTDIR=/usr - -# Tru64 5.1B using GCC3 -#CXX=g++ -#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500 -#STRIP=strip -#AR=ar -#LDFLAGS=-rpath /usr/local/gcc/lib -#DESTDIR=/usr - -# Tru64 5.1B using DEC C++ -#CXX=cxx -#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long -#STRIP=strip -#AR=ar -#LDFLAGS= -#DESTDIR=/usr - -# QNX 6.x using GCC -#CXX=g++ -#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions -#STRIP=strip -#AR=ar -#LDFLAGS=-fexceptions -#DESTDIR=/usr - -# Cross-compile -# Linux using arm-linux-g++ -#CXX=arm-linux-g++ -#CXXFLAGS=-O2 -#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -#STRIP=arm-linux-strip -#AR=arm-linux-ar -#LDFLAGS=-static -#DESTDIR=/usr - ########################## COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) diff --git a/unrar/match.cpp b/unrar/match.cpp index ec88fa61..0a78d8dd 100644 --- a/unrar/match.cpp +++ b/unrar/match.cpp @@ -3,6 +3,7 @@ static bool match(const wchar *pattern,const wchar *string,bool ForceCase); static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase); static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); +static bool IsWildcard(const wchar *Str,size_t CheckSize); inline uint touppercw(uint ch,bool ForceCase) { @@ -16,12 +17,15 @@ inline uint touppercw(uint ch,bool ForceCase) } -bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode) +bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode) { bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0; CmpMode&=MATCH_MODEMASK; + wchar *Name1=PointToName(Wildcard); + wchar *Name2=PointToName(Name); + if (CmpMode!=MATCH_NAMES) { size_t WildLength=wcslen(Wildcard); @@ -32,47 +36,48 @@ bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode) // "path1" mask must match "path1\path2\filename.ext" and "path1" names. wchar NextCh=Name[WildLength]; if (NextCh==L'\\' || NextCh==L'/' || NextCh==0) - return(true); + return true; } // Nothing more to compare for MATCH_SUBPATHONLY. if (CmpMode==MATCH_SUBPATHONLY) - return(false); - - wchar Path1[NM],Path2[NM]; - GetFilePath(Wildcard,Path1,ASIZE(Path1)); - GetFilePath(Name,Path2,ASIZE(Path2)); - - if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && - mwcsicompc(Path1,Path2,ForceCase)!=0) - return(false); + return false; + + // 2023.08.29: We tried std::wstring Path1 and Path2 here, but performance + // impact for O(n^2) complexity loop in CmdExtract::AnalyzeArchive() + // was rather noticeable, 1.7s instead of 0.9s when extracting ~300 files + // with @listfile from archive with ~7000 files. + // This function can be invoked from other O(n^2) loops. So for now + // we prefer to avoid wstring and use pointers and path sizes here. + // Another option could be using std::wstring_view. + + size_t Path1Size=Name1-Wildcard; + size_t Path2Size=Name2-Name; + + if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && + (Path1Size!=Path2Size || + mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)) + return false; if (CmpMode==MATCH_ALLWILD) return match(Wildcard,Name,ForceCase); if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH) - if (IsWildcard(Path1)) - return(match(Wildcard,Name,ForceCase)); + if (IsWildcard(Wildcard,Path1Size)) + return match(Wildcard,Name,ForceCase); else if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard)) { - if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0) - return(false); + if (Path1Size>0 && mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0) + return false; } else - if (mwcsicompc(Path1,Path2,ForceCase)!=0) - return(false); + if (Path1Size!=Path2Size || mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0) + return false; } - wchar *Name1=PointToName(Wildcard); - wchar *Name2=PointToName(Name); - - // Always return false for RAR temporary files to exclude them - // from archiving operations. -// if (mwcsnicompc(L"__rar_",Name2,6,false)==0) -// return(false); if (CmpMode==MATCH_EXACT) - return(mwcsicompc(Name1,Name2,ForceCase)==0); + return mwcsicompc(Name1,Name2,ForceCase)==0; - return(match(Name1,Name2,ForceCase)); + return match(Name1,Name2,ForceCase); } @@ -85,18 +90,18 @@ bool match(const wchar *pattern,const wchar *string,bool ForceCase) switch (patternc) { case 0: - return(stringc==0); + return stringc==0; case '?': if (stringc == 0) - return(false); + return false; break; case '*': if (*pattern==0) - return(true); + return true; if (*pattern=='.') { if (pattern[1]=='*' && pattern[2]==0) - return(true); + return true; const wchar *dot=wcschr(string,'.'); if (pattern[1]==0) return (dot==NULL || dot[1]==0); @@ -104,22 +109,22 @@ bool match(const wchar *pattern,const wchar *string,bool ForceCase) { string=dot; if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL) - return(mwcsicompc(pattern+1,string+1,ForceCase)==0); + return mwcsicompc(pattern+1,string+1,ForceCase)==0; } } while (*string) if (match(pattern,string++,ForceCase)) - return(true); - return(false); + return true; + return false; default: if (patternc != stringc) { // Allow "name." mask match "name" and "name.\" match "name\". if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.')) - return(match(pattern,string,ForceCase)); + return match(pattern,string,ForceCase); else - return(false); + return false; } break; } @@ -145,3 +150,18 @@ int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) return wcsnicomp(Str1,Str2,N); #endif } + + +bool IsWildcard(const wchar *Str,size_t CheckSize) +{ + size_t CheckPos=0; +#ifdef _WIN_ALL + // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\') + CheckPos+=4; +#endif + for (size_t I=CheckPos;I switch. + FILTER_AUTO, // -mc switch is present. + FILTER_FORCE, // -mc+ switch is present. + FILTER_DISABLE // -mc- switch is present. +}; enum SAVECOPY_MODE { @@ -98,6 +104,7 @@ class RAROptions RAROptions(); void Init(); + uint ExclFileAttr; uint InclFileAttr; @@ -107,30 +114,24 @@ class RAROptions bool InclDir; bool InclAttrSet; - size_t WinSize; - wchar TempPath[NM]; - wchar SFXModule[NM]; + uint64 WinSize; + uint64 MaxWinSize; // Switch -mdx. #ifdef USE_QOPEN QOPEN_MODE QOpenMode; #endif bool ConfigDisabled; // Switch -cfg-. - wchar ExtrPath[NM]; - wchar CommentFile[NM]; RAR_CHARSET CommentCharset; RAR_CHARSET FilelistCharset; RAR_CHARSET ErrlogCharset; RAR_CHARSET RedirectCharset; - wchar ArcPath[NM]; // For -ap. - wchar ExclArcPath[NM]; // For -ep4 switch. bool EncryptHeaders; bool SkipEncrypted; bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. - wchar LogName[NM]; MESSAGE_TYPE MsgStream; SOUND_NOTIFY_MODE Sound; OVERWRITE_MODE Overwrite; @@ -164,6 +165,7 @@ class RAROptions bool SaveSymLinks; bool SaveHardLinks; bool AbsoluteLinks; + bool SkipSymLinks; int Priority; int SleepTime; bool KeepBroken; @@ -195,7 +197,6 @@ class RAROptions bool Test; bool VolumePause; FilterMode FilterModes[MAX_FILTER_TYPES]; - wchar EmailTo[NM]; uint VersionControl; APPENDARCNAME_MODE AppendArcNameToPath; POWER_MODE Shutdown; @@ -204,11 +205,6 @@ class RAROptions EXTTIME_MODE xatime; bool PreserveAtime; - // Read data from stdin and store in archive under a name specified here - // when archiving. Read an archive from stdin if any non-empty string - // is specified here when extracting. - wchar UseStdin[NM]; - uint Threads; // We use it to init hash even if RAR_SMP is not defined. @@ -216,7 +212,6 @@ class RAROptions #ifdef RARDLL - wchar DllDestName[NM]; int DllOpMode; int DllError; LPARAM UserData; @@ -224,5 +219,6 @@ class RAROptions CHANGEVOLPROC ChangeVolProc; PROCESSDATAPROC ProcessDataProc; #endif + }; #endif diff --git a/unrar/os.hpp b/unrar/os.hpp index 4b21e49d..8bfc05cf 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -4,10 +4,6 @@ #define FALSE 0 #define TRUE 1 -#ifdef __EMX__ - #define INCL_BASE -#endif - #if defined(RARDLL) && !defined(SILENT) #define SILENT #endif @@ -15,14 +11,12 @@ #include #include #include +#include // For automatic pointers. -#if defined(_WIN_ALL) || defined(_EMX) +#ifdef _WIN_ALL #define LITTLE_ENDIAN -#define NM 2048 - -#ifdef _WIN_ALL // We got a report that just "#define STRICT" is incompatible with @@ -70,28 +64,23 @@ #include #include +// For WMI requests. +#include +#include +#pragma comment(lib, "wbemuuid.lib") -#endif // _WIN_ALL #include #include #include +#include +#include -#if !defined(_EMX) && !defined(_MSC_VER) - #include +// Use SSE only for x86/x64, not ARM Windows. +#if defined(_M_IX86) || defined(_M_X64) + #define USE_SSE + #define SSE_ALIGNMENT 16 #endif -#ifdef _MSC_VER - #include - #include - - // Use SSE only for x86/x64, not ARM Windows. - #if defined(_M_IX86) || defined(_M_X64) - #define USE_SSE - #define SSE_ALIGNMENT 16 - #endif -#else - #include -#endif // _MSC_VER #include #include @@ -113,7 +102,7 @@ #define SPATHDIVIDER L"\\" -#define CPATHDIVIDER '\\' +#define CPATHDIVIDER L'\\' #define MASKALL L"*" #define READBINARY "rb" @@ -123,25 +112,13 @@ #define WRITEBINARY "wb" #define APPENDTEXT "at" -#if defined(_WIN_ALL) - #ifdef _MSC_VER - #define _stdfunction __cdecl - #define _forceinline __forceinline - #else - #define _stdfunction _USERENTRY - #define _forceinline inline - #endif -#else - #define _stdfunction - #define _forceinline inline -#endif +#define _stdfunction __cdecl +#define _forceinline __forceinline -#endif // defined(_WIN_ALL) || defined(_EMX) +#endif // _WIN_ALL #ifdef _UNIX -#define NM 2048 - #include #include #include @@ -149,7 +126,7 @@ #if defined(__QNXNTO__) #include #endif -#if defined(RAR_SMP) && defined(__APPLE__) +#ifdef _APPLE #include #endif #ifndef SFX_MODULE @@ -172,6 +149,23 @@ #include #include +#ifdef __GNUC__ + #if defined(__i386__) || defined(__x86_64__) + #include + + #define USE_SSE + #define SSE_ALIGNMENT 16 + #endif +#endif + +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRYPTO) +#include +#ifndef _APPLE +#include +#include +#endif +#define USE_NEON // Neon SIMD instructions for AArch64 version. +#endif #ifdef S_IFLNK #define SAVE_LINKS @@ -189,7 +183,7 @@ #define SPATHDIVIDER L"/" -#define CPATHDIVIDER '/' +#define CPATHDIVIDER L'/' #define MASKALL L"*" #define READBINARY "r" @@ -237,8 +231,6 @@ #define SSE_ALIGNMENT 1 #endif -#define safebuf static - // Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN. #if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN @@ -269,8 +261,8 @@ #endif #endif -#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) -// Allow not aligned integer access, increases speed in some operations. +#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +// Allow unaligned integer access, increases speed in some operations. #define ALLOW_MISALIGNED #endif diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 6d3f56c0..17e2480c 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -9,6 +9,21 @@ wchar* PointToName(const wchar *Path) } +std::wstring PointToName(const std::wstring &Path) +{ + return std::wstring(Path.substr(GetNamePos(Path))); +} + + +size_t GetNamePos(const std::wstring &Path) +{ + for (int I=(int)Path.size()-1;I>=0;I--) + if (IsPathDiv(Path[I])) + return I+1; + return IsDriveLetter(Path) ? 2 : 0; +} + + wchar* PointToLastChar(const wchar *Path) { size_t Length=wcslen(Path); @@ -16,94 +31,95 @@ wchar* PointToLastChar(const wchar *Path) } -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize) +wchar GetLastChar(const std::wstring &Path) +{ + return Path.empty() ? 0:Path.back(); +} + + +size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath) { - const wchar *DestPtr=SrcPath; + const std::wstring &S=*SrcPath; // To avoid *SrcPath[] everywhere. + size_t DestPos=0; - // Prevent \..\ in any part of path string. - for (const wchar *s=DestPtr;*s!=0;s++) - if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) - DestPtr=s+4; + // Prevent \..\ in any part of path string and \.. at the end of string + for (size_t I=0;I:\ and any sequence of . and \ in the beginning of path string. - while (*DestPtr!=0) + while (DestPos='A' && Letter<='Z' && IsDriveDiv(Path[1]); } -int GetPathDisk(const wchar *Path) +int GetPathDisk(const std::wstring &Path) { if (IsDriveLetter(Path)) - return etoupperw(*Path)-'A'; + return etoupperw(Path[0])-'A'; else return -1; } -void AddEndSlash(wchar *Path,size_t MaxLength) +void AddEndSlash(std::wstring &Path) { - size_t Length=wcslen(Path); - if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+12) + AddEndSlash(OutName); + OutName+=Name; + Pathname=OutName; } -// Returns file path including the trailing path separator symbol. -void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength) +// Returns the file path including the trailing path separator symbol. +// It is allowed for both parameters to point to the same string. +void GetPathWithSep(const std::wstring &FullName,std::wstring &Path) { - if (MaxLength==0) - return; - size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName)); - wcsncpy(Path,FullName,PathLength); - Path[PathLength]=0; + if (std::addressof(FullName)!=std::addressof(Path)) + Path=FullName; + Path.erase(GetNamePos(FullName)); } -// Removes name and returns file path without the trailing -// path separator symbol. -void RemoveNameFromPath(wchar *Path) +// Removes name and returns file path without the trailing path separator. +// But for names like d:\name return d:\ with trailing path separator. +void RemoveNameFromPath(std::wstring &Path) { - wchar *Name=PointToName(Path); - if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) - Name--; - *Name=0; + auto NamePos=GetNamePos(Path); + if (NamePos>=2 && (!IsDriveDiv(Path[1]) || NamePos>=4)) + NamePos--; + Path.erase(NamePos); } #if defined(_WIN_ALL) && !defined(SFX_MODULE) -bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) +bool GetAppDataPath(std::wstring &Path,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; - *Path=0; + Path.clear(); bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && - SHGetPathFromIDList(ppidl,Path) && *Path!=0) + SHGetPathStrFromIDList(ppidl,Path) && !Path.empty()) { - AddEndSlash(Path,MaxSize); - wcsncatz(Path,L"WinRAR",MaxSize); + AddEndSlash(Path); + Path+=L"WinRAR"; Success=FileExist(Path); if (!Success && Create) - Success=MakeDir(Path,false,0)==MKDIR_SUCCESS; + Success=CreateDir(Path); } g_pMalloc->Free(ppidl); return Success; @@ -243,24 +278,41 @@ bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) #endif +#if defined(_WIN_ALL) +bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path) +{ + std::vector Buf(MAX_PATH); + bool Success=SHGetPathFromIDList(pidl,Buf.data())!=FALSE; + Path=Buf.data(); + return Success; +} +#endif + + #if defined(_WIN_ALL) && !defined(SFX_MODULE) -void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) +void GetRarDataPath(std::wstring &Path,bool Create) { - *Path=0; + Path.clear(); HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { - DWORD DataSize=(DWORD)MaxSize,Type; - RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); - RegCloseKey(hKey); + DWORD DataSize; + LSTATUS Code=RegQueryValueEx(hKey,L"AppData",NULL,NULL,NULL,&DataSize); + if (Code==ERROR_SUCCESS) + { + std::vector PathBuf(DataSize/sizeof(wchar)); + RegQueryValueEx(hKey,L"AppData",0,NULL,(BYTE *)PathBuf.data(),&DataSize); + Path=PathBuf.data(); + RegCloseKey(hKey); + } } - if (*Path==0 || !FileExist(Path)) - if (!GetAppDataPath(Path,MaxSize,Create)) + if (Path.empty() || !FileExist(Path)) + if (!GetAppDataPath(Path,Create)) { - GetModuleFileName(NULL,Path,(DWORD)MaxSize); + Path=GetModuleFileStr(); RemoveNameFromPath(Path); } } @@ -268,7 +320,7 @@ void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) #ifndef SFX_MODULE -bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) +bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create) { #ifdef _UNIX static const wchar *ConfPath[]={ @@ -278,24 +330,24 @@ bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) { char *EnvStr=getenv("HOME"); if (EnvStr!=NULL) - CharToWide(EnvStr,Path,MaxSize); + CharToWide(EnvStr,Path); else - wcsncpyz(Path,ConfPath[0],MaxSize); + Path=ConfPath[0]; return true; } Number--; if (Number>=ASIZE(ConfPath)) return false; - wcsncpyz(Path,ConfPath[Number], MaxSize); + Path=ConfPath[Number]; return true; #elif defined(_WIN_ALL) if (Number>1) return false; if (Number==0) - GetRarDataPath(Path,MaxSize,Create); + GetRarDataPath(Path,Create); else { - GetModuleFileName(NULL,Path,(DWORD)MaxSize); + Path=GetModuleFileStr(); RemoveNameFromPath(Path); } return true; @@ -307,13 +359,15 @@ bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) #ifndef SFX_MODULE -void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create) +void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create) { - *FullName=0; - for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++) + FullName.clear(); + for (uint I=0;;I++) { - AddEndSlash(FullName,MaxSize); - wcsncatz(FullName,Name,MaxSize); + std::wstring ConfPath; + if (!EnumConfigPaths(I,ConfPath,Create)) + break; + MakeName(ConfPath,Name,FullName); if (!CheckExist || WildFileExist(FullName)) break; } @@ -321,112 +375,107 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx #endif -// Returns a pointer to rightmost digit of volume number or to beginning +// Returns the position to rightmost digit of volume number or beginning // of file name if numeric part is missing. -wchar* GetVolNumPart(const wchar *ArcName) +size_t GetVolNumPos(const std::wstring &ArcName) { // We do not want to increment any characters in path component. - ArcName=PointToName(ArcName); + size_t NamePos=GetNamePos(ArcName); - if (*ArcName==0) - return (wchar *)ArcName; + if (NamePos==ArcName.size()) + return NamePos; // Pointing to last name character. - const wchar *ChPtr=ArcName+wcslen(ArcName)-1; + size_t Pos=ArcName.size()-1; // Skipping the archive extension. - while (!IsDigit(*ChPtr) && ChPtr>ArcName) - ChPtr--; + while (!IsDigit(ArcName[Pos]) && Pos>NamePos) + Pos--; // Skipping the numeric part of name. - const wchar *NumPtr=ChPtr; - while (IsDigit(*NumPtr) && NumPtr>ArcName) - NumPtr--; + size_t NumPos=Pos; + while (IsDigit(ArcName[NumPos]) && NumPos>NamePos) + NumPos--; // Searching for first numeric part in names like name.part##of##.rar. // Stop search on the first dot. - while (NumPtr>ArcName && *NumPtr!='.') + while (NumPos>NamePos && ArcName[NumPos]!='.') { - if (IsDigit(*NumPtr)) + if (IsDigit(ArcName[NumPos])) { // Validate the first numeric part only if it has a dot somewhere // before it. - const wchar *Dot=wcschr(ArcName,'.'); - if (Dot!=NULL && Dot|\"")==NULL; + return !Name.empty() && Name.find_first_of(L"?*<>|\"")==std::wstring::npos; } - - -void MakeNameUsable(wchar *Name,bool Extended) +void MakeNameUsable(std::wstring &Name,bool Extended) { - for (wchar *s=Name;*s!=0;s++) + for (size_t I=0;I|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) - *s='_'; + if (wcschr(Extended ? L"?*<>|\"":L"?*",Name[I])!=NULL || + Extended && (uint)Name[I]<32) + Name[I]='_'; #ifdef _UNIX // We were asked to apply Windows-like conversion in Linux in case // files are unpacked to Windows share. This code is invoked only @@ -478,26 +526,20 @@ void MakeNameUsable(wchar *Name,bool Extended) { // Windows shares in Unix do not allow the drive letter, // so unlike Windows version, we check all characters here. - if (*s==':') - *s='_'; + if (Name[I]==':') + Name[I]='_'; // No spaces or dots before the path separator are allowed on Windows - // shares. But they are allowed and automtically removed at the end of + // shares. But they are allowed and automatically removed at the end of // file or folder name, so it is useless to replace them here. // Since such files or folders are created successfully, a supposed // conversion here would never be invoked. - if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) - *s='_'; + if ((Name[I]==' ' || Name[I]=='.') && IsPathDiv(Name[I+1])) + Name[I]='_'; } #else - if (s-Name>1 && *s==':') - *s='_'; -#if 0 // We already can create such files. - // Remove ' ' and '.' before path separator, but allow .\ and ..\. - if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name && - !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2])))) - *s='_'; -#endif + if (I>1 && Name[I]==':') + Name[I]='_'; #endif } } @@ -512,20 +554,38 @@ void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) } -void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength) +void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength) { size_t Copied=0; for (;Copied0) - *Dest=0; + Dest.clear(); return; } #ifdef _WIN_ALL { - wchar FullName[NM],*NamePtr; - DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr); - if (Code==0 || Code>ASIZE(FullName)) + DWORD Code=GetFullPathName(Src.c_str(),0,NULL,NULL); // Get the buffer size. + if (Code!=0) { - wchar LongName[NM]; - if (GetWinLongPath(Src,LongName,ASIZE(LongName))) - Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr); + std::vector FullName(Code); + Code=GetFullPathName(Src.c_str(),(DWORD)FullName.size(),FullName.data(),NULL); + + if (Code>0 && Code<=FullName.size()) + { + Dest=FullName.data(); + return; + } } - if (Code!=0 && Code FullName(Code); + Code=GetFullPathName(LongName.c_str(),(DWORD)FullName.size(),FullName.data(),NULL); + + if (Code>0 && Code<=FullName.size()) + { + Dest=FullName.data(); + return; + } + } + } + if (Src!=Dest) + Dest=Src; // Copy source to destination in case of failure. } #elif defined(_UNIX) if (IsFullPath(Src)) - *Dest=0; + Dest.clear(); else { - char CurDirA[NM]; - if (getcwd(CurDirA,ASIZE(CurDirA))==NULL) - *CurDirA=0; - CharToWide(CurDirA,Dest,MaxSize); - AddEndSlash(Dest,MaxSize); + std::vector CurDirA(MAXPATHSIZE); + if (getcwd(CurDirA.data(),CurDirA.size())==NULL) + CurDirA[0]=0; + CharToWide(CurDirA.data(),Dest); + AddEndSlash(Dest); } - wcsncatz(Dest,Src,MaxSize); + Dest+=Src; #else - wcsncpyz(Dest,Src,MaxSize); + Dest=Src; #endif } -bool IsFullPath(const wchar *Path) +bool IsFullPath(const std::wstring &Path) { -/* - wchar PathOnly[NM]; - GetFilePath(Path,PathOnly,ASIZE(PathOnly)); - if (IsWildcard(PathOnly)) - return true; -*/ -#if defined(_WIN_ALL) || defined(_EMX) - return Path[0]=='\\' && Path[1]=='\\' || IsDriveLetter(Path) && IsPathDiv(Path[2]); +#ifdef _WIN_ALL + return Path.size()>=2 && Path[0]=='\\' && Path[1]=='\\' || + Path.size()>=3 && IsDriveLetter(Path) && IsPathDiv(Path[2]); #else - return IsPathDiv(Path[0]); + return Path.size()>=1 && IsPathDiv(Path[0]); #endif } -bool IsFullRootPath(const wchar *Path) +bool IsFullRootPath(const std::wstring &Path) { return IsFullPath(Path) || IsPathDiv(Path[0]); } -void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize) +// Both source and destination can point to the same string. +void GetPathRoot(const std::wstring &Path,std::wstring &Root) { - *Root=0; if (IsDriveLetter(Path)) - swprintf(Root,MaxSize,L"%c:\\",*Path); + Root=Path.substr(0,2) + L"\\"; else if (Path[0]=='\\' && Path[1]=='\\') { - const wchar *Slash=wcschr(Path+2,'\\'); - if (Slash!=NULL) + size_t Slash=Path.find('\\',2); + if (Slash!=std::wstring::npos) { size_t Length; - if ((Slash=wcschr(Slash+1,'\\'))!=NULL) - Length=Slash-Path+1; + if ((Slash=Path.find('\\',Slash+1))!=std::wstring::npos) + Length=Slash+1; else - Length=wcslen(Path); - if (Length>=MaxSize) - Length=0; - wcsncpy(Root,Path,Length); - Root[Length]=0; + Length=Path.size(); + Root=Path.substr(0,Length); } } + else + Root.clear(); } -int ParseVersionFileName(wchar *Name,bool Truncate) +int ParseVersionFileName(std::wstring &Name,bool Truncate) { int Version=0; - wchar *VerText=wcsrchr(Name,';'); - if (VerText!=NULL) + auto VerPos=Name.rfind(';'); + if (VerPos!=std::wstring::npos && VerPos+1FirstName;ChPtr--) - if (IsDigit(*ChPtr)) + for (size_t Pos=GetVolNumPos(Name);Pos>0;Pos--) + if (IsDigit(Name[Pos])) { - *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. + Name[Pos]=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else - if (N=='0') + if (N=='0') // If we already set the rightmost '1' before. { - VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. + VolNumStart=Pos+1; // Store the position of leftmost digit in volume number. break; } } else { // Old volume numbering scheme. Just set the extension to ".rar". - SetExt(FirstName,L"rar",MaxSize); - VolNumStart=GetExt(FirstName); + SetExt(Name,L"rar"); + VolNumStart=GetExtPos(Name); } - if (!FileExist(FirstName)) + if (!FileExist(Name)) { // If the first volume, which name we just generated, does not exist, // check if volume with same name and any other extension is available. // It can help in case of *.exe or *.sfx first volume. - wchar Mask[NM]; - wcsncpyz(Mask,FirstName,ASIZE(Mask)); - SetExt(Mask,L"*",ASIZE(Mask)); + std::wstring Mask=Name; + SetExt(Mask,L"*"); FindFile Find; Find.SetMask(Mask); FindData FD; @@ -689,32 +778,33 @@ wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,b Archive Arc; if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { - wcsncpyz(FirstName,FD.Name,MaxSize); + Name=FD.Name; break; } } } + FirstName=Name; return VolNumStart; } #endif #ifndef SFX_MODULE -static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent) +static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,uint ArcNumber,bool &ArcNumPresent) { + size_t Pos=0; bool Prefix=false; - if (*GenerateMask=='+') + if (GenerateMask[0]=='+') { Prefix=true; // Add the time string before the archive name. - GenerateMask++; // Skip '+' in the beginning of time mask. + Pos++; // Skip '+' in the beginning of time mask. } - wchar Mask[MAX_GENERATE_MASK]; - wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask)); + std::wstring Mask=!GenerateMask.empty() ? GenerateMask:L"yyyymmddhhmmss"; bool QuoteMode=false; uint MAsMinutes=0; // By default we treat 'M' as months. - for (uint I=0;Mask[I]!=0;I++) + for (uint I=0;I=4) CurWeek++; - char Field[10][6]; + char Field[10][11]; sprintf(Field[0],"%04u",rlt.Year); sprintf(Field[1],"%02u",rlt.Month); @@ -799,11 +886,10 @@ static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,u const wchar *MaskChars=L"YMDHISWAEN"; // How many times every modifier character was encountered in the mask. - int CField[sizeof(Field)/sizeof(Field[0])]; + int CField[sizeof(Field)/sizeof(Field[0])]{}; - memset(CField,0,sizeof(CField)); QuoteMode=false; - for (uint I=0;Mask[I]!=0;I++) + for (uint I=0;I0) - DestW[DestSize-1]=0; - - return DestW; -} - - #ifdef _WIN_ALL // We should return 'true' even if resulting path is shorter than MAX_PATH, // because we can also use this function to open files with non-standard // characters, even if their path length is normal. -bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize) +bool GetWinLongPath(const std::wstring &Src,std::wstring &Dest) { - if (*Src==0) + if (Src.empty()) return false; - const wchar *Prefix=L"\\\\?\\"; - const size_t PrefixLength=4; - bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]); - size_t SrcLength=wcslen(Src); + const std::wstring Prefix=L"\\\\?\\"; + + bool FullPath=Src.size()>=3 && IsDriveLetter(Src) && IsPathDiv(Src[2]); if (IsFullPath(Src)) // Paths in d:\path\name format. { if (IsDriveLetter(Src)) { - if (MaxSize<=PrefixLength+SrcLength) - return false; - wcsncpyz(Dest,Prefix,MaxSize); - wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path". + Dest=Prefix+Src; // "\\?\D:\very long path". return true; } else - if (Src[0]=='\\' && Src[1]=='\\') + if (Src.size()>2 && Src[0]=='\\' && Src[1]=='\\') { - if (MaxSize<=PrefixLength+SrcLength+2) - return false; - wcsncpyz(Dest,Prefix,MaxSize); - wcsncatz(Dest,L"UNC",MaxSize); - wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share". + Dest=Prefix+L"UNC"+Src.substr(1); // "\\?\UNC\server\share". return true; } - // We may be here only if we modify IsFullPath in the future. + // We can be here only if modify IsFullPath() in the future. return false; } else { - wchar CurDir[NM]; - DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir); - if (DirCode==0 || DirCode>ASIZE(CurDir)-1) + std::wstring CurDir; + if (!GetCurDir(CurDir)) return false; if (IsPathDiv(Src[0])) // Paths in \path\name format. { - if (MaxSize<=PrefixLength+SrcLength+2) - return false; - wcsncpyz(Dest,Prefix,MaxSize); - CurDir[2]=0; - wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'. - wcsncatz(Dest,Src,MaxSize); + Dest=Prefix+CurDir[0]+L':'+Src; // Copy drive letter 'd:'. return true; } else // Paths in path\name format. { - AddEndSlash(CurDir,ASIZE(CurDir)); - if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength) - return false; - wcsncpyz(Dest,Prefix,MaxSize); - wcsncatz(Dest,CurDir,MaxSize); + Dest=Prefix+CurDir; + AddEndSlash(Dest); + size_t Pos=0; if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname. - Src+=2; + Pos=2; - wcsncatz(Dest,Src,MaxSize); + Dest+=Src.substr(Pos); return true; } } @@ -1008,46 +1056,40 @@ bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. -void ConvertToPrecomposed(wchar *Name,size_t NameSize) +void ConvertToPrecomposed(std::wstring &Name) { - wchar FileName[NM]; - if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP. - FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0) - { - FileName[ASIZE(FileName)-1]=0; - wcsncpyz(Name,FileName,NameSize); - } + if (WinNT() FileName(Size); + if (FoldString(MAP_PRECOMPOSED,Name.c_str(),-1,FileName.data(),(int)FileName.size())!=0) + Name=FileName.data(); } -void MakeNameCompatible(wchar *Name,size_t MaxSize) +void MakeNameCompatible(std::wstring &Name) { // Remove trailing spaces and dots in file name and in dir names in path. - int Src=0,Dest=0; - while (true) - { - if (IsPathDiv(Name[Src]) || Name[Src]==0) - for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--) + for (int I=0;I<(int)Name.size();I++) + if (I+1==Name.size() || IsPathDiv(Name[I+1])) + while (I>=0 && (Name[I]=='.' || Name[I]==' ')) { // Permit path1/./path2 and ../path1 paths. - if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) + if (Name[I]=='.' && I>0 && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) break; - Dest--; + Name.erase(I,1); + I--; } - Name[Dest]=Name[Src]; - if (Name[Src]==0) - break; - Src++; - Dest++; - } // Rename reserved device names, such as aux.txt to _aux.txt. // We check them in path components too, where they are also prohibited. - for (uint I=0;Name[I]!=0;I++) + for (size_t I=0;I0 && IsPathDiv(Name[I-1])) { static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"}; - wchar *s=Name+I; + const wchar *s=&Name[I]; bool MatchFound=false; for (uint J=0;JI+1) // I+1, because we do not move the trailing 0. - memmove(s+1,s,(MaxSize-I-1)*sizeof(*s)); - *s='_'; + std::wstring OrigName=Name; + Name.insert(I,1,'_'); #ifndef SFX_MODULE uiMsg(UIMSG_CORRECTINGNAME,nullptr); uiMsg(UIERROR_RENAMING,nullptr,OrigName,Name); @@ -1084,3 +1123,54 @@ void MakeNameCompatible(wchar *Name,size_t MaxSize) #endif + + +#ifdef _WIN_ALL +std::wstring GetModuleFileStr() +{ + HMODULE hModule=nullptr; + + std::vector Path(256); + while (Path.size()<=MAXPATHSIZE) + { + if (GetModuleFileName(hModule,Path.data(),(DWORD)Path.size()) Buf(BufSize); + DWORD Code=GetCurrentDirectory((DWORD)Buf.size(),Buf.data()); + Dir=Buf.data(); + return Code!=0; +} +#endif + + diff --git a/unrar/pathfn.hpp b/unrar/pathfn.hpp index 62cae0ad..57dab5a5 100644 --- a/unrar/pathfn.hpp +++ b/unrar/pathfn.hpp @@ -2,39 +2,52 @@ #define _RAR_PATHFN_ wchar* PointToName(const wchar *Path); +std::wstring PointToName(const std::wstring &Path); +size_t GetNamePos(const std::wstring &Path); wchar* PointToLastChar(const wchar *Path); -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize); -void SetName(wchar *FullName,const wchar *Name,size_t MaxSize); -void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize); -void SetSFXExt(wchar *SFXName,size_t MaxSize); +wchar GetLastChar(const std::wstring &Path); +size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath); +void SetName(std::wstring &FullName,const std::wstring &Name); +void SetExt(std::wstring &Name,std::wstring NewExt); +void RemoveExt(std::wstring &Name); +void SetSFXExt(std::wstring &SFXName); wchar *GetExt(const wchar *Name); -bool CmpExt(const wchar *Name,const wchar *Ext); -bool IsWildcard(const wchar *Str); +std::wstring GetExt(const std::wstring &Name); +std::wstring::size_type GetExtPos(const std::wstring &Name); +bool CmpExt(const std::wstring &Name,const std::wstring &Ext); +bool IsWildcard(const std::wstring &Str); bool IsPathDiv(int Ch); bool IsDriveDiv(int Ch); -bool IsDriveLetter(const wchar *Path); -int GetPathDisk(const wchar *Path); -void AddEndSlash(wchar *Path,size_t MaxLength); -void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize); -void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength); -void RemoveNameFromPath(wchar *Path); +bool IsDriveLetter(const std::wstring &Path); +int GetPathDisk(const std::wstring &Path); +void AddEndSlash(std::wstring &Path); +void MakeName(const std::wstring &Path,const std::wstring &Name,std::wstring &Pathname); +void GetPathWithSep(const std::wstring &FullName,std::wstring &Path); +void RemoveNameFromPath(std::wstring &Path); #if defined(_WIN_ALL) && !defined(SFX_MODULE) -bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create); -void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create); +bool GetAppDataPath(std::wstring &Path,bool Create); +void GetRarDataPath(std::wstring &Path,bool Create); +#endif +#ifdef _WIN_ALL +bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path); #endif #ifndef SFX_MODULE -bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create); -void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create); +bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create); +void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create); #endif -wchar* GetVolNumPart(const wchar *ArcName); -void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); -bool IsNameUsable(const wchar *Name); -void MakeNameUsable(wchar *Name,bool Extended); +size_t GetVolNumPos(const std::wstring &ArcName); +void NextVolumeName(std::wstring &ArcName,bool OldNumbering); +bool IsNameUsable(const std::wstring &Name); +void MakeNameUsable(std::wstring &Name,bool Extended); void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); -void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength); void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength); +void UnixSlashToDos(const std::string &SrcName,std::string &DestName); +void UnixSlashToDos(const std::wstring &SrcName,std::wstring &DestName); +void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength); void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength); +void DosSlashToUnix(const std::string &SrcName,std::string &DestName); +void DosSlashToUnix(const std::wstring &SrcName,std::wstring &DestName); inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength) { @@ -45,6 +58,15 @@ inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength) #endif } +inline void SlashToNative(const std::string &SrcName,std::string &DestName) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName); +#else + DosSlashToUnix(SrcName,DestName); +#endif +} + inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength) { #ifdef _WIN_ALL @@ -54,22 +76,44 @@ inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength) #endif } -void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize); -bool IsFullPath(const wchar *Path); -bool IsFullRootPath(const wchar *Path); -void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize); -int ParseVersionFileName(wchar *Name,bool Truncate); -wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering); -wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize); +inline void SlashToNative(const std::wstring &SrcName,std::wstring &DestName) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName); +#else + DosSlashToUnix(SrcName,DestName); +#endif +} + +void ConvertNameToFull(const std::wstring &Src,std::wstring &Dest); +bool IsFullPath(const std::wstring &Path); +bool IsFullRootPath(const std::wstring &Path); +void GetPathRoot(const std::wstring &Path,std::wstring &Root); +int ParseVersionFileName(std::wstring &Name,bool Truncate); +size_t VolNameToFirstName(const std::wstring &VolName,std::wstring &FirstName,bool NewNumbering); #ifndef SFX_MODULE -void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving); +void GenerateArchiveName(std::wstring &ArcName,const std::wstring &GenerateMask,bool Archiving); +#endif + +#ifdef _WIN_ALL +bool GetWinLongPath(const std::wstring &Src,std::wstring &Dest); +void ConvertToPrecomposed(std::wstring &Name); +void MakeNameCompatible(std::wstring &Name); +#endif + + +#ifdef _WIN_ALL +std::wstring GetModuleFileStr(); +std::wstring GetProgramFile(const std::wstring &Name); +#endif + +#if defined(_WIN_ALL) +bool SetCurDir(const std::wstring &Dir); #endif #ifdef _WIN_ALL -bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize); -void ConvertToPrecomposed(wchar *Name,size_t NameSize); -void MakeNameCompatible(wchar *Name,size_t MaxSize); +bool GetCurDir(std::wstring &Dir); #endif diff --git a/unrar/qopen.cpp b/unrar/qopen.cpp index d906d06b..e4ae8a9d 100644 --- a/unrar/qopen.cpp +++ b/unrar/qopen.cpp @@ -114,7 +114,7 @@ void QuickOpen::Load(uint64 BlockPos) RawDataPos=0; ReadBufSize=0; ReadBufPos=0; - LastReadHeader.Reset(); + LastReadHeader.clear(); LastReadHeaderPos=0; ReadBuffer(); @@ -126,7 +126,7 @@ bool QuickOpen::Read(void *Data,size_t Size,size_t &Result) if (!Loaded) return false; // Find next suitable cached block. - while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos) + while (LastReadHeaderPos+LastReadHeader.size()<=SeekPos) if (!ReadNext()) break; if (!Loaded) @@ -138,9 +138,9 @@ bool QuickOpen::Read(void *Data,size_t Size,size_t &Result) return false; } - if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size()) + if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.size()) { - memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size); + memcpy(Data,&LastReadHeader[size_t(SeekPos-LastReadHeaderPos)],Size); Result=Size; SeekPos+=Size; UnsyncSeekPos=true; @@ -292,8 +292,8 @@ bool QuickOpen::ReadNext() size_t HeaderSize=(size_t)Raw.GetV(); if (HeaderSize>MAX_HEADER_SIZE_RAR5) return false; - LastReadHeader.Alloc(HeaderSize); - Raw.GetB(&LastReadHeader[0],HeaderSize); + LastReadHeader.resize(HeaderSize); + Raw.GetB(LastReadHeader.data(),HeaderSize); // Calculate the absolute position as offset from quick open service header. LastReadHeaderPos=QOHeaderPos-Offset; return true; diff --git a/unrar/qopen.hpp b/unrar/qopen.hpp index d745cea8..7adca4b5 100644 --- a/unrar/qopen.hpp +++ b/unrar/qopen.hpp @@ -43,7 +43,7 @@ class QuickOpen uint64 RawDataPos; // Current read position in QO data. size_t ReadBufSize; // Size of Buf data currently read from QO. size_t ReadBufPos; // Current read position in Buf data. - Array LastReadHeader; + std::vector LastReadHeader; uint64 LastReadHeaderPos; uint64 SeekPos; bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer. diff --git a/unrar/rar.cpp b/unrar/rar.cpp index 34b4b278..075e768f 100644 --- a/unrar/rar.cpp +++ b/unrar/rar.cpp @@ -12,11 +12,11 @@ int main(int argc, char *argv[]) ErrHandler.SetSignalHandlers(true); #ifdef SFX_MODULE - wchar ModuleName[NM]; + std::wstring ModuleName; #ifdef _WIN_ALL - GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName)); + ModuleName=GetModuleFileStr(); #else - CharToWide(argv[0],ModuleName,ASIZE(ModuleName)); + CharToWide(argv[0],ModuleName); #endif #endif @@ -35,9 +35,10 @@ int main(int argc, char *argv[]) try { - CommandData *Cmd=new CommandData; + // Use std::unique_ptr to free Cmd in case of exception. + std::unique_ptr Cmd(new CommandData); #ifdef SFX_MODULE - wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command)); + Cmd->Command=L"X"; char *Switch=argc>1 ? argv[1]:NULL; if (Switch!=NULL && Cmd->IsSwitch(Switch[0])) { @@ -68,7 +69,7 @@ int main(int argc, char *argv[]) #if defined(_WIN_ALL) && !defined(SFX_MODULE) ShutdownOnClose=Cmd->Shutdown; - if (ShutdownOnClose) + if (ShutdownOnClose!=POWERMODE_KEEP) ShutdownCheckAnother(true); #endif @@ -78,7 +79,6 @@ int main(int argc, char *argv[]) Cmd->OutTitle(); Cmd->ProcessCommand(); - delete Cmd; } catch (RAR_EXIT ErrCode) { @@ -100,6 +100,7 @@ int main(int argc, char *argv[]) Shutdown(ShutdownOnClose); #endif ErrHandler.MainExit=true; + CloseLogOptions(); return ErrHandler.GetErrorCode(); } #endif diff --git a/unrar/rar.hpp b/unrar/rar.hpp index 67edb673..633ea0e7 100644 --- a/unrar/rar.hpp +++ b/unrar/rar.hpp @@ -16,7 +16,6 @@ #include "unicode.hpp" #include "errhnd.hpp" #include "secpassword.hpp" -#include "array.hpp" #include "strlist.hpp" #include "timefn.hpp" #include "sha1.hpp" diff --git a/unrar/rardefs.hpp b/unrar/rardefs.hpp index 6858d39c..986aaaf0 100644 --- a/unrar/rardefs.hpp +++ b/unrar/rardefs.hpp @@ -17,6 +17,10 @@ #define MAXPASSWORD 512 #define MAXPASSWORD_RAR 128 +// Set some arbitrary sensible limit to maximum path length to prevent +// the excessive memory allocation for dynamically allocated strings. +#define MAXPATHSIZE 0x10000 + #define MAXSFXSIZE 0x200000 #define MAXCMTSIZE 0x40000 @@ -24,6 +28,10 @@ #define DefSFXName L"default.sfx" #define DefSortListName L"rarfiles.lst" +// Maximum dictionary allowed by compression. Can be less than +// maximum dictionary supported by decompression. +#define PACK_MAX_DICT 0x1000000000ULL // 64 GB. + #ifndef SFX_MODULE #define USE_QOPEN diff --git a/unrar/raros.hpp b/unrar/raros.hpp index 4f4f2ae7..b701637a 100644 --- a/unrar/raros.hpp +++ b/unrar/raros.hpp @@ -1,15 +1,6 @@ #ifndef _RAR_RAROS_ #define _RAR_RAROS_ -#ifdef __EMX__ - #define _EMX -#endif - -#ifdef __DJGPP__ - #define _DJGPP - #define _EMX -#endif - #if defined(__WIN32__) || defined(_WIN32) #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop. #ifdef _M_X64 @@ -29,7 +20,7 @@ #define _APPLE #endif -#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE) +#if !defined(_WIN_ALL) && !defined(_UNIX) #define _UNIX #endif diff --git a/unrar/rawint.hpp b/unrar/rawint.hpp index c8cd86fc..5c77c54f 100644 --- a/unrar/rawint.hpp +++ b/unrar/rawint.hpp @@ -87,7 +87,21 @@ inline uint32 RawGetBE4(const byte *m) #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) return __builtin_bswap32(*(uint32 *)m); #else - return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; + return uint32(m[0])<<24 | uint32(m[1])<<16 | uint32(m[2])<<8 | m[3]; +#endif +} + + +// Load 8 big endian bytes from memory and return uint64. +inline uint64 RawGetBE8(const byte *m) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + return _byteswap_uint64(*(uint64 *)m); +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) + return __builtin_bswap64(*(uint64 *)m); +#else + return uint64(m[0])<<56 | uint64(m[1])<<48 | uint64(m[2])<<40 | uint64(m[3])<<32 | + uint64(m[4])<<24 | uint64(m[5])<<16 | uint64(m[6])<<8 | m[7]; #endif } @@ -108,6 +122,26 @@ inline void RawPutBE4(uint32 i,byte *mem) } +// Save integer to memory as big endian. +inline void RawPutBE8(uint64 i,byte *mem) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + *(uint64*)mem = _byteswap_uint64(i); +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) + *(uint64*)mem = __builtin_bswap64(i); +#else + mem[0]=byte(i>>56); + mem[1]=byte(i>>48); + mem[2]=byte(i>>40); + mem[3]=byte(i>>32); + mem[4]=byte(i>>24); + mem[5]=byte(i>>16); + mem[6]=byte(i>>8); + mem[7]=byte(i); +#endif +} + + inline uint32 ByteSwap32(uint32 i) { #ifdef _MSC_VER @@ -119,4 +153,29 @@ inline uint32 ByteSwap32(uint32 i) #endif } + + + +inline bool IsPow2(uint64 n) // Check if 'n' is power of 2. +{ + return (n & (n-1))==0; +} + + +inline uint64 GetGreaterOrEqualPow2(uint64 n) +{ + uint64 p=1; + while (pRead(&Data[FullSize],AlignedReadSize); Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize); DataSize+=ReadSize==0 ? 0:Size; @@ -55,7 +55,7 @@ size_t RawRead::Read(size_t Size) #endif if (Size!=0) { - Data.Add(Size); + Data.resize(Data.size()+Size); ReadSize=SrcFile->Read(&Data[DataSize],Size); DataSize+=ReadSize; } @@ -67,7 +67,7 @@ void RawRead::Read(byte *SrcData,size_t Size) { if (Size!=0) { - Data.Add(Size); + Data.resize(Data.size()+Size); memcpy(&Data[DataSize],SrcData,Size); DataSize+=Size; } diff --git a/unrar/rawread.hpp b/unrar/rawread.hpp index b3198987..777867a2 100644 --- a/unrar/rawread.hpp +++ b/unrar/rawread.hpp @@ -4,7 +4,7 @@ class RawRead { private: - Array Data; + std::vector Data; File *SrcFile; size_t DataSize; size_t ReadPos; @@ -27,7 +27,7 @@ class RawRead uint GetCRC50(); byte* GetDataPtr() {return &Data[0];} size_t Size() {return DataSize;} - size_t PaddedSize() {return Data.Size()-DataSize;} + size_t PaddedSize() {return Data.size()-DataSize;} size_t DataLeft() {return DataSize-ReadPos;} size_t GetPos() {return ReadPos;} void SetPos(size_t Pos) {ReadPos=Pos;} diff --git a/unrar/rdwrfn.cpp b/unrar/rdwrfn.cpp index fa713760..1f006883 100644 --- a/unrar/rdwrfn.cpp +++ b/unrar/rdwrfn.cpp @@ -259,15 +259,6 @@ void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, } -#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) -void ComprDataIO::SetAV15Encryption() -{ - Decryption=true; - Decrypt->SetAV15Encryption(); -} -#endif - - #if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetCmt13Encryption() { @@ -299,11 +290,15 @@ void ComprDataIO::AdjustTotalArcSize(Archive *Arc) // packed size to beginning of these blocks. Earlier we already calculated // the total size based on entire archive sizes. We also set LastArcSize // to start of first trailing block, to add it later to ProcessedArcSize. - int64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; - if (Arc->MainHead.QOpenOffset!=0) // QO is always preceding RR record. + uint64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; + // QO is always preceding RR record. + // Also we check QO and RR to be less than archive length to prevent + // negative "ArcLength-LastArcSize" and possible signed integer overflow + // when calculating TotalArcSize. + if (Arc->MainHead.QOpenOffset>0 && Arc->MainHead.QOpenOffsetMainHead.QOpenOffset; else - if (Arc->MainHead.RROffset!=0) + if (Arc->MainHead.RROffset>0 && Arc->MainHead.RROffsetMainHead.RROffset; else { diff --git a/unrar/rdwrfn.hpp b/unrar/rdwrfn.hpp index 3060a0ff..eda9f545 100644 --- a/unrar/rdwrfn.hpp +++ b/unrar/rdwrfn.hpp @@ -72,7 +72,6 @@ class ComprDataIO void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); - void SetAV15Encryption(); void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;} diff --git a/unrar/recvol.cpp b/unrar/recvol.cpp index b1782071..426d9cee 100644 --- a/unrar/recvol.cpp +++ b/unrar/recvol.cpp @@ -1,11 +1,12 @@ #include "rar.hpp" + #include "recvol3.cpp" #include "recvol5.cpp" -bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent) +bool RecVolumesRestore(CommandData *Cmd,const std::wstring &Name,bool Silent) { Archive Arc(Cmd); if (!Arc.Open(Name)) @@ -42,24 +43,20 @@ bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent) } -void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const std::wstring &Name) { - wchar RevName[NM]; - *RevName=0; - if (Arc!=NULL) + std::wstring RevName; + if (Arc==NULL) + RevName=Name; + else { // We received .rar or .exe volume as a parameter, trying to find // the matching .rev file number 1. bool NewNumbering=Arc->NewNumbering; - wchar ArcName[NM]; - wcsncpyz(ArcName,Name,ASIZE(ArcName)); - - wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); - wchar RecVolMask[NM]; - wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); - size_t BaseNamePartLength=VolNumStart-ArcName; - wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + std::wstring RecVolMask; + size_t VolNumStart=VolNameToFirstName(Name,RecVolMask,NewNumbering); + RecVolMask.replace(VolNumStart, std::wstring::npos, L"*.rev"); FindFile Find; Find.SetMask(RecVolMask); @@ -67,31 +64,30 @@ void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) while (Find.Next(&RecData)) { - wchar *Num=GetVolNumPart(RecData.Name); - if (*Num!='1') // Name must have "0...01" numeric part. + size_t NumPos=GetVolNumPos(RecData.Name); + if (RecData.Name[NumPos]!='1') // Name must have "0...01" numeric part. continue; bool FirstVol=true; - while (--Num>=RecData.Name && IsDigit(*Num)) - if (*Num!='0') + while (NumPos>0 && IsDigit(RecData.Name[--NumPos])) + if (RecData.Name[NumPos]!='0') { FirstVol=false; break; } if (FirstVol) { - wcsncpyz(RevName,RecData.Name,ASIZE(RevName)); - Name=RevName; + RevName=RecData.Name; break; } } - if (*RevName==0) // First .rev file not found. + if (RevName.empty()) // First .rev file not found. return; } File RevFile; - if (!RevFile.Open(Name)) + if (!RevFile.Open(RevName)) { - ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN. + ErrHandler.OpenErrorMsg(RevName); // It also sets RARX_OPEN. return; } mprintf(L"\n"); @@ -101,11 +97,11 @@ void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) if (Rev5) { RecVolumes5 RecVol(Cmd,true); - RecVol.Test(Cmd,Name); + RecVol.Test(Cmd,RevName); } else { RecVolumes3 RecVol(Cmd,true); - RecVol.Test(Cmd,Name); + RecVol.Test(Cmd,RevName); } } diff --git a/unrar/recvol.hpp b/unrar/recvol.hpp index 4a6d663f..159bd1ad 100644 --- a/unrar/recvol.hpp +++ b/unrar/recvol.hpp @@ -8,7 +8,7 @@ class RecVolumes3 { private: File *SrcFile[256]; - Array Buf; + std::vector Buf; #ifdef RAR_SMP ThreadPool *RSThreadPool; @@ -16,16 +16,16 @@ class RecVolumes3 public: RecVolumes3(CommandData *Cmd,bool TestOnly); ~RecVolumes3(); - void Make(CommandData *Cmd,wchar *ArcName); - bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); - void Test(CommandData *Cmd,const wchar *Name); + void Make(CommandData *Cmd,std::wstring ArcName); + bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent); + void Test(CommandData *Cmd,const std::wstring &Name); }; struct RecVolItem { File *f; - wchar Name[NM]; + std::wstring Name; uint CRC; uint64 FileSize; bool New; // Newly created RAR volume. @@ -52,7 +52,7 @@ class RecVolumes5 void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode); uint ReadHeader(File *RecFile,bool FirstRev); - Array RecItems; + std::vector RecItems; byte *RealReadBuffer; // Real pointer returned by 'new'. byte *ReadBuffer; // Pointer aligned for SSE instructions. @@ -78,11 +78,11 @@ class RecVolumes5 public: RecVolumes5(CommandData *Cmd,bool TestOnly); ~RecVolumes5(); - bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); - void Test(CommandData *Cmd,const wchar *Name); + bool Restore(CommandData *Cmd,const std::wstring &Name,bool Silent); + void Test(CommandData *Cmd,const std::wstring &Name); }; -bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent); -void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name); +bool RecVolumesRestore(CommandData *Cmd,const std::wstring &Name,bool Silent); +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const std::wstring &Name); #endif diff --git a/unrar/recvol3.cpp b/unrar/recvol3.cpp index 0138d0f3..4199deed 100644 --- a/unrar/recvol3.cpp +++ b/unrar/recvol3.cpp @@ -23,12 +23,6 @@ class RSEncode // Encode or decode data area, one object per one thread. #ifdef RAR_SMP -THREAD_PROC(RSEncodeThread) -{ - RSEncode *rs=(RSEncode *)Data; - rs->EncodeBuf(); -} - THREAD_PROC(RSDecodeThread) { RSEncode *rs=(RSEncode *)Data; @@ -47,8 +41,7 @@ RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly) } else { - Buf.Alloc(TotalBufferSize); - memset(SrcFile,0,sizeof(SrcFile)); + Buf.resize(TotalBufferSize); #ifdef RAR_SMP RSThreadPool=new ThreadPool(Cmd->Threads); #endif @@ -68,30 +61,16 @@ RecVolumes3::~RecVolumes3() -void RSEncode::EncodeBuf() -{ - for (int BufPos=BufStart;BufPosName;Ext--) - if (!IsDigit(*Ext)) - if (*Ext=='_' && IsDigit(*(Ext-1))) + for (ExtPos--;ExtPos>0;ExtPos--) + if (!IsDigit(Name[ExtPos])) + if (Name[ExtPos]=='_' && IsDigit(Name[ExtPos-1])) DigitGroup++; else break; @@ -99,19 +78,19 @@ static bool IsNewStyleRev(const wchar *Name) } -bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) +bool RecVolumes3::Restore(CommandData *Cmd,const std::wstring &Name,bool Silent) { - wchar ArcName[NM]; - wcsncpyz(ArcName,Name,ASIZE(ArcName)); - wchar *Ext=GetExt(ArcName); + std::wstring ArcName=Name; bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10. - bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0; + bool RevName=CmpExt(ArcName,L"rev"); if (RevName) { NewStyle=IsNewStyleRev(ArcName); - while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_')) - Ext--; - wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName)); + + size_t ExtPos=GetExtPos(ArcName); + while (ExtPos>1 && (IsDigit(ArcName[ExtPos-1]) || ArcName[ExtPos-1]=='_')) + ExtPos--; + ArcName.replace(ExtPos,std::wstring::npos,L"*.*"); FindFile Find; Find.SetMask(ArcName); @@ -121,7 +100,7 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) Archive Arc(Cmd); if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) { - wcsncpyz(ArcName,fd.Name,ASIZE(ArcName)); + ArcName=fd.Name; break; } } @@ -138,11 +117,10 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) bool NewNumbering=Arc.NewNumbering; Arc.Close(); - wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); - wchar RecVolMask[NM]; - wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); - size_t BaseNamePartLength=VolNumStart-ArcName; - wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + size_t VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering); + std::wstring RecVolMask=ArcName; + RecVolMask.replace(VolNumStart,std::wstring::npos,L"*.rev"); + size_t BaseNamePartLength=VolNumStart; int64 RecFileSize=0; @@ -155,25 +133,25 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) Find.SetMask(RecVolMask); FindData RecData; int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; - wchar PrevName[NM]; + std::wstring PrevName; while (Find.Next(&RecData)) { - wchar *CurName=RecData.Name; + std::wstring CurName=RecData.Name; int P[3]; if (!RevName && !NewStyle) { NewStyle=true; - wchar *Dot=GetExt(CurName); - if (Dot!=NULL) + size_t DotPos=GetExtPos(CurName); + if (DotPos!=std::wstring::npos) { - int LineCount=0; - Dot--; - while (Dot>CurName && *Dot!='.') + uint LineCount=0; + DotPos--; + while (DotPos>0 && CurName[DotPos]!='.') { - if (*Dot=='_') + if (CurName[DotPos]=='_') LineCount++; - Dot--; + DotPos--; } if (LineCount==2) NewStyle=false; @@ -209,17 +187,17 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) } else { - wchar *Dot=GetExt(CurName); - if (Dot==NULL) + size_t DotPos=GetExtPos(CurName); + if (DotPos==std::wstring::npos) continue; bool WrongParam=false; for (size_t I=0;I=CurName+BaseNamePartLength); - P[I]=atoiw(Dot+1); + DotPos--; + } while (IsDigit(CurName[DotPos]) && DotPos>=BaseNamePartLength); + P[I]=atoiw(&CurName[DotPos+1]); if (P[I]==0 || P[I]>255) WrongParam=true; } @@ -235,7 +213,7 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) } RecVolNumber=P[1]; FileNumber=P[2]; - wcsncpyz(PrevName,CurName,ASIZE(PrevName)); + PrevName=CurName; File *NewFile=new File; NewFile->TOpen(CurName); @@ -256,11 +234,9 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) if (FoundRecVolumes==0) return false; - bool WriteFlags[256]; - memset(WriteFlags,0,sizeof(WriteFlags)); + bool WriteFlags[256]{}; - wchar LastVolName[NM]; - *LastVolName=0; + std::wstring LastVolName; for (int CurArcNum=0;CurArcNumClose(); - wchar NewName[NM]; - wcsncpyz(NewName,ArcName,ASIZE(NewName)); - wcsncatz(NewName,L".bad",ASIZE(NewName)); + std::wstring NewName=ArcName+L".bad"; uiMsg(UIMSG_BADARCHIVE,ArcName); uiMsg(UIMSG_RENAMING,ArcName,NewName); @@ -329,13 +303,13 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) MissingVolumes++; if (CurArcNum==FileNumber-1) - wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName)); + LastVolName=ArcName; uiMsg(UIMSG_MISSINGVOL,ArcName); uiMsg(UIEVENT_NEWARCHIVE,ArcName); } SrcFile[CurArcNum]=(File*)NewFile; - NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering); + NextVolumeName(ArcName,!NewNumbering); } uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); @@ -460,7 +434,7 @@ bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) CurFile->Close(); SrcFile[I]=NULL; } - if (*LastVolName!=0) + if (!LastVolName.empty()) { // Truncate the last volume to its real size. Archive Arc(Cmd); @@ -504,7 +478,7 @@ void RSEncode::DecodeBuf() } -void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) +void RecVolumes3::Test(CommandData *Cmd,const std::wstring &Name) { if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32. { @@ -512,8 +486,7 @@ void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) return; } - wchar VolName[NM]; - wcsncpyz(VolName,Name,ASIZE(VolName)); + std::wstring VolName=Name; while (FileExist(VolName)) { @@ -525,7 +498,7 @@ void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) } if (!uiStartFileExtract(VolName,false,true,false)) return; - mprintf(St(MExtrTestFile),VolName); + mprintf(St(MExtrTestFile),VolName.c_str()); mprintf(L" "); CurFile.Seek(0,SEEK_END); int64 Length=CurFile.Tell(); @@ -546,6 +519,6 @@ void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) ErrHandler.SetErrorCode(RARX_CRC); } - NextVolumeName(VolName,ASIZE(VolName),false); + NextVolumeName(VolName,false); } } diff --git a/unrar/recvol5.cpp b/unrar/recvol5.cpp index 2d9c9471..e094b171 100644 --- a/unrar/recvol5.cpp +++ b/unrar/recvol5.cpp @@ -48,8 +48,8 @@ RecVolumes5::~RecVolumes5() { delete[] RealBuf; delete[] RealReadBuffer; - for (uint I=0;IArcName && IsDigit(*(Num-1))) - Num--; - if (Num<=PointToName(ArcName)) + size_t NumPos=GetVolNumPos(ArcName); + while (NumPos>0 && IsDigit(ArcName[NumPos-1])) + NumPos--; + if (NumPos<=GetNamePos(ArcName)) return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. - wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); + ArcName.replace(NumPos,std::wstring::npos,L"*.*"); - wchar FirstVolName[NM]; - *FirstVolName=0; - - wchar LongestRevName[NM]; - *LongestRevName=0; + std::wstring FirstVolName; + std::wstring LongestRevName; int64 RecFileSize=0; @@ -184,8 +180,8 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) ItemPos=RecNum; FoundRecVolumes++; - if (wcslen(fd.Name)>wcslen(LongestRevName)) - wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName)); + if (fd.Name.size()>LongestRevName.size()) + LongestRevName=fd.Name; } } else @@ -204,35 +200,35 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) // RAR volume found. Get its number, store the handle in appropriate // array slot, clean slots in between if we had to grow the array. - wchar *Num=GetVolNumPart(fd.Name); + size_t NumPos=GetVolNumPos(fd.Name); uint VolNum=0; - for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) - VolNum+=(*Num-'0')*K; + for (uint K=1;(int)NumPos>=0 && IsDigit(fd.Name[NumPos]);K*=10,NumPos--) + VolNum+=(fd.Name[NumPos]-'0')*K; if (VolNum==0 || VolNum>MaxVolumes) continue; - size_t CurSize=RecItems.Size(); + size_t CurSize=RecItems.size(); if (VolNum>CurSize) { - RecItems.Alloc(VolNum); - for (size_t I=CurSize;If=Vol; Item->New=false; - wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); + Item->Name=fd.Name; } } @@ -244,10 +240,10 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) // If we did not find even a single .rar volume, create .rar volume name // based on the longest .rev file name. Use longest .rev, so we have // enough space for volume number. - if (*FirstVolName==0) + if (FirstVolName.empty()) { - SetExt(LongestRevName,L"rar",ASIZE(LongestRevName)); - VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true); + SetExt(LongestRevName,L"rar"); + VolNameToFirstName(LongestRevName,FirstVolName,true); } uiMsg(UIMSG_RECVOLCALCCHECKSUM); @@ -309,9 +305,8 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) { Item->f->Close(); - wchar NewName[NM]; - wcsncpyz(NewName,Item->Name,ASIZE(NewName)); - wcsncatz(NewName,L".bad",ASIZE(NewName)); + std::wstring NewName; + NewName=Item->Name+L".bad"; uiMsg(UIMSG_BADARCHIVE,Item->Name); uiMsg(UIMSG_RENAMING,Item->Name,NewName); @@ -322,12 +317,12 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) if ((Item->New=(Item->f==NULL))==true) { - wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); + Item->Name=FirstVolName; uiMsg(UIMSG_CREATING,Item->Name); uiMsg(UIEVENT_NEWARCHIVE,Item->Name); File *NewVol=new File; bool UserReject; - if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) + if (!FileCreate(Cmd,NewVol,Item->Name,&UserReject)) { if (!UserReject) ErrHandler.CreateErrorMsg(Item->Name); @@ -336,7 +331,7 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) NewVol->Prealloc(Item->FileSize); Item->f=NewVol; } - NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); + NextVolumeName(FirstVolName,false); } @@ -389,7 +384,7 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) J++; VolNum=J++; // Use next valid REV volume data instead of RAR. } - RecVolItem *Item=RecItems+VolNum; + RecVolItem *Item=&RecItems[VolNum]; byte *B=&ReadBuf[0]; int ReadSize=0; @@ -411,7 +406,7 @@ bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) for (uint I=0,J=0;IFileSize); Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); Item->FileSize-=WriteSize; @@ -477,10 +472,10 @@ uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) { // If we have read the first valid REV file, init data structures // using information from REV header. - size_t CurSize=RecItems.Size(); - RecItems.Alloc(TotalCount); - for (size_t I=CurSize;I=1) // Check the maximum supported cpuid function. @@ -102,8 +110,24 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe } else AES_NI=false; +#elif defined(__GNUC__) + AES_NI=__builtin_cpu_supports("aes"); +#endif + #elif defined(USE_NEON) - AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; + #ifdef _APPLE + // getauxval isn't available in OS X + uint Value=0; + size_t Size=sizeof(Value); + int RetCode=sysctlbyname("hw.optional.arm.FEAT_AES",&Value,&Size,NULL,0); + + // We treat sysctlbyname failure with -1 return code as AES presence, + // because "hw.optional.arm.FEAT_AES" was missing in OS X 11, but AES + // still was supported by Neon. + AES_Neon=RetCode!=0 || Value!=0; + #else + AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; + #endif #endif // Other developers asked us to initialize it to suppress "may be used @@ -585,16 +609,16 @@ void TestRijndael() for (uint L=0;L<3;L++) { byte Out[16]; - wchar Str[sizeof(Out)*2+1]; + std::wstring Str; uint KeyLength=128+L*64; rij.Init(true,Key[L],KeyLength,IV); for (uint I=0;I0 && FindCode==SCAN_SUCCESS) - if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH)) + if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name.c_str(),false,MATCH_WILDSUBPATH)) continue; break; } @@ -79,7 +78,7 @@ bool ScanTree::ExpandFolderMask() { bool WildcardFound=false; uint SlashPos=0; - for (int I=0;CurMask[I]!=0;I++) + for (uint I=0;I0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask))) + if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask)) return true; FolderWildcards=false; FilterList.Reset(); - if (!FileMasks->GetString(CurMask,ASIZE(CurMask))) + if (!FileMasks->GetString(CurMask)) return false; // Check if folder wildcards present. @@ -144,10 +141,10 @@ bool ScanTree::GetFilteredMask() uint SlashPos=0; uint StartPos=0; #ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. - if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\') + if (CurMask.rfind(L"\\\\?\\",0)==0) StartPos=4; #endif - for (uint I=StartPos;CurMask[I]!=0;I++) + for (uint I=StartPos;I2 && CurMask[0]=='\\' && CurMask[1]=='\\') { - const wchar *Slash=wcschr(CurMask+2,'\\'); - if (Slash!=NULL) + auto Slash=CurMask.find('\\',2); + if (Slash!=std::wstring::npos) { - Slash=wcschr(Slash+1,'\\'); - ScanEntireDisk=Slash!=NULL && *(Slash+1)==0; + Slash=CurMask.find('\\',Slash+1); + // If backslash is found and it is the last string character. + ScanEntireDisk=Slash!=std::wstring::npos && Slash+1==CurMask.size(); } } else ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; - wchar *Name=PointToName(CurMask); - if (*Name==0) - wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); - if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) + auto NamePos=GetNamePos(CurMask); + std::wstring Name=CurMask.substr(NamePos); + if (Name.empty()) + CurMask+=MASKALL; + if (Name==L"." || Name==L"..") { - AddEndSlash(CurMask,ASIZE(CurMask)); - wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); + AddEndSlash(CurMask); + CurMask+=MASKALL; } - SpecPathLength=Name-CurMask; + SpecPathLength=NamePos; Depth=0; - wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); + OrigCurMask=CurMask; return true; } @@ -250,7 +249,7 @@ bool ScanTree::GetNextMask() SCAN_CODE ScanTree::FindProc(FindData *FD) { - if (*CurMask==0) + if (CurMask.empty()) return SCAN_NEXT; bool FastFindFile=false; @@ -283,10 +282,9 @@ SCAN_CODE ScanTree::FindProc(FindData *FD) // Create the new FindFile object for wildcard based search. FindStack[Depth]=new FindFile; - wchar SearchMask[NM]; - wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask)); + std::wstring SearchMask=CurMask; if (SearchAll) - SetName(SearchMask,MASKALL,ASIZE(SearchMask)); + SetName(SearchMask,MASKALL); FindStack[Depth]->SetMask(SearchMask); } else @@ -325,7 +323,7 @@ SCAN_CODE ScanTree::FindProc(FindData *FD) // It is not necessary for directories, because even in "fast find" // mode, directory recursing will quit by (Depth < 0) condition, // which returns SCAN_DONE to calling function. - *CurMask=0; + CurMask.clear(); return RetCode; } @@ -344,9 +342,6 @@ SCAN_CODE ScanTree::FindProc(FindData *FD) if (Error) ScanError(Error); - wchar DirName[NM]; - *DirName=0; - // Going to at least one directory level higher. delete FindStack[Depth]; FindStack[Depth--]=NULL; @@ -362,29 +357,32 @@ SCAN_CODE ScanTree::FindProc(FindData *FD) return SCAN_DONE; } - wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER); - if (Slash!=NULL) + auto Slash=CurMask.rfind(CPATHDIVIDER); + if (Slash!=std::wstring::npos) { - wchar Mask[NM]; - wcsncpyz(Mask,Slash,ASIZE(Mask)); + std::wstring Mask; + Mask=CurMask.substr(Slash); // Name mask with leading slash like \*.* if (DepthIsDir) + { + FD->Flags|=FDDF_SECONDDIR; + return Error ? SCAN_ERROR:SCAN_SUCCESS; } - } - if (GetDirs==SCAN_GETDIRSTWICE && - FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir) - { - FD->Flags|=FDDF_SECONDDIR; - return Error ? SCAN_ERROR:SCAN_SUCCESS; } return Error ? SCAN_ERROR:SCAN_NEXT; } @@ -414,22 +412,22 @@ SCAN_CODE ScanTree::FindProc(FindData *FD) return FastFindFile ? SCAN_DONE:SCAN_NEXT; } - wchar Mask[NM]; - - wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); - wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); + std::wstring Mask=FastFindFile ? MASKALL:PointToName(CurMask); + CurMask=FD->Name; - if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) + if (CurMask.size()+Mask.size()+1>=MAXPATHSIZE || Depth>=MAXSCANDEPTH-1) { uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); return SCAN_ERROR; } - AddEndSlash(CurMask,ASIZE(CurMask)); - wcsncatz(CurMask,Mask,ASIZE(CurMask)); + AddEndSlash(CurMask); + CurMask+=Mask; Depth++; + FindStack.resize(Depth+1); + // We need to use OrigCurMask for depths less than SetAllMaskDepth // and "*" for depths equal or larger than SetAllMaskDepth. // It is important when "fast finding" directories at Depth > 0. @@ -470,19 +468,18 @@ void ScanTree::ScanError(bool &Error) // We cannot just check FD->FileAttr here, it can be undefined // if we process "folder\*" mask or if we process "folder" mask, // but "folder" is inaccessible. - wchar *Slash=PointToName(CurMask); - if (Slash>CurMask) + auto Slash=GetNamePos(CurMask); + if (Slash>1) { - *(Slash-1)=0; - DWORD Attr=GetFileAttributes(CurMask); - *(Slash-1)=CPATHDIVIDER; + std::wstring Parent=CurMask.substr(0,Slash-1); + DWORD Attr=GetFileAttr(Parent); if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) Error=false; } // Do not display an error if we cannot scan contents of // "System Volume Information" folder. Normally it is not accessible. - if (wcsstr(CurMask,L"System Volume Information\\")!=NULL) + if (CurMask.find(L"System Volume Information\\")!=std::wstring::npos) Error=false; } #endif @@ -495,10 +492,10 @@ void ScanTree::ScanError(bool &Error) if (ErrDirList!=NULL) ErrDirList->AddString(CurMask); if (ErrDirSpecPathLength!=NULL) - ErrDirSpecPathLength->Push((uint)SpecPathLength); - wchar FullName[NM]; + ErrDirSpecPathLength->push_back((uint)SpecPathLength); + std::wstring FullName; // This conversion works for wildcard masks too. - ConvertNameToFull(CurMask,FullName,ASIZE(FullName)); + ConvertNameToFull(CurMask,FullName); uiMsg(UIERROR_DIRSCAN,FullName); ErrHandler.SysErrMsg(); } diff --git a/unrar/scantree.hpp b/unrar/scantree.hpp index 7ebe69ad..06bf545c 100644 --- a/unrar/scantree.hpp +++ b/unrar/scantree.hpp @@ -11,20 +11,21 @@ enum SCAN_DIRS enum SCAN_CODE { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT }; -#define MAXSCANDEPTH (NM/2) - class CommandData; class ScanTree { private: + static constexpr size_t MAXSCANDEPTH = MAXPATHSIZE/2; + bool ExpandFolderMask(); bool GetFilteredMask(); bool GetNextMask(); SCAN_CODE FindProc(FindData *FD); void ScanError(bool &Error); - FindFile *FindStack[MAXSCANDEPTH]; +// FindFile *FindStack[MAXSCANDEPTH]; + std::vector FindStack; int Depth; int SetAllMaskDepth; @@ -33,13 +34,13 @@ class ScanTree RECURSE_MODE Recurse; bool GetLinks; SCAN_DIRS GetDirs; - int Errors; + uint Errors; // Set when processing paths like c:\ (root directory without wildcards). bool ScanEntireDisk; - wchar CurMask[NM]; - wchar OrigCurMask[NM]; + std::wstring CurMask; + std::wstring OrigCurMask; // Store all folder masks generated from folder wildcard mask in non-recursive mode. StringList ExpandedFolderList; @@ -49,7 +50,7 @@ class ScanTree // Save the list of unreadable dirs here. StringList *ErrDirList; - Array *ErrDirSpecPathLength; + std::vector *ErrDirSpecPathLength; // Set if processing a folder wildcard mask. bool FolderWildcards; @@ -57,7 +58,7 @@ class ScanTree bool SearchAllInRoot; size_t SpecPathLength; - wchar ErrArcName[NM]; + std::wstring ErrArcName; CommandData *Cmd; public: @@ -65,10 +66,10 @@ class ScanTree ~ScanTree(); SCAN_CODE GetNext(FindData *FindData); size_t GetSpecPathLength() {return SpecPathLength;} - int GetErrors() {return Errors;}; - void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));} + uint GetErrors() {return Errors;}; + void SetErrArcName(const std::wstring &Name) {ErrArcName=Name;} void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;} - void SetErrDirList(StringList *List,Array *Lengths) + void SetErrDirList(StringList *List,std::vector *Lengths) { ErrDirList=List; ErrDirSpecPathLength=Lengths; diff --git a/unrar/secpassword.cpp b/unrar/secpassword.cpp index 08da5497..8d8f2986 100644 --- a/unrar/secpassword.cpp +++ b/unrar/secpassword.cpp @@ -69,8 +69,8 @@ SecPassword::~SecPassword() void SecPassword::Clean() { PasswordSet=false; - if (Password.size()>0) - cleandata(&Password[0],Password.size()*sizeof(Password[0])); + if (!Password.empty()) + cleandata(Password.data(),Password.size()*sizeof(Password[0])); } @@ -79,7 +79,7 @@ void SecPassword::Clean() // So we use our own function for this purpose. void cleandata(void *data,size_t size) { - if (data==NULL || size==0) + if (data==nullptr || size==0) return; #if defined(_WIN_ALL) && defined(_MSC_VER) SecureZeroMemory(data,size); @@ -120,6 +120,14 @@ void SecPassword::Get(wchar *Psw,size_t MaxSize) } +void SecPassword::Get(std::wstring &Psw) +{ + wchar PswBuf[MAXPASSWORD]; + Get(PswBuf,ASIZE(PswBuf)); + Psw=PswBuf; +} + + void SecPassword::Set(const wchar *Psw) diff --git a/unrar/secpassword.hpp b/unrar/secpassword.hpp index 5284bce1..44e073c9 100644 --- a/unrar/secpassword.hpp +++ b/unrar/secpassword.hpp @@ -15,6 +15,7 @@ class SecPassword ~SecPassword(); void Clean(); void Get(wchar *Psw,size_t MaxSize); + void Get(std::wstring &Psw); void Set(const wchar *Psw); bool IsSet() {return PasswordSet;} size_t Length(); @@ -23,6 +24,7 @@ class SecPassword void cleandata(void *data,size_t size); +inline void cleandata(std::wstring &s) {cleandata(&s[0],s.size()*sizeof(s[0]));} void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess); #endif diff --git a/unrar/sha256.cpp b/unrar/sha256.cpp index f90d2c09..e18a21d6 100644 --- a/unrar/sha256.cpp +++ b/unrar/sha256.cpp @@ -146,3 +146,13 @@ void sha256_done(sha256_context *ctx, byte *Digest) sha256_init(ctx); } + + +void sha256_get(const void *Data, size_t Size, byte *Digest) +{ + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, Data, Size); + sha256_done(&ctx, Digest); +} + diff --git a/unrar/sha256.hpp b/unrar/sha256.hpp index b6837e76..186297c0 100644 --- a/unrar/sha256.hpp +++ b/unrar/sha256.hpp @@ -13,5 +13,6 @@ typedef struct void sha256_init(sha256_context *ctx); void sha256_process(sha256_context *ctx, const void *Data, size_t Size); void sha256_done(sha256_context *ctx, byte *Digest); +void sha256_get(const void *Data, size_t Size, byte *Digest); #endif diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index 7617f7a5..2d9cf6ef 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -2,66 +2,67 @@ const char *NullToEmpty(const char *Str) { - return Str==NULL ? "":Str; + return Str==nullptr ? "":Str; } const wchar *NullToEmpty(const wchar *Str) { - return Str==NULL ? L"":Str; + return Str==nullptr ? L"":Str; } -void IntToExt(const char *Src,char *Dest,size_t DestSize) +void IntToExt(const std::string &Src,std::string &Dest) { #ifdef _WIN_ALL - // OemToCharBuff does not stop at 0, so let's check source length. - size_t SrcLength=strlen(Src)+1; - if (DestSize>SrcLength) - DestSize=SrcLength; - OemToCharBuffA(Src,Dest,(DWORD)DestSize); - Dest[DestSize-1]=0; + if (std::addressof(Src)!=std::addressof(Dest)) + Dest=Src; + // OemToCharA use seems to be discouraged. So we use OemToCharBuffA, + // which doesn't stop at 0 and converts the entire passed length. + OemToCharBuffA(&Dest[0],&Dest[0],(DWORD)Dest.size()); + + std::string::size_type Pos=Dest.find('\0'); // Avoid zeroes inside of Dest. + if (Pos!=std::string::npos) + Dest.erase(Pos); + #else - if (Dest!=Src) - strncpyz(Dest,Src,DestSize); + if (std::addressof(Src)!=std::addressof(Dest)) + Dest=Src; #endif } // Convert archived names and comments to Unicode. // Allows user to select a code page in GUI. -void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding) +void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding) { #if defined(_WIN_ALL) // Console Windows RAR. if (Encoding==ACTW_UTF8) - UtfToWide(Src,Dest,DestSize); + UtfToWide(Src,Dest); else { - Array NameA; + std::string NameA; if (Encoding==ACTW_OEM) { - NameA.Alloc(DestSize+1); - IntToExt(Src,&NameA[0],NameA.Size()); - Src=&NameA[0]; + IntToExt(Src,NameA); + Src=NameA.data(); } - CharToWide(Src,Dest,DestSize); + CharToWide(Src,Dest); } #else // RAR for Unix. if (Encoding==ACTW_UTF8) - UtfToWide(Src,Dest,DestSize); + UtfToWide(Src,Dest); else - CharToWide(Src,Dest,DestSize); + CharToWide(Src,Dest); #endif - // Ensure that we return a zero terminate string for security reason. - // While [Jni]CharToWide might already do it, be protected in case of future - // changes in these functions. - if (DestSize>0) - Dest[DestSize-1]=0; + TruncateAtZero(Dest); // Ensure there are no zeroes inside of string. } + + int stricomp(const char *s1,const char *s2) { #ifdef _WIN_ALL @@ -113,6 +114,19 @@ wchar* RemoveEOL(wchar *Str) } +void RemoveEOL(std::wstring &Str) +{ + while (!Str.empty()) + { + wchar c=Str.back(); + if (c=='\r' || c=='\n' || c==' ' || c=='\t') + Str.pop_back(); + else + break; + } +} + + wchar* RemoveLF(wchar *Str) { for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) @@ -121,6 +135,13 @@ wchar* RemoveLF(wchar *Str) } +void RemoveLF(std::wstring &Str) +{ + for (int I=(int)Str.size()-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) + Str.erase(I); +} + + #if defined(SFX_MODULE) // char version of etoupperw. Used in console SFX module only. // Fast toupper for English only input and output. Additionally to speed, @@ -176,30 +197,18 @@ bool IsAlpha(int ch) -void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize) +void BinToHex(const byte *Bin,size_t BinSize,std::wstring &Hex) { - uint A=0,W=0; // ASCII and Unicode hex output positions. + Hex.clear(); for (uint I=0;I> 4; uint Low=Bin[I] & 0xf; - uint HighHex=High>9 ? 'a'+High-10:'0'+High; - uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low; - if (HexA!=NULL && A9 ? 'a'+High-10 : '0'+High; + uint LowHex=Low>9 ? 'a'+Low-10 : '0'+Low; + Hex+=HighHex; + Hex+=LowHex; } - if (HexA!=NULL && HexSize>0) - HexA[A]=0; - if (HexW!=NULL && HexSize>0) - HexW[W]=0; } @@ -217,22 +226,25 @@ uint GetDigits(uint Number) #endif -bool LowAscii(const char *Str) +bool LowAscii(const std::string &Str) { - for (size_t I=0;Str[I]!=0;I++) - if (/*(byte)Str[I]<32 || */(byte)Str[I]>127) + for (char Ch : Str) + { + // We convert char to byte in case char is signed. + if (/*(uint)Ch<32 || */(byte)Ch>127) return false; + } return true; } -bool LowAscii(const wchar *Str) +bool LowAscii(const std::wstring &Str) { - for (size_t I=0;Str[I]!=0;I++) + for (wchar Ch : Str) { // 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)Ch<32 || */(uint)Ch>127) return false; } return true; @@ -249,6 +261,12 @@ int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison. } +int wcsicompc(const std::wstring &s1,const std::wstring &s2) +{ + return wcsicompc(s1.c_str(),s2.c_str()); +} + + int wcsnicompc(const wchar *s1,const wchar *s2,size_t n) { #if defined(_UNIX) @@ -259,6 +277,12 @@ int wcsnicompc(const wchar *s1,const wchar *s2,size_t n) } +int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n) +{ + return wcsnicompc(s1.c_str(),s2.c_str(),n); +} + + // Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. void strncpyz(char *dest, const char *src, size_t maxlen) { @@ -383,88 +407,149 @@ void fmtitoa(int64 n,wchar *Str,size_t MaxSize) } -const wchar* GetWide(const char *Src) +std::wstring GetWide(const char *Src) { - const size_t MaxLength=NM; - static wchar StrTable[4][MaxLength]; - static uint StrNum=0; - if (++StrNum >= ASIZE(StrTable)) - StrNum=0; - wchar *Str=StrTable[StrNum]; - CharToWide(Src,Str,MaxLength); - Str[MaxLength-1]=0; + std::wstring Str; + CharToWide(Src,Str); return Str; } // Parse string containing parameters separated with spaces. -// Support quote marks. Param can be NULL to return the pointer to next -// parameter, which can be used to estimate the buffer size for Param. -const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize) +// Support quote marks. Accepts and updates the current position in the string. +// Returns false if there is nothing to parse. +bool GetCmdParam(const std::wstring &CmdLine,std::wstring::size_type &Pos,std::wstring &Param) { - while (IsSpace(*CmdLine)) - CmdLine++; - if (*CmdLine==0) - return NULL; + Param.clear(); + + while (IsSpace(CmdLine[Pos])) + Pos++; + if (Pos==CmdLine.size()) + return false; - size_t ParamSize=0; bool Quote=false; - while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine))) + while (Pos=0 || Msg.size()>MaxAllocSize) + break; + Msg.resize(Msg.size()*4); + } + std::wstring::size_type ZeroPos=Msg.find(L'\0'); + if (ZeroPos!=std::wstring::npos) + Msg.resize(ZeroPos); // Remove excessive zeroes at the end. + + return Msg; } #endif + + +#ifdef _WIN_ALL +bool ExpandEnvironmentStr(std::wstring &Str) +{ + DWORD ExpCode=ExpandEnvironmentStrings(Str.c_str(),nullptr,0); + if (ExpCode==0) + return false; + std::vector Buf(ExpCode); + ExpCode=ExpandEnvironmentStrings(Str.c_str(),Buf.data(),(DWORD)Buf.size()); + if (ExpCode==0 || ExpCode>Buf.size()) + return false; + Str=Buf.data(); + return true; +} +#endif + + +void TruncateAtZero(std::wstring &Str) +{ + std::wstring::size_type Pos=Str.find(L'\0'); + if (Pos!=std::wstring::npos) + Str.erase(Pos); +} diff --git a/unrar/strfn.hpp b/unrar/strfn.hpp index 2a21feae..fb3003b2 100644 --- a/unrar/strfn.hpp +++ b/unrar/strfn.hpp @@ -3,16 +3,18 @@ const char* NullToEmpty(const char *Str); const wchar* NullToEmpty(const wchar *Str); -void IntToExt(const char *Src,char *Dest,size_t DestSize); +void IntToExt(const std::string &Src,std::string &Dest); enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8}; -void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding); +void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding); int stricomp(const char *s1,const char *s2); int strnicomp(const char *s1,const char *s2,size_t n); wchar* RemoveEOL(wchar *Str); +void RemoveEOL(std::wstring &Str); wchar* RemoveLF(wchar *Str); +void RemoveLF(std::wstring &Str); void strncpyz(char *dest, const char *src, size_t maxlen); void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen); @@ -28,25 +30,35 @@ bool IsDigit(int ch); bool IsSpace(int ch); bool IsAlpha(int ch); -void BinToHex(const byte *Bin,size_t BinSize,char *Hex,wchar *HexW,size_t HexSize); +void BinToHex(const byte *Bin,size_t BinSize,std::wstring &Hex); #ifndef SFX_MODULE uint GetDigits(uint Number); #endif -bool LowAscii(const char *Str); -bool LowAscii(const wchar *Str); +bool LowAscii(const std::string &Str); +bool LowAscii(const std::wstring &Str); int wcsicompc(const wchar *s1,const wchar *s2); +int wcsicompc(const std::wstring &s1,const std::wstring &s2); int wcsnicompc(const wchar *s1,const wchar *s2,size_t n); +int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n); void itoa(int64 n,char *Str,size_t MaxSize); void itoa(int64 n,wchar *Str,size_t MaxSize); void fmtitoa(int64 n,wchar *Str,size_t MaxSize); -const wchar* GetWide(const char *Src); -const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize); +std::wstring GetWide(const char *Src); +bool GetCmdParam(const std::wstring &CmdLine,std::wstring::size_type &Pos,std::wstring &Param); #ifndef RARDLL -void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize); +void PrintfPrepareFmt(const wchar *Org,std::wstring &Cvt); +std::wstring wstrprintf(const wchar *fmt,...); +std::wstring vwstrprintf(const wchar *fmt,va_list arglist); #endif +#ifdef _WIN_ALL +bool ExpandEnvironmentStr(std::wstring &Str); +#endif + +void TruncateAtZero(std::wstring &Str); + #endif diff --git a/unrar/strlist.cpp b/unrar/strlist.cpp index 50d69c71..e76654fc 100644 --- a/unrar/strlist.cpp +++ b/unrar/strlist.cpp @@ -9,18 +9,20 @@ StringList::StringList() void StringList::Reset() { Rewind(); - StringData.Reset(); + StringData.clear(); StringsCount=0; SavePosNumber=0; } +/* void StringList::AddStringA(const char *Str) { - Array StrW(strlen(Str)); - CharToWide(Str,&StrW[0],StrW.Size()); - AddString(&StrW[0]); + std::wstring StrW; + CharToWide(Str,StrW); + AddString(StrW); } +*/ void StringList::AddString(const wchar *Str) @@ -28,22 +30,30 @@ void StringList::AddString(const wchar *Str) if (Str==NULL) Str=L""; - size_t PrevSize=StringData.Size(); - StringData.Add(wcslen(Str)+1); + size_t PrevSize=StringData.size(); + StringData.resize(PrevSize+wcslen(Str)+1); wcscpy(&StringData[PrevSize],Str); StringsCount++; } +void StringList::AddString(const std::wstring &Str) +{ + AddString(Str.c_str()); +} + + +/* bool StringList::GetStringA(char *Str,size_t MaxLength) { - Array StrW(MaxLength); - if (!GetString(&StrW[0],StrW.Size())) + std::wstring StrW; + if (!GetString(StrW)) return false; - WideToChar(&StrW[0],Str,MaxLength); + WideToChar(StrW.c_str(),Str,MaxLength); return true; } +*/ bool StringList::GetString(wchar *Str,size_t MaxLength) @@ -56,6 +66,16 @@ bool StringList::GetString(wchar *Str,size_t MaxLength) } +bool StringList::GetString(std::wstring &Str) +{ + wchar *StrPtr; + if (!GetString(&StrPtr)) + return false; + Str=StrPtr; + return true; +} + + #ifndef SFX_MODULE bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) { @@ -71,6 +91,22 @@ bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) RestorePosition(); return RetCode; } + + +bool StringList::GetString(std::wstring &Str,int StringNum) +{ + SavePosition(); + Rewind(); + bool RetCode=true; + while (StringNum-- >=0) + if (!GetString(Str)) + { + RetCode=false; + break; + } + RestorePosition(); + return RetCode; +} #endif @@ -84,7 +120,7 @@ wchar* StringList::GetString() bool StringList::GetString(wchar **Str) { - if (CurPos>=StringData.Size()) // No more strings left unprocessed. + if (CurPos>=StringData.size()) // No more strings left unprocessed. { if (Str!=NULL) *Str=NULL; @@ -107,7 +143,7 @@ void StringList::Rewind() #ifndef SFX_MODULE -bool StringList::Search(const wchar *Str,bool CaseSensitive) +bool StringList::Search(const std::wstring &Str,bool CaseSensitive) { SavePosition(); Rewind(); @@ -115,8 +151,8 @@ bool StringList::Search(const wchar *Str,bool CaseSensitive) wchar *CurStr; while (GetString(&CurStr)) { - if (Str!=NULL && CurStr!=NULL) - if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0) + if (CurStr!=NULL) + if (CaseSensitive && Str!=CurStr || !CaseSensitive && wcsicomp(Str,CurStr)!=0) continue; Found=true; break; diff --git a/unrar/strlist.hpp b/unrar/strlist.hpp index 16a2cbb0..d89ba5c9 100644 --- a/unrar/strlist.hpp +++ b/unrar/strlist.hpp @@ -4,7 +4,7 @@ class StringList { private: - Array StringData; + std::vector StringData; size_t CurPos; size_t StringsCount; @@ -13,17 +13,20 @@ class StringList public: StringList(); void Reset(); - void AddStringA(const char *Str); +// void AddStringA(const char *Str); void AddString(const wchar *Str); - bool GetStringA(char *Str,size_t MaxLength); + void AddString(const std::wstring &Str); +// bool GetStringA(char *Str,size_t MaxLength); bool GetString(wchar *Str,size_t MaxLength); + bool GetString(std::wstring &Str); bool GetString(wchar *Str,size_t MaxLength,int StringNum); + bool GetString(std::wstring &Str,int StringNum); wchar* GetString(); bool GetString(wchar **Str); void Rewind(); size_t ItemsCount() {return StringsCount;}; - size_t GetCharCount() {return StringData.Size();} - bool Search(const wchar *Str,bool CaseSensitive); + size_t GetCharCount() {return StringData.size();} + bool Search(const std::wstring &Str,bool CaseSensitive); void SavePosition(); void RestorePosition(); }; diff --git a/unrar/system.cpp b/unrar/system.cpp index 9e536227..cf9d1836 100644 --- a/unrar/system.cpp +++ b/unrar/system.cpp @@ -150,16 +150,18 @@ bool ShutdownCheckAnother(bool Open) + #if defined(_WIN_ALL) // Load library from Windows System32 folder. Use this function to prevent // loading a malicious code from current folder or same folder as exe. HMODULE WINAPI LoadSysLibrary(const wchar *Name) { - wchar SysDir[NM]; - if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0) - return NULL; - MakeName(SysDir,Name,SysDir,ASIZE(SysDir)); - return LoadLibrary(SysDir); + std::vector SysDir(MAX_PATH); + if (GetSystemDirectory(SysDir.data(),(UINT)SysDir.size())==0) + return nullptr; + std::wstring FullName; + MakeName(SysDir.data(),Name,FullName); + return LoadLibrary(FullName.c_str()); } @@ -186,6 +188,7 @@ SSE_VERSION _SSE_Version=GetSSEVersion(); SSE_VERSION GetSSEVersion() { +#ifdef _MSC_VER int CPUInfo[4]; __cpuid(CPUInfo, 0); @@ -210,6 +213,18 @@ SSE_VERSION GetSSEVersion() if ((CPUInfo[3] & 0x2000000)!=0) return SSE_SSE; } +#elif defined(__GNUC__) + if (__builtin_cpu_supports("avx2")) + return SSE_AVX2; + if (__builtin_cpu_supports("sse4.1")) + return SSE_SSE41; + if (__builtin_cpu_supports("ssse3")) + return SSE_SSSE3; + if (__builtin_cpu_supports("sse2")) + return SSE_SSE2; + if (__builtin_cpu_supports("sse")) + return SSE_SSE; +#endif return SSE_NONE; } #endif diff --git a/unrar/system.hpp b/unrar/system.hpp index a56d6b7f..de4c14a6 100644 --- a/unrar/system.hpp +++ b/unrar/system.hpp @@ -21,7 +21,7 @@ void InitSystemOptions(int SleepTime); void SetPriority(int Priority); clock_t MonoClock(); void Wait(); -bool EmailFile(const wchar *FileName,const wchar *MailToW); +bool EmailFile(const std::wstring &FileName,std::wstring MailToW); void Shutdown(POWER_MODE Mode); bool ShutdownCheckAnother(bool Open); @@ -30,7 +30,6 @@ HMODULE WINAPI LoadSysLibrary(const wchar *Name); bool IsUserAdmin(); #endif - #ifdef USE_SSE enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2}; SSE_VERSION GetSSEVersion(); diff --git a/unrar/timefn.cpp b/unrar/timefn.cpp index 0abf49de..ceac8300 100644 --- a/unrar/timefn.cpp +++ b/unrar/timefn.cpp @@ -183,8 +183,7 @@ void RarTime::SetUnix(time_t ut) // Get the high precision Unix time in nanoseconds since 01-01-1970. uint64 RarTime::GetUnixNS() { - // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. - uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + const uint64 ushift=11644473600000000000ULL; // ns between 01-01-1601 and 01-01-1970. return itime*(1000000000/TICKS_PER_SECOND)-ushift; } @@ -192,8 +191,7 @@ uint64 RarTime::GetUnixNS() // Set the high precision Unix time in nanoseconds since 01-01-1970. void RarTime::SetUnixNS(uint64 ns) { - // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. - uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + const uint64 ushift=11644473600000000000ULL; // ns between 01-01-1601 and 01-01-1970. itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND); } diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 5def26df..d0a90de2 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -19,8 +19,8 @@ enum UIMESSAGE_CODE { UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN, UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, - UIERROR_NOTSFX, UIERROR_OLDTOSFX, - UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM, + UIERROR_NOTSFX, UIERROR_OLDTOSFX,UIERROR_WRONGSFXVER, + UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM,UIERROR_EXTRDICTOUTMEM, UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, @@ -76,19 +76,25 @@ enum UIASKREP_RESULT { UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED }; -UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); -UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); void uiInit(SOUND_NOTIFY_MODE Sound); -void uiStartArchiveExtract(bool Extract,const wchar *ArcName); -bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip); +void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName); +bool uiStartFileExtract(const std::wstring &FileName,bool Extract,bool Test,bool Skip); void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize); void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); -enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd); +enum UIPASSWORD_TYPE +{ + UIPASSWORD_GLOBAL, // For -p, -hp without parameter or Ctrl+P in WinRAR. + UIPASSWORD_FILE, // Extracting an encrypted file. + UIPASSWORD_ARCHIVE, // Extracting or opening an encrypted header archive. +}; + +bool uiGetPassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password,CheckPassword *CheckPwd); bool uiIsGlobalPasswordSet(); enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; @@ -96,11 +102,13 @@ void uiAlarm(UIALARM_TYPE Type); void uiEolAfterMsg(); -bool uiAskNextVolume(wchar *VolName,size_t MaxSize); +bool uiAskNextVolume(std::wstring &VolName); #if !defined(SILENT) && !defined(SFX_MODULE) -void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); +void uiAskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); #endif -bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull); +bool uiAskRepeatWrite(const std::wstring &FileName,bool DiskFull); + +bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize); #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month); @@ -131,6 +139,12 @@ class uiMsgStore Str[StrSize++]=s; return *this; } + uiMsgStore& operator << (const std::wstring &s) + { + if (StrSizeOverwrite==OVERWRITE_NONE) return UIASKREP_R_SKIP; #if !defined(SFX_MODULE) && !defined(SILENT) // Must be before Cmd->AllYes check or -y switch would override -or. - if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize)) + if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name)) return UIASKREP_R_REPLACE; #endif @@ -27,9 +28,8 @@ UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,i return UIASKREP_R_REPLACE; } - wchar NewName[NM]; - wcsncpyz(NewName,Name,ASIZE(NewName)); - UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags); + std::wstring NewName=Name; + UIASKREP_RESULT Choice=uiAskReplace(NewName,FileSize,FileTime,Flags); if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) PrepareToDelete(Name); @@ -46,16 +46,16 @@ UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,i } if (Choice==UIASKREP_R_RENAME) { - if (PointToName(NewName)==NewName) - SetName(Name,NewName,MaxNameSize); + if (GetNamePos(NewName)==0) + SetName(Name,NewName); else - wcsncpyz(Name,NewName,MaxNameSize); + Name=NewName; if (FileExist(Name)) - return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags); + return uiAskReplaceEx(Cmd,Name,FileSize,FileTime,Flags); return UIASKREP_R_REPLACE; } #if !defined(SFX_MODULE) && !defined(SILENT) - if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize)) + if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name)) { Cmd->Overwrite=OVERWRITE_AUTORENAME; return UIASKREP_R_REPLACE; @@ -63,3 +63,23 @@ UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,i #endif return Choice; } + + +bool GetAutoRenamedName(std::wstring &Name) +{ + std::wstring Ext=GetExt(Name); + for (uint FileVer=1;FileVer<1000000;FileVer++) + { + std::wstring NewName=Name; + RemoveExt(NewName); + wchar Ver[10]; + itoa(FileVer,Ver,ASIZE(Ver)); + NewName = NewName + L"(" + Ver + L")" + Ext; + if (!FileExist(NewName)) + { + Name=NewName; + return true; + } + } + return false; +} diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index b524c25f..15406ee9 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -1,12 +1,11 @@ static bool AnyMessageDisplayed=false; // 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) +UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags) { wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; - FindData ExistingFD; - memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails. + FindData ExistingFD={}; // Init in case find fails. FindFile::FastFind(Name,&ExistingFD); itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1)); ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false); @@ -14,16 +13,16 @@ UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTi if (FileSize==INT64NDF || FileTime==NULL) { eprintf(L"\n"); - eprintf(St(MAskOverwrite),Name); + eprintf(St(MAskOverwrite),Name.c_str()); } else { itoa(FileSize,SizeText2,ASIZE(SizeText2)); FileTime->GetText(DateStr2,ASIZE(DateStr2),false); if ((Flags & UIASKREP_F_EXCHSRCDEST)==0) - eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2); + eprintf(St(MAskReplace),Name.c_str(),SizeText1,DateStr1,SizeText2,DateStr2); else - eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1); + eprintf(St(MAskReplace),Name.c_str(),SizeText2,DateStr2,SizeText1,DateStr1); } bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0; @@ -46,7 +45,7 @@ UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTi if (AllowRename && Choice==5) { mprintf(St(MAskNewName)); - if (getwstr(Name,MaxNameSize)) + if (getwstr(Name)) return UIASKREP_R_RENAME; else return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'. @@ -57,13 +56,13 @@ UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTi -void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName) { - mprintf(St(Extract ? MExtracting : MExtrTest), ArcName); + mprintf(St(Extract ? MExtracting : MExtrTest), ArcName.c_str()); } -bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +bool uiStartFileExtract(const std::wstring &FileName,bool Extract,bool Test,bool Skip) { return true; } @@ -225,6 +224,12 @@ void uiMsgStore::Msg() Log(Str[0],St(MNewerRAR)); break; #endif + case UIERROR_EXTRDICTOUTMEM: + Log(Str[0],St(MExtrDictOutMem),Num[0]); +#ifdef _WIN_32 + Log(Str[0],St(MSuggest64bit)); +#endif + break; case UIERROR_RECVOLDIFFSETS: Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); break; @@ -389,7 +394,6 @@ void uiMsgStore::Msg() break; - case UIEVENT_RRTESTINGSTART: mprintf(L"%s ",St(MTestingRR)); break; @@ -397,7 +401,7 @@ void uiMsgStore::Msg() } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, +bool uiGetPassword(UIPASSWORD_TYPE Type,const std::wstring &FileName, SecPassword *Password,CheckPassword *CheckPwd) { // Unlike GUI we cannot provide Cancel button here, so we use the empty @@ -433,14 +437,14 @@ void uiAlarm(UIALARM_TYPE Type) -bool uiAskNextVolume(wchar *VolName,size_t MaxSize) +bool uiAskNextVolume(std::wstring &VolName) { - eprintf(St(MAskNextVol),VolName); + eprintf(St(MAskNextVol),VolName.c_str()); return Ask(St(MContinueQuit))!=2; } -void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) +void uiAskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) { eprintf(St(MErrReadInfo)); int Code=Ask(St(MIgnoreAllRetryQuit)); @@ -452,14 +456,35 @@ void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bo } -bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull) +bool uiAskRepeatWrite(const std::wstring &FileName,bool DiskFull) { mprintf(L"\n"); - Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName); + Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName.c_str()); return Ask(St(MRetryAbort))==1; } +bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize) +{ + mprintf(L"\n%s",FileName.c_str()); + const uint64 GB=1024*1024*1024; // We display sizes in GB here. + + // Include the reminder for switches like -md6400m. + DictSize=DictSize/GB+(DictSize%GB!=0 ? 1:0); + + // Exclude reminder for in case it is less than archive dictionary size, + // but still more than nearest GB value. Otherwise we could have message + // like "7 GB dictionary exceeds 7 GB limit", where first 7 might be actually + // 6272 MB and second is 6400 MB. + MaxDictSize/=GB; + + mprintf(St(MDictNotAllowed),(uint)DictSize,(uint)MaxDictSize,(uint)DictSize); + mprintf(St(MDictExtrAnyway),(uint)DictSize,(uint)DictSize); + mprintf(L"\n"); + return false; // Stop extracting. +} + + #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month) { diff --git a/unrar/uisilent.cpp b/unrar/uisilent.cpp index 81558857..a1b49885 100644 --- a/unrar/uisilent.cpp +++ b/unrar/uisilent.cpp @@ -1,5 +1,5 @@ // Purely user interface function. Gets and returns user input. -UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags) { return UIASKREP_R_REPLACE; } @@ -7,12 +7,12 @@ UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTi -void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +void uiStartArchiveExtract(bool Extract,const std::wstring &ArcName) { } -bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +bool uiStartFileExtract(const std::wstring &FileName,bool Extract,bool Test,bool Skip) { return true; } @@ -33,7 +33,7 @@ void uiMsgStore::Msg() } -bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, +bool uiGetPassword(UIPASSWORD_TYPE Type,const std::wstring &FileName, SecPassword *Password,CheckPassword *CheckPwd) { return false; @@ -62,6 +62,17 @@ void uiGiveTick() } +bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize) +{ +#ifdef RARDLL + if (Cmd->Callback!=nullptr && + Cmd->Callback(UCM_LARGEDICT,Cmd->UserData,(LPARAM)(DictSize/1024),(LPARAM)(MaxDictSize/1024))==1) + return true; // Continue extracting if unrar.dll callback permits it. +#endif + return false; // Stop extracting. +} + + #ifndef SFX_MODULE const wchar *uiGetMonthName(int Month) { diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index 141a97fe..e501f96b 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -1,6 +1,6 @@ -static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) +static bool UnixSymlink(CommandData *Cmd,const std::string &Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { CreatePath(LinkName,true,Cmd->DisableNames); @@ -9,15 +9,15 @@ static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkNam // called earlier inside of uiAskReplaceEx. DelFile(LinkName); - char LinkNameA[NM]; - WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); - if (symlink(Target,LinkNameA)==-1) // Error. + std::string LinkNameA; + WideToChar(LinkName,LinkNameA); + if (symlink(Target.c_str(),LinkNameA.c_str())==-1) // Error. { if (errno==EEXIST) uiMsg(UIERROR_ULINKEXIST,LinkName); else { - uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName); + uiMsg(UIERROR_SLINKCREATE,L"",LinkName); ErrHandler.SetErrorCode(RARX_WARNING); } return false; @@ -29,14 +29,14 @@ static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkNam times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; times[1].tv_sec=ftm->GetUnix(); times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; - utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW); + utimensat(AT_FDCWD,LinkNameA.c_str(),times,AT_SYMLINK_NOFOLLOW); #else struct timeval tv[2]; tv[0].tv_sec=fta->GetUnix(); tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000); tv[1].tv_sec=ftm->GetUnix(); tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000); - lutimes(LinkNameA,tv); + lutimes(LinkNameA.c_str(),tv); #endif #endif @@ -55,9 +55,9 @@ static bool IsFullPath(const char *PathA) // Unix ASCII version. // it didn't affect the number of path related characters we analyze // in IsRelativeSymlinkSafe later. // This check is likely to be excessive, but let's keep it anyway. -static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize) +static bool SafeCharToWide(const std::string &Src,std::wstring &Dest) { - if (!CharToWide(Src,Dest,DestSize) || *Dest==0) + if (!CharToWide(Src,Dest) || Dest.empty()) return false; uint SrcChars=0,DestChars=0; for (uint I=0;Src[I]!=0;I++) @@ -73,18 +73,18 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize) static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, const wchar *LinkName,bool &UpLink) { - char Target[NM]; if (IsLink(Arc.FileHead.FileAttr)) { size_t DataSize=(size_t)Arc.FileHead.PackSize; - if (DataSize>ASIZE(Target)-1) + if (DataSize>MAXPATHSIZE) return false; - if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize) + std::vector TargetBuf(DataSize+1); + if ((size_t)DataIO.UnpRead((byte*)TargetBuf.data(),DataSize)!=DataSize) return false; - Target[DataSize]=0; + std::string Target(TargetBuf.data(),TargetBuf.size()); DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); - DataIO.UnpHash.Update(Target,strlen(Target)); + DataIO.UnpHash.Update(Target.data(),strlen(Target.data())); DataIO.UnpHash.Result(&Arc.FileHead.FileHash); // Return true in case of bad checksum, so link will be processed further @@ -92,16 +92,17 @@ static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL)) return true; - wchar TargetW[NM]; - if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + std::wstring TargetW; + if (!SafeCharToWide(Target.data(),TargetW)) return false; + TruncateAtZero(TargetW); // Use Arc.FileHead.FileName instead of LinkName, since LinkName // can include the destination path as a prefix, which can // confuse IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || - !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) + !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName.c_str(),LinkName,TargetW.c_str()))) return false; - UpLink=strstr(Target,"..")!=NULL; + UpLink=Target.find("..")!=std::string::npos; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } return false; @@ -110,27 +111,28 @@ static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) { - char Target[NM]; - WideToChar(hd->RedirName,Target,ASIZE(Target)); + std::string Target; + WideToChar(hd->RedirName,Target); if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) { // Cannot create Windows absolute path symlinks in Unix. Only relative path // Windows symlinks can be created here. RAR 5.0 used \??\ prefix // for Windows absolute symlinks, since RAR 5.1 /??/ is used. // We escape ? as \? to avoid "trigraph" warning - if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0) + if (Target.rfind("\\??\\",0)!=std::string::npos || + Target.rfind("/\?\?/",0)!=std::string::npos) return false; - DosSlashToUnix(Target,Target,ASIZE(Target)); + DosSlashToUnix(Target,Target); } - wchar TargetW[NM]; - if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + std::wstring TargetW; + if (!SafeCharToWide(Target,TargetW)) return false; // Use hd->FileName instead of LinkName, since LinkName can include // the destination path as a prefix, which can confuse // IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || - !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW))) + !IsRelativeSymlinkSafe(Cmd,hd->FileName.c_str(),Name,TargetW.c_str()))) return false; return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index 73b09bb2..997f8b2b 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -128,6 +128,26 @@ bool CharToWide(const char *Src,wchar *Dest,size_t DestSize) } +bool WideToChar(const std::wstring &Src,std::string &Dest) +{ + // We need more than 1 char per wchar_t for DBCS and up to 4 for UTF-8. + std::vector DestA(4*Src.size()+1); // "+1" for terminating zero. + bool Result=WideToChar(Src.c_str(),DestA.data(),DestA.size()); + Dest=DestA.data(); + return Result; +} + + +bool CharToWide(const std::string &Src,std::wstring &Dest) +{ + // 2 wchar_t per char in case char is converted to UTF-16 surrogate pair. + std::vector DestW(2*Src.size()+1); // "+1" for terminating zero. + bool Result=CharToWide(Src.c_str(),DestW.data(),DestW.size()); + Dest=DestW.data(); + return Result; +} + + #if defined(_UNIX) && defined(MBFUNCTIONS) // Convert and restore mapped inconvertible Unicode characters. // We use it for extended ASCII names in Unix. @@ -244,6 +264,19 @@ byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize) } +// Store UTF-16 raw byte stream. +void WideToRaw(const std::wstring &Src,std::vector &Dest) +{ + for (wchar C : Src) + { + 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); +} + + wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) { for (size_t I=0;I &Src) +{ + std::wstring Dest; + for (size_t I=0;I+1>6)); + Dest.push_back(0x80|(c&0x3f)); + } + else + { + if (c>=0xd800 && c<=0xdbff && I=0xdc00 && Src[I]<=0xdfff) // Surrogate pair. + { + c=((c-0xd800)<<10)+(Src[I]-0xdc00)+0x10000; + I++; + } + if (c<0x10000) + { + Dest.push_back(0xe0|(c>>12)); + Dest.push_back(0x80|((c>>6)&0x3f)); + Dest.push_back(0x80|(c&0x3f)); + } + else + if (c < 0x200000) + { + Dest.push_back(0xf0|(c>>18)); + Dest.push_back(0x80|((c>>12)&0x3f)); + Dest.push_back(0x80|((c>>6)&0x3f)); + Dest.push_back(0x80|(c&0x3f)); + } + } + } +} + + + size_t WideToUtfSize(const wchar *Src) { size_t Size=0; @@ -397,6 +484,146 @@ bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize) } +bool UtfToWide(const char *Src,std::wstring &Dest) +{ + bool Success=true; + Dest.clear(); + while (*Src!=0) + { + uint c=byte(*(Src++)),d; + if (c<0x80) + d=c; + else + if ((c>>5)==6) + { + if ((*Src&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0x1f)<<6)|(*Src&0x3f); + Src++; + } + else + if ((c>>4)==14) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); + Src+=2; + } + else + if ((c>>3)==30) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); + Src+=3; + } + else + { + Success=false; + break; + } + if (d>0xffff) + { + if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. + { + Success=false; + continue; + } + if (sizeof(wchar_t)==2) // Use the surrogate pair. + { + Dest.push_back( ((d-0x10000)>>10)+0xd800 ); + Dest.push_back( (d&0x3ff)+0xdc00 ); + } + else + Dest.push_back( d ); + } + else + Dest.push_back( d ); + } + return Success; +} + + +/* +bool UtfToWide(const std::vector &Src,std::wstring &Dest) +{ + bool Success=true; + Dest.clear(); + for (size_t I=0;I>5)==6) + { + if (Src.size()-I<1 || (Src[I]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0x1f)<<6)|(Src[I]&0x3f); + I++; + } + else + if ((c>>4)==14) + { + if (Src.size()-I<2 || (Src[I]&0xc0)!=0x80 || (Src[I+1]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0xf)<<12)|((Src[I]&0x3f)<<6)|(Src[I+1]&0x3f); + I+=2; + } + else + if ((c>>3)==30) + { + if (Src.size()-I<3 || (Src[I]&0xc0)!=0x80 || (Src[I+1]&0xc0)!=0x80 || (Src[I+2]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&7)<<18)|((Src[I]&0x3f)<<12)|((Src[I+1]&0x3f)<<6)|(Src[I+2]&0x3f); + I+=3; + } + else + { + Success=false; + break; + } + if (d>0xffff) + { + if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. + { + Success=false; + continue; + } + if (sizeof(Dest[0])==2) // Use the surrogate pair. + { + Dest.push_back( ((d-0x10000)>>10)+0xd800 ); + Dest.push_back( (d&0x3ff)+0xdc00 ); + } + else + Dest.push_back( d ); + } + else + Dest.push_back( d ); + } + return Success; +} +*/ + + // For zero terminated strings. bool IsTextUtf8(const byte *Src) { @@ -483,7 +710,15 @@ const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) if (tolowerw(str[i+j])!=tolowerw(search[j])) break; } - return NULL; + return nullptr; +} + + +// Case insensitive std::wstring substring search. +std::wstring::size_type wcscasestr(const std::wstring &str, const std::wstring &search) +{ + const wchar *Found=wcscasestr(str.c_str(),search.c_str()); + return Found==nullptr ? std::wstring::npos : Found-str.c_str(); } @@ -500,10 +735,14 @@ wchar* wcslower(wchar *s) #endif return s; } -#endif -#ifndef SFX_MODULE +void wcslower(std::wstring &s) +{ + wcslower(&s[0]); +} + + wchar* wcsupper(wchar *s) { #ifdef _WIN_ALL @@ -516,6 +755,12 @@ wchar* wcsupper(wchar *s) #endif return s; } + + +void wcsupper(std::wstring &s) +{ + wcsupper(&s[0]); +} #endif @@ -548,27 +793,28 @@ int tolowerw(int ch) } -int atoiw(const wchar *s) +int atoiw(const std::wstring &s) { return (int)atoilw(s); } -int64 atoilw(const wchar *s) +int64 atoilw(const std::wstring &s) { bool sign=false; - if (*s=='-') // We do use signed integers here, for example, in GUI SFX. + size_t Pos=0; + if (s[Pos]=='-') // We do use signed integers here, for example, in GUI SFX. { - s++; + Pos++; sign=true; } // Use unsigned type here, since long string can overflow the variable // and signed integer overflow is undefined behavior in C++. uint64 n=0; - while (*s>='0' && *s<='9') + while (s[Pos]>='0' && s[Pos]<='9') { - n=n*10+(*s-'0'); - s++; + n=n*10+(s[Pos]-'0'); + Pos++; } // Check int64(n)>=0 to avoid the signed overflow with undefined behavior // when negating 0x8000000000000000. diff --git a/unrar/unicode.hpp b/unrar/unicode.hpp index 9bfd9c5d..2d867b3a 100644 --- a/unrar/unicode.hpp +++ b/unrar/unicode.hpp @@ -7,25 +7,37 @@ bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); +bool WideToChar(const std::wstring &Src,std::string &Dest); +bool CharToWide(const std::string &Src,std::wstring &Dest); byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize); +void WideToRaw(const std::wstring &Src,std::vector &Dest); wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); +std::wstring RawToWide(const std::vector &Src); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); +void WideToUtf(const std::wstring &Src,std::string &Dest); size_t WideToUtfSize(const wchar *Src); bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize); +bool UtfToWide(const char *Src,std::wstring &Dest); +//bool UtfToWide(const std::vector &Src,std::wstring &Dest); bool IsTextUtf8(const byte *Src); bool IsTextUtf8(const byte *Src,size_t SrcSize); int wcsicomp(const wchar *s1,const wchar *s2); +inline int wcsicomp(const std::wstring &s1,const std::wstring &s2) {return wcsicomp(s1.c_str(),s2.c_str());} int wcsnicomp(const wchar *s1,const wchar *s2,size_t n); +inline int wcsnicomp(const std::wstring &s1,const std::wstring &s2,size_t n) {return wcsnicomp(s1.c_str(),s2.c_str(),n);} const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search); +std::wstring::size_type wcscasestr(const std::wstring &str, const std::wstring &search); #ifndef SFX_MODULE wchar* wcslower(wchar *s); +void wcslower(std::wstring &s); wchar* wcsupper(wchar *s); +void wcsupper(std::wstring &s); #endif int toupperw(int ch); int tolowerw(int ch); -int atoiw(const wchar *s); -int64 atoilw(const wchar *s); +int atoiw(const std::wstring &s); +int64 atoilw(const std::wstring &s); #ifdef DBCS_SUPPORTED class SupportDBCS diff --git a/unrar/unpack.cpp b/unrar/unpack.cpp index 9236e748..1c48b395 100644 --- a/unrar/unpack.cpp +++ b/unrar/unpack.cpp @@ -10,8 +10,8 @@ #ifndef SFX_MODULE #include "unpack15.cpp" #include "unpack20.cpp" -#endif #include "unpack30.cpp" +#endif #include "unpack50.cpp" #include "unpack50frag.cpp" @@ -24,6 +24,7 @@ Unpack::Unpack(ComprDataIO *DataIO) Suspended=false; UnpAllBuf=false; UnpSomeRead=false; + ExtraDist=false; #ifdef RAR_SMP MaxUserThreads=1; UnpThreadPool=NULL; @@ -47,7 +48,9 @@ Unpack::Unpack(ComprDataIO *DataIO) Unpack::~Unpack() { +#ifndef SFX_MODULE InitFilters30(false); +#endif if (Window!=NULL) free(Window); @@ -70,13 +73,11 @@ void Unpack::SetThreads(uint Threads) #endif -void Unpack::Init(size_t WinSize,bool Solid) +// We get 64-bit WinSize, so we still can check and quit for dictionaries +// exceeding 4 GB in 32-bit builds. Then we convert WinSize to size_t +// MaxWinSize. +void Unpack::Init(uint64 WinSize,bool Solid) { - // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size - // will be 0 because of size_t overflow. Let's issue the memory error. - if (WinSize==0) - ErrHandler.MemoryError(); - // Minimum window size must be at least twice more than maximum possible // size of filter block, which is 0x10000 in RAR now. If window size is // smaller, we can have a block with never cleared flt->NextWindow flag @@ -88,8 +89,12 @@ void Unpack::Init(size_t WinSize,bool Solid) if (WinSize<=MaxWinSize) // Use the already allocated window. return; - if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB. - return; + if (WinSize>0x10000000000ULL) // Window size must not exceed 1 TB. + throw std::bad_alloc(); + + // 32-bit build can't unpack dictionaries exceeding 32-bit. + if (WinSize>=0x100000000 && sizeof(size_t)<=4) + throw std::bad_alloc(); // Archiving code guarantees that window size does not grow in the same // solid stream. So if we are here, we are either creating a new window @@ -102,7 +107,7 @@ void Unpack::Init(size_t WinSize,bool Solid) if (Grow && Fragmented) throw std::bad_alloc(); - byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize); + byte *NewWindow=Fragmented ? NULL : (byte *)malloc((size_t)WinSize); if (NewWindow==NULL) if (Grow || WinSize<0x1000000) @@ -118,7 +123,7 @@ void Unpack::Init(size_t WinSize,bool Solid) free(Window); Window=NULL; } - FragWindow.Init(WinSize); + FragWindow.Init((size_t)WinSize); Fragmented=true; } @@ -126,7 +131,7 @@ void Unpack::Init(size_t WinSize,bool Solid) { // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access unused areas of sliding dictionary. - memset(NewWindow,0,WinSize); + memset(NewWindow,0,(size_t)WinSize); // If Window is not NULL, it means that window size has grown. // In solid streams we need to copy data to a new window in such case. @@ -141,7 +146,7 @@ void Unpack::Init(size_t WinSize,bool Solid) Window=NewWindow; } - MaxWinSize=WinSize; + MaxWinSize=(size_t)WinSize; MaxWinMask=MaxWinSize-1; } @@ -154,21 +159,23 @@ void Unpack::DoUnpack(uint Method,bool Solid) switch(Method) { #ifndef SFX_MODULE - case 15: // rar 1.5 compression + case 15: // RAR 1.5 compression. if (!Fragmented) Unpack15(Solid); break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB + case 20: // RAR 2.x compression. + case 26: // Files larger than 2GB. if (!Fragmented) Unpack20(Solid); break; -#endif - case 29: // rar 3.x compression + case 29: // RAR 3.x compression. if (!Fragmented) Unpack29(Solid); break; - case 50: // RAR 5.0 compression algorithm. +#endif + case 50: // RAR 5.0 and 7.0 compression algorithms. + case 70: + ExtraDist=(Method==70); #ifdef RAR_SMP if (MaxUserThreads>1) { @@ -215,8 +222,8 @@ void Unpack::UnpInitData(bool Solid) BlockHeader.BlockSize=-1; // '-1' means not defined yet. #ifndef SFX_MODULE UnpInitData20(Solid); -#endif UnpInitData30(Solid); +#endif UnpInitData50(Solid); } diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index 737b31c2..c807a1ef 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -2,7 +2,7 @@ #define _RAR_UNPACK_ // Maximum allowed number of compressed bits processed in quick mode. -#define MAX_QUICK_DECODE_BITS 10 +#define MAX_QUICK_DECODE_BITS 9 // Maximum number of filters per entire data block. Must be at least // twice more than MAX_PACK_FILTERS to store filters from two data blocks. @@ -102,7 +102,7 @@ struct UnpackDecodedItem ushort Length; union { - uint Distance; + size_t Distance; byte Literal[8]; // Store up to 8 chars here to speed up extraction. }; }; @@ -143,13 +143,13 @@ struct UnpackThreadData struct UnpackFilter { + // Groop 'byte' and 'bool' together to reduce the actual struct size. byte Type; - uint BlockStart; - uint BlockLength; byte Channels; -// uint Width; -// byte PosR; bool NextWindow; + + size_t BlockStart; + uint BlockLength; }; @@ -193,7 +193,7 @@ class FragmentedWindow ~FragmentedWindow(); void Init(size_t WinSize); byte& operator [](size_t Item); - void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask); + void CopyString(uint Length,size_t Distance,size_t &UnpPtr,size_t MaxWinSize); void CopyData(byte *Dest,size_t WinPos,size_t Size); size_t GetBlockSize(size_t StartPos,size_t RequiredSize); }; @@ -216,10 +216,9 @@ class Unpack:PackDef bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables); void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size); _forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec); - void CopyString(); - inline void InsertOldDist(unsigned int Distance); + inline void InsertOldDist(size_t Distance); void UnpInitData(bool Solid); - _forceinline void CopyString(uint Length,uint Distance); + _forceinline void CopyString(uint Length,size_t Distance); uint ReadFilterData(BitInput &Inp); bool ReadFilter(BitInput &Inp,UnpackFilter &Filter); bool AddFilter(UnpackFilter &Filter); @@ -240,20 +239,22 @@ class Unpack:PackDef byte *ReadBufMT; #endif - Array FilterSrcMemory; - Array FilterDstMemory; + std::vector FilterSrcMemory; + std::vector FilterDstMemory; // Filters code, one entry per filter. - Array Filters; + std::vector Filters; - uint OldDist[4],OldDistPtr; + size_t OldDist[4],OldDistPtr; uint LastLength; // LastDist is necessary only for RAR2 and older with circular OldDist // array. In RAR3 last distance is always stored in OldDist[0]. uint LastDist; - size_t UnpPtr,WrPtr; + size_t UnpPtr; // Current position in window. + + size_t WrPtr; // Last written unpacked data position. // Top border of read packed data. int ReadTop; @@ -266,7 +267,7 @@ class Unpack:PackDef UnpackBlockHeader BlockHeader; UnpackBlockTables BlockTables; - size_t WriteBorder; + size_t WriteBorder; // Perform write when reaching this border. byte *Window; @@ -289,7 +290,7 @@ class Unpack:PackDef void LongLZ(); void HuffDecode(); void GetFlagsBuf(); - void UnpInitData15(int Solid); + void UnpInitData15(bool Solid); void InitHuff(); void CorrHuff(ushort *CharSet,byte *NumToPlace); void CopyString15(uint Distance,uint Length); @@ -359,15 +360,15 @@ class Unpack:PackDef BitInput VMCodeInp; // Filters code, one entry per filter. - Array Filters30; + std::vector Filters30; // Filters stack, several entrances of same filter are possible. - Array PrgStack; + std::vector PrgStack; // Lengths of preceding data blocks, one length of one last block // for every filter. Used to reduce the size required to write // the data block length if lengths are repeating. - Array OldFilterLengths; + std::vector OldFilterLengths; int LastFilter; /***************************** Unpack v 3.0 *********************************/ @@ -375,9 +376,9 @@ class Unpack:PackDef public: Unpack(ComprDataIO *DataIO); ~Unpack(); - void Init(size_t WinSize,bool Solid); + void Init(uint64 WinSize,bool Solid); void DoUnpack(uint Method,bool Solid); - bool IsFileExtracted() {return(FileExtracted);} + bool IsFileExtracted() {return FileExtracted;} void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} @@ -389,6 +390,8 @@ class Unpack:PackDef size_t MaxWinSize; size_t MaxWinMask; + bool ExtraDist; // Allow distances up to 1 TB. + uint GetChar() { if (Inp.InAddr>BitInput::MAX_SIZE-30) @@ -399,6 +402,30 @@ class Unpack:PackDef } return Inp.InBuf[Inp.InAddr++]; } + + + // If window position crosses the window beginning, wrap it to window end. + // Replaces &MaxWinMask for non-power 2 window sizes. + // We can't use %WinSize everywhere not only for performance reason, + // but also because C++ % is reminder instead of modulo. + // We need additional checks in the code if WinPos distance from 0 + // can exceed MaxWinSize. Alternatively we could add such check here. + inline size_t WrapDown(size_t WinPos) + { + return WinPos >= MaxWinSize ? WinPos + MaxWinSize : WinPos; + } + + // If window position crosses the window end, wrap it to window beginning. + // Replaces &MaxWinMask for non-power 2 window sizes. + // Unlike WrapDown, we can use %WinSize here if there was no size_t + // overflow when calculating WinPos. + // We need additional checks in the code if WinPos distance from MaxWinSize + // can be MaxWinSize or more. Alternatively we could add such check here + // or use %WinSize. + inline size_t WrapUp(size_t WinPos) + { + return WinPos >= MaxWinSize ? WinPos - MaxWinSize : WinPos; + } }; #endif diff --git a/unrar/unpack15.cpp b/unrar/unpack15.cpp index 1e7cf76c..377fd847 100644 --- a/unrar/unpack15.cpp +++ b/unrar/unpack15.cpp @@ -185,7 +185,7 @@ void Unpack::ShortLZ() LCount=0; SaveLength=Length; - Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; + Distance=(uint)OldDist[(OldDistPtr-(Length-9)) & 3]; Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2; if (Length==0x101 && SaveLength==10) { @@ -424,7 +424,7 @@ void Unpack::GetFlagsBuf() } -void Unpack::UnpInitData15(int Solid) +void Unpack::UnpInitData15(bool Solid) { if (!Solid) { diff --git a/unrar/unpack20.cpp b/unrar/unpack20.cpp index 93c8ba05..38a045cc 100644 --- a/unrar/unpack20.cpp +++ b/unrar/unpack20.cpp @@ -2,7 +2,8 @@ void Unpack::CopyString20(uint Length,uint Distance) { - LastDist=OldDist[OldDistPtr++]=Distance; + LastDist=Distance; + OldDist[OldDistPtr++]=Distance; OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0. LastLength=Length; DestUnpSize-=Length; @@ -109,7 +110,7 @@ void Unpack::Unpack20(bool Solid) } if (Number<261) { - uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; + uint Distance=(uint)OldDist[(OldDistPtr-(Number-256)) & 3]; uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); uint Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) diff --git a/unrar/unpack30.cpp b/unrar/unpack30.cpp index 7c2adfab..bb5dce0d 100644 --- a/unrar/unpack30.cpp +++ b/unrar/unpack30.cpp @@ -17,8 +17,8 @@ void Unpack::Unpack29(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - static int DDecode[DC]; - static byte DBits[DC]; + static int DDecode[DC30]; + static byte DBits[DC30]; static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; @@ -219,7 +219,7 @@ void Unpack::Unpack29(bool Solid) if (Number<263) { uint DistNum=Number-259; - uint Distance=OldDist[DistNum]; + uint Distance=(uint)OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; @@ -306,7 +306,7 @@ bool Unpack::ReadVMCode() } if (Length==0) return false; - Array VMCode(Length); + std::vector VMCode(Length); for (uint I=0;I>8; Inp.addbits(8); } - return AddVMCode(FirstByte,&VMCode[0],Length); + return AddVMCode(FirstByte,VMCode.data(),Length); } @@ -346,7 +346,7 @@ bool Unpack::ReadVMCodePPM() } if (Length==0) return false; - Array VMCode(Length); + std::vector VMCode(Length); for (uint I=0;IFilters30.Size() || FiltPos>OldFilterLengths.Size()) + if (FiltPos>Filters30.size() || FiltPos>OldFilterLengths.size()) return false; LastFilter=FiltPos; - bool NewFilter=(FiltPos==Filters30.Size()); + bool NewFilter=(FiltPos==Filters30.size()); UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. @@ -393,15 +393,15 @@ bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) return false; } - Filters30.Add(1); - Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; - StackFilter->ParentFilter=(uint)(Filters30.Size()-1); + StackFilter->ParentFilter=(uint)Filters30.size(); + Filter=new UnpackFilter30; + Filters30.push_back(Filter); // Reserve one item to store the data block length of our new filter // entry. We'll set it to real block length below, after reading it. // But we need to initialize it now, because when processing corrupt // data, we can access this item even before we set it to real value. - OldFilterLengths.Push(0); + OldFilterLengths.push_back(0); } else // Filter was used in the past. { @@ -410,7 +410,7 @@ bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) } uint EmptyCount=0; - for (uint I=0;IMAX3_UNPACK_FILTERS) + if (PrgStack.size()>MAX3_UNPACK_FILTERS) { delete StackFilter; return false; } - PrgStack.Add(1); + PrgStack.resize(PrgStack.size()+1); EmptyCount=1; } - size_t StackPos=PrgStack.Size()-EmptyCount; + size_t StackPos=PrgStack.size()-EmptyCount; PrgStack[StackPos]=StackFilter; uint BlockStart=RarVM::ReadData(VMCodeInp); @@ -448,7 +448,7 @@ bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) // for same filter. It is possible for corrupt data to access a new // and not filled yet item of OldFilterLengths array here. This is why // we set new OldFilterLengths items to zero above. - StackFilter->BlockLength=FiltPosBlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; @@ -472,7 +472,7 @@ bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) uint VMCodeSize=RarVM::ReadData(VMCodeInp); if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) return false; - Array VMCode(VMCodeSize); + std::vector VMCode(VMCodeSize); for (uint I=0;I>8; VMCodeInp.faddbits(8); } - VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + VM.Prepare(VMCode.data(),VMCodeSize,&Filter->Prg); } StackFilter->Prg.Type=Filter->Prg.Type; @@ -520,7 +520,7 @@ void Unpack::UnpWriteBuf30() { uint WrittenBorder=(uint)WrPtr; uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); - for (size_t I=0;IFilteredDataSize; delete PrgStack[I]; - PrgStack[I]=NULL; - while (I+1FilteredDataSize; I++; delete PrgStack[I]; - PrgStack[I]=NULL; + PrgStack[I]=nullptr; } UnpIO->UnpWrite(FilteredData,FilteredDataSize); UnpSomeRead=true; @@ -601,10 +601,10 @@ void Unpack::UnpWriteBuf30() { // Current filter intersects the window write border, so we adjust // the window border to process this filter next time, not now. - for (size_t J=I;JNextWindow) + if (flt!=nullptr && flt->NextWindow) flt->NextWindow=false; } WrPtr=WrittenBorder; @@ -752,14 +752,14 @@ void Unpack::InitFilters30(bool Solid) { if (!Solid) { - OldFilterLengths.SoftReset(); + OldFilterLengths.clear(); LastFilter=0; - for (size_t I=0;I=ReadBorder) { @@ -42,7 +42,7 @@ void Unpack::Unpack5(bool Solid) break; } - if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); if (WrittenFileSize>DestUnpSize) @@ -67,7 +67,8 @@ void Unpack::Unpack5(bool Solid) { uint Length=SlotToLength(Inp,MainSlot-262); - uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + size_t Distance=1; + uint DBits,DistSlot=DecodeNumber(Inp,&BlockTables.DD); if (DistSlot<4) { DBits=0; @@ -76,7 +77,7 @@ void Unpack::Unpack5(bool Solid) else { DBits=DistSlot/2 - 1; - Distance+=(2 | (DistSlot & 1)) << DBits; + Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) @@ -85,7 +86,11 @@ void Unpack::Unpack5(bool Solid) { if (DBits>4) { - Distance+=((Inp.getbits32()>>(36-DBits))<<4); + // It is also possible to always use getbits64() here. + if (DBits>36) + Distance+=( ( size_t(Inp.getbits64() ) >> (68-DBits) ) << 4 ); + else + Distance+=( ( size_t(Inp.getbits32() ) >> (36-DBits) ) << 4 ); Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); @@ -112,7 +117,7 @@ void Unpack::Unpack5(bool Solid) InsertOldDist(Distance); LastLength=Length; if (Fragmented) - FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinSize); else CopyString(Length,Distance); continue; @@ -128,7 +133,7 @@ void Unpack::Unpack5(bool Solid) { if (LastLength!=0) if (Fragmented) - FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); + FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinSize); else CopyString(LastLength,OldDist[0]); continue; @@ -136,7 +141,7 @@ void Unpack::Unpack5(bool Solid) if (MainSlot<262) { uint DistNum=MainSlot-258; - uint Distance=OldDist[DistNum]; + size_t Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; @@ -145,7 +150,7 @@ void Unpack::Unpack5(bool Solid) uint Length=SlotToLength(Inp,LengthSlot); LastLength=Length; if (Fragmented) - FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinSize); else CopyString(Length,Distance); continue; @@ -196,20 +201,25 @@ bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter) bool Unpack::AddFilter(UnpackFilter &Filter) { - if (Filters.Size()>=MAX_UNPACK_FILTERS) + if (Filters.size()>=MAX_UNPACK_FILTERS) { UnpWriteBuf(); // Write data, apply and flush filters. - if (Filters.Size()>=MAX_UNPACK_FILTERS) + if (Filters.size()>=MAX_UNPACK_FILTERS) InitFilters(); // Still too many filters, prevent excessive memory use. } // If distance to filter start is that large that due to circular dictionary // mode now it points to old not written yet data, then we set 'NextWindow' // flag and process this filter only after processing that older data. - Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; - - Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); - Filters.Push(Filter); + Filter.NextWindow=WrPtr!=UnpPtr && WrapDown(WrPtr-UnpPtr)<=Filter.BlockStart; + + // In malformed archive Filter.BlockStart can be many times larger + // than window size, so here we must use the reminder instead of + // subtracting the single window size as WrapUp can do. So the result + // is always within the window. Since we add and not subtract here, + // reminder always provides the valid result in valid archives. + Filter.BlockStart=(Filter.BlockStart+UnpPtr)%MaxWinSize; + Filters.push_back(Filter); return true; } @@ -255,10 +265,10 @@ bool Unpack::UnpReadBuf() void Unpack::UnpWriteBuf() { size_t WrittenBorder=WrPtr; - size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; + size_t FullWriteSize=WrapDown(UnpPtr-WrittenBorder); size_t WriteSizeLeft=FullWriteSize; bool NotAllFiltersProcessed=false; - for (size_t I=0;IBlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) + // to next block and no further wrap arounds is possible. + if (WrapDown(flt->BlockStart-WrPtr)<=FullWriteSize) flt->NextWindow=false; continue; } - uint BlockStart=flt->BlockStart; + size_t BlockStart=flt->BlockStart; uint BlockLength=flt->BlockLength; - if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. { - uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + size_t BlockEnd=WrapUp(BlockStart+BlockLength); - FilterSrcMemory.Alloc(BlockLength); - byte *Mem=&FilterSrcMemory[0]; + FilterSrcMemory.resize(BlockLength); + byte *Mem=FilterSrcMemory.data(); if (BlockStartType!=FILTER_NONE) @@ -365,7 +375,7 @@ void Unpack::UnpWriteBuf() // Remove processed filters from queue. size_t EmptyCount=0; - for (size_t I=0;I0) Filters[I-EmptyCount]=Filters[I]; @@ -373,7 +383,7 @@ void Unpack::UnpWriteBuf() EmptyCount++; } if (EmptyCount>0) - Filters.Alloc(Filters.Size()-EmptyCount); + Filters.resize(Filters.size()-EmptyCount); if (!NotAllFiltersProcessed) // Only if all filters are processed. { @@ -385,12 +395,12 @@ void Unpack::UnpWriteBuf() // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE // instead of potentially huge MaxWinSize blocks. It also allows us // to keep the size of Filters array reasonable. - WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; + WriteBorder=WrapUp(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE)); // Choose the nearest among WriteBorder and WrPtr actual written border. // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. if (WriteBorder==UnpPtr || - WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) + WrPtr!=UnpPtr && WrapDown(WrPtr-UnpPtr)Channels,SrcPos=0; - FilterDstMemory.Alloc(DataSize); - byte *DstData=&FilterDstMemory[0]; + FilterDstMemory.resize(DataSize); + byte *DstData=FilterDstMemory.data(); // Bytes from same channels are grouped to continual data blocks, // so we need to place them back to their interleaving positions. @@ -492,13 +502,13 @@ void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr) if (Fragmented) { - size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask; + size_t SizeToWrite=WrapDown(EndPtr-StartPtr); while (SizeToWrite>0) { size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); UnpWriteData(&FragWindow[StartPtr],BlockSize); SizeToWrite-=BlockSize; - StartPtr=(StartPtr+BlockSize) & MaxWinMask; + StartPtr=WrapUp(StartPtr+BlockSize); } } else @@ -572,6 +582,7 @@ bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) Header.LastBlockInFile=(BlockFlags & 0x40)!=0; Header.TablePresent=(BlockFlags & 0x80)!=0; + return true; } @@ -610,8 +621,8 @@ bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTable MakeDecodeTables(BitLength,&Tables.BD,BC); - byte Table[HUFF_TABLE_SIZE]; - const uint TableSize=HUFF_TABLE_SIZE; + byte Table[HUFF_TABLE_SIZEX]; + const uint TableSize=ExtraDist ? HUFF_TABLE_SIZEX:HUFF_TABLE_SIZEB; for (uint I=0;IReadTop-5) @@ -674,14 +685,15 @@ bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTable if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) return false; MakeDecodeTables(&Table[0],&Tables.LD,NC); - MakeDecodeTables(&Table[NC],&Tables.DD,DC); - MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); - MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + uint DCodes=ExtraDist ? DCX : DCB; + MakeDecodeTables(&Table[NC],&Tables.DD,DCodes); + MakeDecodeTables(&Table[NC+DCodes],&Tables.LDD,LDC); + MakeDecodeTables(&Table[NC+DCodes+LDC],&Tables.RD,RC); return true; } void Unpack::InitFilters() { - Filters.SoftReset(); + Filters.clear(); } diff --git a/unrar/unpack50frag.cpp b/unrar/unpack50frag.cpp index 3c008ff2..b318ebf0 100644 --- a/unrar/unpack50frag.cpp +++ b/unrar/unpack50frag.cpp @@ -74,16 +74,22 @@ byte& FragmentedWindow::operator [](size_t Item) } -void FragmentedWindow::CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask) +void FragmentedWindow::CopyString(uint Length,size_t Distance,size_t &UnpPtr,size_t MaxWinSize) { size_t SrcPtr=UnpPtr-Distance; - while (Length-- > 0) - { - (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; - // We need to have masked UnpPtr after quit from loop, so it must not - // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' - UnpPtr=(UnpPtr+1) & MaxWinMask; - } + if (SrcPtr>=MaxWinSize) + SrcPtr+=MaxWinSize; + // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize + // in a malformed archive. + if (SrcPtr 0) + { + (*this)[UnpPtr]=(*this)[SrcPtr]; + if (++SrcPtr>=MaxWinSize) + SrcPtr-=MaxWinSize; + if (++UnpPtr>=MaxWinSize) + UnpPtr-=MaxWinSize; + } } diff --git a/unrar/unpack50mt.cpp b/unrar/unpack50mt.cpp index 82c9c4a8..baed0e07 100644 --- a/unrar/unpack50mt.cpp +++ b/unrar/unpack50mt.cpp @@ -1,3 +1,5 @@ +// 2023.09.09: 0x400000 and 2 are optimal for i9-12900K. +// Further increasing the buffer size reduced the extraction speed. #define UNP_READ_SIZE_MT 0x400000 #define UNP_BLOCKS_PER_THREAD 2 @@ -277,7 +279,7 @@ void Unpack::Unpack5MT(bool Solid) } } } - UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here. + UnpPtr=WrapUp(UnpPtr); // ProcessDecoded and maybe others can leave UnpPtr >= MaxWinSize here. UnpWriteBuf(); BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; @@ -362,7 +364,8 @@ void Unpack::UnpackDecode(UnpackThreadData &D) { uint Length=SlotToLength(D.Inp,MainSlot-262); - uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + size_t Distance=1; + uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; @@ -371,7 +374,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D) else { DBits=DistSlot/2 - 1; - Distance+=(2 | (DistSlot & 1)) << DBits; + Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) @@ -380,7 +383,11 @@ void Unpack::UnpackDecode(UnpackThreadData &D) { if (DBits>4) { - Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + // It is also possible to always use getbits64() here. + if (DBits>36) + Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); + else + Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); @@ -450,8 +457,8 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; while (ItemDestUnpSize) @@ -469,7 +476,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) else #endif for (uint I=0;I<=Item->Length;I++) - Window[UnpPtr++ & MaxWinMask]=Item->Literal[I]; + Window[WrapUp(UnpPtr++)]=Item->Literal[I]; } else if (Item->Type==UNPDT_MATCH) @@ -481,8 +488,8 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) else if (Item->Type==UNPDT_REP) { - uint Distance=OldDist[Item->Distance]; - for (uint I=Item->Distance;I>0;I--) + size_t Distance=OldDist[Item->Distance]; + for (size_t I=Item->Distance;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; LastLength=Item->Length; @@ -505,7 +512,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) Item++; Filter.Channels=(byte)Item->Length; - Filter.BlockLength=Item->Distance; + Filter.BlockLength=(uint)Item->Distance; AddFilter(Filter); } @@ -543,7 +550,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) while (true) { - UnpPtr&=MaxWinMask; + UnpPtr=WrapUp(UnpPtr); if (D.Inp.InAddr>=ReadBorder) { if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && @@ -559,7 +566,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) break; } } - if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); if (WrittenFileSize>DestUnpSize) @@ -576,7 +583,8 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) { uint Length=SlotToLength(D.Inp,MainSlot-262); - uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + size_t Distance=1; + uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); if (DistSlot<4) { DBits=0; @@ -585,7 +593,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) else { DBits=DistSlot/2 - 1; - Distance+=(2 | (DistSlot & 1)) << DBits; + Distance+=size_t(2 | (DistSlot & 1)) << DBits; } if (DBits>0) @@ -594,7 +602,11 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) { if (DBits>4) { - Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + // It is also possible to always use getbits64() here. + if (DBits>36) + Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); + else + Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); D.Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); @@ -639,7 +651,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) if (MainSlot<262) { uint DistNum=MainSlot-258; - uint Distance=OldDist[DistNum]; + size_t Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; diff --git a/unrar/unpackinline.cpp b/unrar/unpackinline.cpp index 04c3d1f7..d476c4c5 100644 --- a/unrar/unpackinline.cpp +++ b/unrar/unpackinline.cpp @@ -1,4 +1,4 @@ -_forceinline void Unpack::InsertOldDist(uint Distance) +_forceinline void Unpack::InsertOldDist(size_t Distance) { OldDist[3]=OldDist[2]; OldDist[2]=OldDist[1]; @@ -6,23 +6,23 @@ _forceinline void Unpack::InsertOldDist(uint Distance) OldDist[0]=Distance; } -#ifdef _MSC_VER -#define FAST_MEMCPY +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) +#define UNPACK_COPY8 // We can copy 8 bytes at any position as uint64. #endif -_forceinline void Unpack::CopyString(uint Length,uint Distance) +_forceinline void Unpack::CopyString(uint Length,size_t Distance) { size_t SrcPtr=UnpPtr-Distance; if (SrcPtr=8) @@ -40,7 +40,7 @@ _forceinline void Unpack::CopyString(uint Length,uint Distance) Dest+=8; Length-=8; } -#ifdef FAST_MEMCPY +#ifdef UNPACK_COPY8 else while (Length>=8) { @@ -49,9 +49,7 @@ _forceinline void Unpack::CopyString(uint Length,uint Distance) // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH // always, so overlap here is impossible. - // This memcpy expanded inline by MSVC. We could also use uint64 - // assignment, which seems to provide about the same speed. - memcpy(Dest,Src,8); + RawPut8(RawGet8(Src),Dest); Src+=8; Dest+=8; @@ -69,13 +67,21 @@ _forceinline void Unpack::CopyString(uint Length,uint Distance) if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. } else - while (Length-- > 0) // Slow copying with all possible precautions. - { - Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask]; - // We need to have masked UnpPtr after quit from loop, so it must not - // be replaced with 'Window[UnpPtr++ & MaxWinMask]' - UnpPtr=(UnpPtr+1) & MaxWinMask; - } + { + SrcPtr=WrapDown(SrcPtr); // Because we called UnpPtr-Distance above. + // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize + // in a malformed archive. Depedning on WrapDown implementation, + // it might not correct it, so to prevent out of bound Window read + // we check SrcPtr 0) // Slow copying with all possible precautions. + { + Window[UnpPtr]=Window[WrapUp(SrcPtr++)]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[WrapUp(UnpPtr++)]' + UnpPtr=WrapUp(UnpPtr+1); + } + } } diff --git a/unrar/uowners.cpp b/unrar/uowners.cpp index 5eb12790..cbe2f404 100644 --- a/unrar/uowners.cpp +++ b/unrar/uowners.cpp @@ -2,18 +2,16 @@ void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { - char NameA[NM]; - WideToChar(FileName,NameA,ASIZE(NameA)); - - if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL) + // There must be 0 byte between owner and group strings. + // Otherwise strlen call below wouldn't be safe. + if (memchr(Arc.SubHead.SubData.data(),0,Arc.SubHead.SubData.size())==NULL) return; - char *OwnerName=(char *)&Arc.SubHead.SubData[0]; + char *OwnerName=(char *)Arc.SubHead.SubData.data(); int OwnerSize=strlen(OwnerName)+1; - int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; - char GroupName[NM]; - strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize); - GroupName[GroupSize]=0; + int GroupSize=Arc.SubHead.SubData.size()-OwnerSize; + char *GroupName=(char *)&Arc.SubHead.SubData[OwnerSize]; + std::string GroupStr(GroupName,GroupName+GroupSize); struct passwd *pw; if ((pw=getpwnam(OwnerName))==NULL) @@ -25,7 +23,7 @@ void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) uid_t OwnerID=pw->pw_uid; struct group *gr; - if ((gr=getgrnam(GroupName))==NULL) + if ((gr=getgrnam(GroupStr.c_str()))==NULL) { uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); ErrHandler.SetErrorCode(RARX_WARNING); @@ -33,10 +31,14 @@ void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) } uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; + + std::string NameA; + WideToChar(FileName,NameA); + #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(NameA,OwnerID,GroupID)!=0) + if (lchown(NameA.c_str(),OwnerID,GroupID)!=0) #else - if (chown(NameA,OwnerID,GroupID)!=0) + if (chown(NameA.c_str(),OwnerID,GroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); @@ -46,11 +48,8 @@ void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) } -void SetUnixOwner(Archive &Arc,const wchar *FileName) +void SetUnixOwner(Archive &Arc,const std::wstring &FileName) { - char NameA[NM]; - WideToChar(FileName,NameA,ASIZE(NameA)); - // First, we try to resolve symbolic names. If they are missing or cannot // be resolved, we try to use numeric values if any. If numeric values // are missing too, function fails. @@ -85,10 +84,14 @@ void SetUnixOwner(Archive &Arc,const wchar *FileName) else hd.UnixGroupID=gr->gr_gid; } + + std::string NameA; + WideToChar(FileName,NameA); + #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) + if (lchown(NameA.c_str(),hd.UnixOwnerID,hd.UnixGroupID)!=0) #else - if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) + if (chown(NameA.c_str(),hd.UnixOwnerID,hd.UnixGroupID)!=0) #endif { uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); diff --git a/unrar/version.hpp b/unrar/version.hpp index 73f084e9..fd31ae02 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ -#define RARVER_MAJOR 6 -#define RARVER_MINOR 23 +#define RARVER_MAJOR 7 +#define RARVER_MINOR 0 #define RARVER_BETA 1 -#define RARVER_DAY 19 -#define RARVER_MONTH 7 +#define RARVER_DAY 22 +#define RARVER_MONTH 10 #define RARVER_YEAR 2023 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index 4924d8d0..e1715155 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -1,8 +1,8 @@ #include "rar.hpp" #ifdef RARDLL -static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize); -static bool DllVolNotify(CommandData *Cmd,wchar *NextName); +bool DllVolChange(CommandData *Cmd,std::wstring &NextName); +static bool DllVolNotify(CommandData *Cmd,const std::wstring &NextName); #endif @@ -35,9 +35,8 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma Arc.Close(); - wchar NextName[NM]; - wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); - NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + std::wstring NextName=Arc.FileName; + NextVolumeName(NextName,!Arc.NewNumbering); #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; @@ -56,7 +55,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma // the next file might not be fully decoded yet. They write chunks of data // and then close the file again until the next chunk comes in. - if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) + if (Cmd->VolumePause && !uiAskNextVolume(NextName)) FailedOpen=true; #endif @@ -76,18 +75,17 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma { // Checking for new style volumes renamed by user to old style // name format. Some users did it for unknown reason. - wchar AltNextName[NM]; - wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName)); - NextVolumeName(AltNextName,ASIZE(AltNextName),true); + std::wstring AltNextName=Arc.FileName; + NextVolumeName(AltNextName,true); OldSchemeTested=true; if (Arc.Open(AltNextName,OpenMode)) { - wcsncpyz(NextName,AltNextName,ASIZE(NextName)); + NextName=AltNextName; break; } } #ifdef RARDLL - if (!DllVolChange(Cmd,NextName,ASIZE(NextName))) + if (!DllVolChange(Cmd,NextName)) { FailedOpen=true; break; @@ -109,7 +107,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma break; } #ifndef SILENT - if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName))) + if (Cmd->AllYes || !uiAskNextVolume(NextName)) #endif { FailedOpen=true; @@ -128,7 +126,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma } if (Command=='T' || Command=='X' || Command=='E') - mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); + mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName.c_str()); Arc.CheckArc(true); @@ -158,7 +156,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma } if (ShowFileName && !Cmd->DisableNames) { - mprintf(St(MExtrPoints),Arc.FileHead.FileName); + mprintf(St(MExtrPoints),Arc.FileHead.FileName.c_str()); if (!Cmd->DisablePercentage) mprintf(L" "); } @@ -190,45 +188,65 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma #ifdef RARDLL -bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize) +bool DllVolChange(CommandData *Cmd,std::wstring &NextName) { bool DllVolChanged=false,DllVolAborted=false; if (Cmd->Callback!=NULL) { - wchar OrgNextName[NM]; - wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName)); - if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) + std::wstring OrgNextName=NextName; + + std::vector NameBuf(MAXPATHSIZE); + std::copy(NextName.data(), NextName.data() + NextName.size() + 1, NameBuf.begin()); + + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NameBuf.data(),RAR_VOL_ASK)==-1) DllVolAborted=true; else - if (wcscmp(OrgNextName,NextName)!=0) + { + NextName=NameBuf.data(); + if (OrgNextName!=NextName) DllVolChanged=true; else { - char NextNameA[NM],OrgNextNameA[NM]; - WideToChar(NextName,NextNameA,ASIZE(NextNameA)); - strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA)); - if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1) + std::string NextNameA; + WideToChar(NextName,NextNameA); + std::string OrgNextNameA=NextNameA; + + std::vector NameBufA(MAXPATHSIZE); + std::copy(NextNameA.data(), NextNameA.data() + NextNameA.size() + 1, NameBufA.begin()); + + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NameBufA.data(),RAR_VOL_ASK)==-1) DllVolAborted=true; else - if (strcmp(OrgNextNameA,NextNameA)!=0) + { + NextNameA=NameBufA.data(); + if (OrgNextNameA!=NextNameA) { // We can damage some Unicode characters by U->A->U conversion, // so set Unicode name only if we see that ANSI name is changed. - CharToWide(NextNameA,NextName,NameSize); + CharToWide(NextNameA,NextName); DllVolChanged=true; } + } } + } } if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) { - char NextNameA[NM]; - WideToChar(NextName,NextNameA,ASIZE(NextNameA)); - int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); + std::string NextNameA; + WideToChar(NextName,NextNameA); + + std::vector NameBufA(MAXPATHSIZE); + std::copy(NextNameA.data(), NextNameA.data() + NextNameA.size() + 1, NameBufA.begin()); + + int RetCode=Cmd->ChangeVolProc(NameBufA.data(),RAR_VOL_ASK); if (RetCode==0) DllVolAborted=true; else - CharToWide(NextNameA,NextName,NameSize); + { + NextNameA=NameBufA.data(); + CharToWide(NextNameA,NextName); + } } // We quit only on 'abort' condition, but not on 'name not changed'. @@ -246,20 +264,21 @@ bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize) #ifdef RARDLL -bool DllVolNotify(CommandData *Cmd,wchar *NextName) +static bool DllVolNotify(CommandData *Cmd,const std::wstring &NextName) { - char NextNameA[NM]; - WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + std::string NextNameA; + WideToChar(NextName,NextNameA); + if (Cmd->Callback!=NULL) { - if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName.data(),RAR_VOL_NOTIFY)==-1) return false; - if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1) + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA.data(),RAR_VOL_NOTIFY)==-1) return false; } if (Cmd->ChangeVolProc!=NULL) { - int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); + int RetCode=Cmd->ChangeVolProc((char *)NextNameA.data(),RAR_VOL_NOTIFY); if (RetCode==0) return false; } diff --git a/unrar/win32acl.cpp b/unrar/win32acl.cpp index d4797bde..36cb8525 100644 --- a/unrar/win32acl.cpp +++ b/unrar/win32acl.cpp @@ -5,7 +5,7 @@ static bool ReadSacl=false; #ifndef SFX_MODULE -void ExtractACL20(Archive &Arc,const wchar *FileName) +void ExtractACL20(Archive &Arc,const std::wstring &FileName) { SetACLPrivileges(); @@ -27,7 +27,7 @@ void ExtractACL20(Archive &Arc,const wchar *FileName) Unpack Unpack(&DataIO); Unpack.Init(0x10000,false); - Array UnpData(Arc.EAHead.UnpSize); + std::vector UnpData(Arc.EAHead.UnpSize); DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); DataIO.EnableShowProgress(false); @@ -49,7 +49,7 @@ void ExtractACL20(Archive &Arc,const wchar *FileName) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0]; - int SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName.c_str(),si,sd); if (!SetCode) { @@ -64,9 +64,9 @@ void ExtractACL20(Archive &Arc,const wchar *FileName) #endif -void ExtractACL(Archive &Arc,const wchar *FileName) +void ExtractACL(Archive &Arc,const std::wstring &FileName) { - Array SubData; + std::vector SubData; if (!Arc.ReadSubData(&SubData,NULL,false)) return; @@ -78,12 +78,12 @@ void ExtractACL(Archive &Arc,const wchar *FileName) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0]; - int SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName.c_str(),si,sd); if (!SetCode) { - wchar LongName[NM]; - if (GetWinLongPath(FileName,LongName,ASIZE(LongName))) - SetCode=SetFileSecurity(LongName,si,sd); + std::wstring LongName; + if (GetWinLongPath(FileName,LongName)) + SetCode=SetFileSecurity(LongName.c_str(),si,sd); } if (!SetCode) diff --git a/unrar/win32lnk.cpp b/unrar/win32lnk.cpp index 759c4900..6d3a1bc9 100644 --- a/unrar/win32lnk.cpp +++ b/unrar/win32lnk.cpp @@ -40,26 +40,24 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) PrivSet=true; } - const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024; - Array Buf(BufSize); - REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; - - wchar SubstName[NM]; - wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName)); - size_t SubstLength=wcslen(SubstName); - - wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName; - bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0; - if (WinPrefix) - PrintNameSrc+=4; - if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0) - { - *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name. - PrintNameSrc+=3; - } - wcscpy(PrintNameDst,PrintNameSrc); + const std::wstring &SubstName=hd->RedirName; + size_t SubstLength=SubstName.size(); + + // REPARSE_DATA_BUFFER receives both SubstName and PrintName strings, + // thus "*2" below. PrintName is either shorter or same length as SubstName. + const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+((DWORD)SubstLength+1)*2*sizeof(wchar); + + std::vector Buf(BufSize); + REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)Buf.data(); + + // Remove \??\ NTFS junction prefix of present. + bool WinPrefix=SubstName.rfind(L"\\??\\",0)!=std::wstring::npos; + std::wstring PrintName=WinPrefix ? SubstName.substr(4):SubstName; + + if (WinPrefix && PrintName.rfind(L"UNC\\",0)!=std::wstring::npos) + PrintName=L"\\"+PrintName.substr(3); // Convert UNC\server\share to \\server\share. - size_t PrintLength=wcslen(PrintName); + size_t PrintLength=PrintName.size(); bool AbsPath=WinPrefix; // IsFullPath is not really needed here, AbsPath check is enough. @@ -86,9 +84,9 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) // Unix symlinks do not have their own 'directory' attribute. if (hd->Dir || hd->DirTarget) { - if (!CreateDirectory(Name,NULL)) + if (!CreateDir(Name)) { - uiMsg(UIERROR_DIRCREATE,UINULL,Name); + uiMsg(UIERROR_DIRCREATE,L"",Name); ErrHandler.SetErrorCode(RARX_CREATE); return false; } @@ -118,11 +116,11 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) rdb->MountPointReparseBuffer.SubstituteNameOffset=0; rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); - wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName.data()); rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); - wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName.data()); } else if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK) @@ -139,11 +137,11 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0; rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); - wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName.data()); rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); - wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName.data()); rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE; } @@ -166,7 +164,7 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) rdb->ReparseDataLength,NULL,0,&Returned,NULL)) { CloseHandle(hFile); - uiMsg(UIERROR_SLINKCREATE,UINULL,Name); + uiMsg(UIERROR_SLINKCREATE,L"",Name); DWORD LastError=GetLastError(); if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) && diff --git a/unrar/win32stm.cpp b/unrar/win32stm.cpp index 9dab728b..0c9c72a4 100644 --- a/unrar/win32stm.cpp +++ b/unrar/win32stm.cpp @@ -1,7 +1,25 @@ +#ifdef _WIN_ALL +// StreamName must include the leading ':'. +static bool IsNtfsReservedStream(const std::wstring &StreamName) +{ + 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", + L"::$INDEX_ROOT",L"::$LOGGED_UTILITY_STREAM",L":$EFS:$LOGGED_UTILITY_STREAM", + L":$TXF_DATA:$LOGGED_UTILITY_STREAM",L"::$OBJECT_ID",L"::$REPARSE_POINT" + }; + for (const wchar *Name : Reserved) + if (wcsicomp(StreamName,Name)==0) + return true; + return false; +} +#endif + + #if !defined(SFX_MODULE) && defined(_WIN_ALL) -void ExtractStreams20(Archive &Arc,const wchar *FileName) +void ExtractStreams20(Archive &Arc,const std::wstring &FileName) { if (Arc.BrokenHeader) { @@ -17,36 +35,39 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName) return; } - wchar StreamName[NM+2]; - if (FileName[0]!=0 && FileName[1]==0) + std::wstring StreamName; + if (FileName.size()==1) { // Convert single character names like f:stream to .\f:stream to // resolve the ambiguity with drive letters. - wcsncpyz(StreamName,L".\\",ASIZE(StreamName)); - wcsncatz(StreamName,FileName,ASIZE(StreamName)); + StreamName=L".\\"+FileName; } else - wcsncpyz(StreamName,FileName,ASIZE(StreamName)); - if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || - Arc.StreamHead.StreamName[0]!=':') + StreamName=FileName; + if (Arc.StreamHead.StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } - wchar StoredName[NM]; - CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); - ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); + std::wstring StoredName; + // "substr(1)" to exclude ':', so we can use ConvertPath() below. + CharToWide(Arc.StreamHead.StreamName.substr(1),StoredName); + ConvertPath(&StoredName,&StoredName); - wcsncatz(StreamName,StoredName,ASIZE(StreamName)); + StoredName=L":"+StoredName; + if (IsNtfsReservedStream(StoredName)) + return; + + StreamName+=StoredName; - FindData fd; - bool Found=FindFile::FastFind(FileName,&fd); + FindData FD; + bool Found=FindFile::FastFind(FileName,&FD); - if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) - SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(StreamName)) @@ -72,31 +93,29 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName) } File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) - SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, - &fd.ftLastWriteTime); - if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) - SetFileAttr(FileName,fd.FileAttr); + SetFileTime(HostFile.GetHandle(),&FD.ftCreationTime,&FD.ftLastAccessTime, + &FD.ftLastWriteTime); + if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,FD.FileAttr); } #endif #ifdef _WIN_ALL -void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) +void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode) { - wchar FullName[NM+2]; + 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. - wcsncpyz(FullName,L".\\",ASIZE(FullName)); - wcsncatz(FullName,FileName,ASIZE(FullName)); + FullName=L".\\"+FileName; } else - wcsncpyz(FullName,FileName,ASIZE(FullName)); + FullName=FileName; - wchar StreamName[NM]; - GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); - if (*StreamName!=':') + std::wstring StreamName=GetStreamNameNTFS(Arc); + if (StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); @@ -110,14 +129,17 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) return; } - wcsncatz(FullName,StreamName,ASIZE(FullName)); + FullName+=StreamName; + + if (IsNtfsReservedStream(StreamName)) + return; - FindData fd; - bool HostFound=FindFile::FastFind(FileName,&fd); + FindData FD; + bool HostFound=FindFile::FastFind(FileName,&FD); - if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) - SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName)) @@ -129,32 +151,26 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) // Restoring original file timestamps. File HostFile; if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) - SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, - &fd.ftLastWriteTime); + 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); + SetFileAttr(FileName,FD.FileAttr); } #endif -void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize) +std::wstring GetStreamNameNTFS(Archive &Arc) { - byte *Data=&Arc.SubHead.SubData[0]; - size_t DataSize=Arc.SubHead.SubData.Size(); + std::wstring Dest; if (Arc.Format==RARFMT15) - { - size_t DestSize=Min(DataSize/2,MaxSize-1); - RawToWide(Data,StreamName,DestSize); - StreamName[DestSize]=0; - } + Dest=RawToWide(Arc.SubHead.SubData); else { - char UtfString[NM*4]; - size_t DestSize=Min(DataSize,ASIZE(UtfString)-1); - memcpy(UtfString,Data,DestSize); - UtfString[DestSize]=0; - UtfToWide(UtfString,StreamName,MaxSize); + std::vector Src=Arc.SubHead.SubData; + Src.push_back(0); // Needed for our UtfToWide. + UtfToWide((char *)Src.data(),Dest); } + return Dest; } From b8109883668a9b8c533b05491661c87514a9bf9a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:07 +0000 Subject: [PATCH 21/76] Added unrar 7.0.2 --- unrar/dll.rc | 4 ++-- unrar/pathfn.cpp | 9 ++++++++- unrar/version.hpp | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index f672364d..5d18243a 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 1, 1026 -PRODUCTVERSION 7, 0, 1, 1026 +FILEVERSION 7, 0, 1, 1029 +PRODUCTVERSION 7, 0, 1, 1029 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 17e2480c..2588c733 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -1076,7 +1076,14 @@ void MakeNameCompatible(std::wstring &Name) if (I+1==Name.size() || IsPathDiv(Name[I+1])) while (I>=0 && (Name[I]=='.' || Name[I]==' ')) { - // Permit path1/./path2 and ../path1 paths. + if (I==0) + { + if (Name[I]==' ') + Name[I]='_'; // Convert " /path" to "_/path". + break; // Allow ./path1 paths. + } + // Permit path1/./path2 and ../path1 paths. Leading dots are possible + // if specified by user in the destination path. if (Name[I]=='.' && I>0 && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) break; Name.erase(I,1); diff --git a/unrar/version.hpp b/unrar/version.hpp index fd31ae02..3f245504 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 #define RARVER_BETA 1 -#define RARVER_DAY 22 +#define RARVER_DAY 25 #define RARVER_MONTH 10 #define RARVER_YEAR 2023 From abb9fa4632f0db7fe5afe26031b1df40dc5d563a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:07 +0000 Subject: [PATCH 22/76] Added unrar 7.0.3 --- unrar/archive.cpp | 4 +- unrar/archive.hpp | 4 ++ unrar/arcread.cpp | 15 ++++-- unrar/cmddata.cpp | 10 ++-- unrar/consio.cpp | 3 ++ unrar/dll.hpp | 2 +- unrar/dll.rc | 4 +- unrar/extract.cpp | 10 ++-- unrar/headers5.hpp | 3 +- unrar/list.cpp | 8 ++-- unrar/loclang.hpp | 2 +- unrar/log.cpp | 2 + unrar/options.cpp | 2 +- unrar/options.hpp | 2 +- unrar/pathfn.cpp | 1 + unrar/rardefs.hpp | 3 ++ unrar/rawint.hpp | 2 +- unrar/strfn.cpp | 15 ++++++ unrar/strfn.hpp | 1 + unrar/uiconsole.cpp | 12 ++--- unrar/unpack.cpp | 105 +++++++++++++++++++++-------------------- unrar/unpack.hpp | 8 +++- unrar/unpack15.cpp | 20 ++++++-- unrar/unpack20.cpp | 3 ++ unrar/unpack30.cpp | 3 ++ unrar/unpack50.cpp | 20 ++++++-- unrar/unpack50frag.cpp | 36 +++++++++----- unrar/unpack50mt.cpp | 24 ++++++++++ unrar/unpackinline.cpp | 57 ++++++++++++++++------ unrar/version.hpp | 4 +- 30 files changed, 265 insertions(+), 120 deletions(-) diff --git a/unrar/archive.cpp b/unrar/archive.cpp index 90d857fe..c2293de7 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -336,8 +336,8 @@ int64 Archive::Tell() #endif -// Return true if dictionary size is valid. If size is RAR7 only, return -// the adjusted nearest bottom value in Size7. Return header flags in Flags. +// Return 0 if dictionary size is invalid. If size is RAR7 only, return +// the adjusted nearest bottom value. Return header flags in Flags. uint64 Archive::GetWinSize(uint64 Size,uint &Flags) { Flags=0; diff --git a/unrar/archive.hpp b/unrar/archive.hpp index 754a2b8f..675e63ae 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -140,7 +140,11 @@ class Archive:public File uint VolNumber; int64 VolWrite; + + // Total size of files adding to archive. Might also include the size of + // files repacked in solid archive. uint64 AddingFilesSize; + uint64 AddingHeadersSize; bool NewArchive; diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index d493d71d..4b8d99f2 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -831,10 +831,10 @@ size_t Archive::ReadHeader50() // them. uint UnpVer=(CompInfo & 0x3f); if (UnpVer==0) - hd->UnpVer=50; + hd->UnpVer=VER_PACK5; else if (UnpVer==1) - hd->UnpVer=70; + hd->UnpVer=VER_PACK7; else hd->UnpVer=VER_UNKNOWN; @@ -858,9 +858,18 @@ size_t Archive::ReadHeader50() hd->WinSize=0; else { - hd->WinSize=0x20000ULL<<((CompInfo>>10)&0x1f); + hd->WinSize=0x20000ULL<<((CompInfo>>10)&(UnpVer==0 ? 0x0f:0x1f)); if (UnpVer==1) + { hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f); + + // RAR7 header with RAR5 compression. Needed to append RAR7 files + // to RAR5 solid stream if new dictionary is larger than existing. + if ((CompInfo & FCI_RAR5_COMPAT)!=0) + hd->UnpVer=VER_PACK5; + if (hd->WinSize>UNPACK_MAX_DICT) + hd->UnpVer=VER_UNKNOWN; + } } size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 54b10f23..c050ab6e 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -610,12 +610,12 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; case 'D': { - bool SetMaxDict=toupperw(Switch[2])=='X'; + bool SetDictLimit=toupperw(Switch[2])=='X'; - uint64 Size=atoiw(Switch+(SetMaxDict ? 3 : 2)); + uint64 Size=atoiw(Switch+(SetDictLimit ? 3 : 2)); wchar LastChar=toupperw(Switch[wcslen(Switch)-1]); if (IsDigit(LastChar)) - LastChar=SetMaxDict ? 'G':'M'; // Treat -md128 as -md128m and -mdx32 as -mdx32g. + LastChar=SetDictLimit ? 'G':'M'; // Treat -md128 as -md128m and -mdx32 as -mdx32g. switch(LastChar) { case 'K': @@ -640,8 +640,8 @@ void CommandData::ProcessSwitch(const wchar *Switch) Size<=0x100000000ULL && !IsPow2(Size)) BadSwitch(Switch); else - if (SetMaxDict) - MaxWinSize=Size; + if (SetDictLimit) + WinSizeLimit=Size; else { WinSize=Size; diff --git a/unrar/consio.cpp b/unrar/consio.cpp index 74502d3a..023c3f3c 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -71,6 +71,9 @@ static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { // No need for PrintfPrepareFmt here, vwstrprintf calls it. std::wstring s=vwstrprintf(fmt,arglist); + + ReplaceEsc(s); + #ifdef _WIN_ALL if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) { diff --git a/unrar/dll.hpp b/unrar/dll.hpp index 06ef7022..ac8d53fa 100644 --- a/unrar/dll.hpp +++ b/unrar/dll.hpp @@ -32,7 +32,7 @@ #define RAR_VOL_ASK 0 #define RAR_VOL_NOTIFY 1 -#define RAR_DLL_VERSION 8 +#define RAR_DLL_VERSION 9 #define RAR_HASH_NONE 0 #define RAR_HASH_CRC32 1 diff --git a/unrar/dll.rc b/unrar/dll.rc index 5d18243a..2c96b36d 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 1, 1029 -PRODUCTVERSION 7, 0, 1, 1029 +FILEVERSION 7, 0, 1, 1043 +PRODUCTVERSION 7, 0, 1, 1043 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 67842a72..f4ea12c5 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -862,8 +862,8 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) } catch (std::bad_alloc) { - if (Arc.FileHead.WinSize>0x1000000) - uiMsg(UIERROR_EXTRDICTOUTMEM,Arc.FileName,uint(Arc.FileHead.WinSize/0x100000)); + if (Arc.FileHead.WinSize>=0x40000000) + uiMsg(UIERROR_EXTRDICTOUTMEM,Arc.FileName,uint(Arc.FileHead.WinSize/0x40000000+(Arc.FileHead.WinSize%0x40000000!=0 ? 1 : 0))); throw; } @@ -1696,13 +1696,13 @@ void CmdExtract::GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumber bool CmdExtract::CheckWinLimit(Archive &Arc,std::wstring &ArcFileName) { - if (Arc.FileHead.WinSize<=Cmd->MaxWinSize || Arc.FileHead.WinSize<=Cmd->WinSize) + if (Arc.FileHead.WinSize<=Cmd->WinSizeLimit || Arc.FileHead.WinSize<=Cmd->WinSize) return true; - if (uiDictLimit(Cmd,ArcFileName,Arc.FileHead.WinSize,Max(Cmd->MaxWinSize,Cmd->WinSize))) + if (uiDictLimit(Cmd,ArcFileName,Arc.FileHead.WinSize,Max(Cmd->WinSizeLimit,Cmd->WinSize))) { // No more prompts when extracting other files. Important for GUI versions, // where we might not have [Max]WinSize set permanently when extracting. - Cmd->MaxWinSize=Arc.FileHead.WinSize; + Cmd->WinSizeLimit=Arc.FileHead.WinSize; } else { diff --git a/unrar/headers5.hpp b/unrar/headers5.hpp index c5c3fe3f..361e554d 100644 --- a/unrar/headers5.hpp +++ b/unrar/headers5.hpp @@ -53,7 +53,7 @@ #define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used). #define FCI_METHOD_BIT2 0x00000200 #define FCI_DICT_BIT0 0x00000400 // Dictionary size. -#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 1 TB. #define FCI_DICT_BIT2 0x00001000 #define FCI_DICT_BIT3 0x00002000 #define FCI_DICT_BIT4 0x00004000 @@ -62,6 +62,7 @@ #define FCI_DICT_FRACT2 0x00020000 #define FCI_DICT_FRACT3 0x00040000 #define FCI_DICT_FRACT4 0x00080000 +#define FCI_RAR5_COMPAT 0x00100000 // RAR7 compression flags and RAR5 compression algorithm. // Main header extra field values. #define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. diff --git a/unrar/list.cpp b/unrar/list.cpp index 15a3fb4d..8292e69f 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -363,15 +363,15 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo std::wstring WinSize; if (!hd.Dir) if (hd.WinSize%1073741824==0) - WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"G"; + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"g"; else if (hd.WinSize%1048576==0) - WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"M"; + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"m"; else if (hd.WinSize>=1024) - WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"K"; + WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"k"; else - WinSize=L" -md=?(>2G)"; + WinSize=L" -md=?"; mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo), Format==RARFMT15 ? L"1.5":L"5.0", diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index a4342b98..8a61ed0e 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -384,7 +384,7 @@ #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 MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u MB 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 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." diff --git a/unrar/log.cpp b/unrar/log.cpp index 074ecdb2..57f7648e 100644 --- a/unrar/log.cpp +++ b/unrar/log.cpp @@ -25,6 +25,8 @@ void Log(const wchar *ArcName,const wchar *fmt,...) std::wstring s=vwstrprintf(fmt,arglist); + ReplaceEsc(s); + va_end(arglist); eprintf(L"%ls",s.c_str()); ErrHandler.SetSystemErrorCode(Code); diff --git a/unrar/options.cpp b/unrar/options.cpp index 8ac1f686..ab8cff5e 100644 --- a/unrar/options.cpp +++ b/unrar/options.cpp @@ -10,7 +10,7 @@ void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); WinSize=0x2000000; - MaxWinSize=0x100000000; + WinSizeLimit=0x100000000; Overwrite=OVERWRITE_DEFAULT; Method=3; MsgStream=MSG_STDOUT; diff --git a/unrar/options.hpp b/unrar/options.hpp index 29f74afd..8f0f6ad1 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -115,7 +115,7 @@ class RAROptions bool InclAttrSet; uint64 WinSize; - uint64 MaxWinSize; // Switch -mdx. + uint64 WinSizeLimit; // Switch -mdx. #ifdef USE_QOPEN QOPEN_MODE QOpenMode; diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 2588c733..f9f0c4b7 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -1078,6 +1078,7 @@ void MakeNameCompatible(std::wstring &Name) { if (I==0) { + // Windows 10 Explorer can't rename or delete " " files and folders. if (Name[I]==' ') Name[I]='_'; // Convert " /path" to "_/path". break; // Allow ./path1 paths. diff --git a/unrar/rardefs.hpp b/unrar/rardefs.hpp index 986aaaf0..5be04e06 100644 --- a/unrar/rardefs.hpp +++ b/unrar/rardefs.hpp @@ -32,6 +32,9 @@ // maximum dictionary supported by decompression. #define PACK_MAX_DICT 0x1000000000ULL // 64 GB. +// Maximum dictionary allowed by decompression. +#define UNPACK_MAX_DICT 0x1000000000ULL // 64 GB. + #ifndef SFX_MODULE #define USE_QOPEN diff --git a/unrar/rawint.hpp b/unrar/rawint.hpp index 5c77c54f..e5889fda 100644 --- a/unrar/rawint.hpp +++ b/unrar/rawint.hpp @@ -173,7 +173,7 @@ inline uint64 GetGreaterOrEqualPow2(uint64 n) inline uint64 GetLessOrEqualPow2(uint64 n) { - size_t p=1; + uint64 p=1; while (p*2<=n) p*=2; return p; diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index 2d9cf6ef..3fdaca59 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -553,3 +553,18 @@ void TruncateAtZero(std::wstring &Str) if (Pos!=std::wstring::npos) Str.erase(Pos); } + + +void ReplaceEsc(std::wstring &Str) +{ + std::wstring::size_type Pos=0; + while (true) + { + Pos=Str.find(L'\033',Pos); + if (Pos==std::wstring::npos) + break; + Str[Pos]=L'\''; + Str.insert(Pos+1,L"\\033'"); + Pos+=6; + } +} diff --git a/unrar/strfn.hpp b/unrar/strfn.hpp index fb3003b2..fd4793c5 100644 --- a/unrar/strfn.hpp +++ b/unrar/strfn.hpp @@ -60,5 +60,6 @@ bool ExpandEnvironmentStr(std::wstring &Str); #endif void TruncateAtZero(std::wstring &Str); +void ReplaceEsc(std::wstring &Str); #endif diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index 15406ee9..b58293ba 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -224,12 +224,6 @@ void uiMsgStore::Msg() Log(Str[0],St(MNewerRAR)); break; #endif - case UIERROR_EXTRDICTOUTMEM: - Log(Str[0],St(MExtrDictOutMem),Num[0]); -#ifdef _WIN_32 - Log(Str[0],St(MSuggest64bit)); -#endif - break; case UIERROR_RECVOLDIFFSETS: Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); break; @@ -242,6 +236,12 @@ void uiMsgStore::Msg() case UIERROR_RECVOLCANNOTFIX: mprintf(St(MRecVolCannotFix)); break; + case UIERROR_EXTRDICTOUTMEM: + Log(Str[0],St(MExtrDictOutMem),Num[0]); +#ifdef _WIN_32 + Log(Str[0],St(MSuggest64bit)); +#endif + break; case UIERROR_UNEXPEOF: Log(Str[0],St(MLogUnexpEOF)); break; diff --git a/unrar/unpack.cpp b/unrar/unpack.cpp index 1c48b395..86663efb 100644 --- a/unrar/unpack.cpp +++ b/unrar/unpack.cpp @@ -31,6 +31,7 @@ Unpack::Unpack(ComprDataIO *DataIO) ReadBufMT=NULL; UnpThreadData=NULL; #endif + AllocWinSize=0; MaxWinSize=0; MaxWinMask=0; @@ -52,8 +53,7 @@ Unpack::~Unpack() InitFilters30(false); #endif - if (Window!=NULL) - free(Window); + free(Window); #ifdef RAR_SMP delete UnpThreadPool; delete[] ReadBufMT; @@ -74,7 +74,7 @@ void Unpack::SetThreads(uint Threads) // We get 64-bit WinSize, so we still can check and quit for dictionaries -// exceeding 4 GB in 32-bit builds. Then we convert WinSize to size_t +// exceeding a threshold in 32-bit builds. Then we convert WinSize to size_t // MaxWinSize. void Unpack::Init(uint64 WinSize,bool Solid) { @@ -87,43 +87,53 @@ void Unpack::Init(uint64 WinSize,bool Solid) if (WinSize0x10000000000ULL) // Window size must not exceed 1 TB. + if (WinSize>Min(0x10000000000ULL,UNPACK_MAX_DICT)) // Window size must not exceed 1 TB. throw std::bad_alloc(); - // 32-bit build can't unpack dictionaries exceeding 32-bit. - if (WinSize>=0x100000000 && sizeof(size_t)<=4) + // 32-bit build can't unpack dictionaries exceeding 32-bit even in theory. + // Also we've not verified if WrapUp and WrapDown work properly in 32-bit + // version and >2GB dictionary and if 32-bit version can handle >2GB + // distances. Since such version is unlikely to allocate >2GB anyway, + // we prohibit >2GB dictionaries for 32-bit build here. + if (WinSize>0x80000000 && sizeof(size_t)<=4) throw std::bad_alloc(); + // Solid block shall use the same window size for all files. + // But if Window isn't initialized when Solid is set, it means that + // first file in solid block doesn't have the solid flag. We initialize + // the window anyway for such malformed archive. + // Non-solid files shall use their specific window sizes, + // so current window size and unpack routine behavior doesn't depend on + // previously unpacked files and their extraction order. + if (!Solid || Window==nullptr) + { + MaxWinSize=(size_t)WinSize; + MaxWinMask=MaxWinSize-1; + } + + // Use the already allocated window when processing non-solid files + // with reducing dictionary sizes. + if (WinSize<=AllocWinSize) + return; + // Archiving code guarantees that window size does not grow in the same // solid stream. So if we are here, we are either creating a new window // or increasing the size of non-solid window. So we could safely reject - // current window data without copying them to a new window, though being - // extra cautious, we still handle the solid window grow case below. - bool Grow=Solid && (Window!=NULL || Fragmented); - - // We do not handle growth for existing fragmented window. - if (Grow && Fragmented) + // current window data without copying them to a new window. + if (Solid && (Window!=NULL || Fragmented && WinSize>FragWindow.GetWinSize())) throw std::bad_alloc(); - byte *NewWindow=Fragmented ? NULL : (byte *)malloc((size_t)WinSize); + free(Window); + + Window=Fragmented ? NULL : (byte *)malloc((size_t)WinSize); - if (NewWindow==NULL) - if (Grow || WinSize<0x1000000) - { - // We do not support growth for new fragmented window. - // Also exclude RAR4 and small dictionaries. - throw std::bad_alloc(); - } + if (Window==NULL) + if (WinSize<0x1000000 || sizeof(size_t)>4) + throw std::bad_alloc(); // Exclude RAR4, small dictionaries and 64-bit. else { - if (Window!=NULL) // If allocated by preceding files. - { - free(Window); - Window=NULL; - } - FragWindow.Init((size_t)WinSize); + if (WinSize>FragWindow.GetWinSize()) + FragWindow.Init((size_t)WinSize); Fragmented=true; } @@ -131,23 +141,12 @@ void Unpack::Init(uint64 WinSize,bool Solid) { // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access unused areas of sliding dictionary. - memset(NewWindow,0,(size_t)WinSize); - - // If Window is not NULL, it means that window size has grown. - // In solid streams we need to copy data to a new window in such case. - // RAR archiving code does not allow it in solid streams now, - // but let's implement it anyway just in case we'll change it sometimes. - if (Grow) - for (size_t I=1;I<=MaxWinSize;I++) - NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; - - if (Window!=NULL) - free(Window); - Window=NewWindow; - } + // 2023.10.31: We've added FirstWinDone based unused area access check + // in Unpack::CopyString(), so this memset might be unnecessary now. +// memset(Window,0,(size_t)WinSize); - MaxWinSize=(size_t)WinSize; - MaxWinMask=MaxWinSize-1; + AllocWinSize=WinSize; + } } @@ -173,9 +172,9 @@ void Unpack::DoUnpack(uint Method,bool Solid) Unpack29(Solid); break; #endif - case 50: // RAR 5.0 and 7.0 compression algorithms. - case 70: - ExtraDist=(Method==70); + case VER_PACK5: // 50. RAR 5.0 and 7.0 compression algorithms. + case VER_PACK7: // 70. + ExtraDist=(Method==VER_PACK7); #ifdef RAR_SMP if (MaxUserThreads>1) { @@ -201,13 +200,19 @@ void Unpack::UnpInitData(bool Solid) { if (!Solid) { - memset(OldDist,0,sizeof(OldDist)); + OldDist[0]=OldDist[1]=OldDist[2]=OldDist[3]=(size_t)-1; + OldDistPtr=0; - LastDist=LastLength=0; + + LastDist=(uint)-1; // Initialize it to -1 like LastDist. + LastLength=0; + // memset(Window,0,MaxWinSize); memset(&BlockTables,0,sizeof(BlockTables)); UnpPtr=WrPtr=0; - WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; + PrevPtr=0; + FirstWinDone=false; + WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE); } // Filters never share several solid files, so we can safely reset them // even in solid archive. diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index c807a1ef..0de65687 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -188,14 +188,16 @@ class FragmentedWindow void Reset(); byte *Mem[MAX_MEM_BLOCKS]; size_t MemSize[MAX_MEM_BLOCKS]; + size_t LastAllocated; public: FragmentedWindow(); ~FragmentedWindow(); void Init(size_t WinSize); byte& operator [](size_t Item); - void CopyString(uint Length,size_t Distance,size_t &UnpPtr,size_t MaxWinSize); + void CopyString(uint Length,size_t Distance,size_t &UnpPtr,bool FirstWinDone,size_t MaxWinSize); void CopyData(byte *Dest,size_t WinPos,size_t Size); size_t GetBlockSize(size_t StartPos,size_t RequiredSize); + size_t GetWinSize() {return LastAllocated;} }; @@ -254,6 +256,9 @@ class Unpack:PackDef size_t UnpPtr; // Current position in window. + size_t PrevPtr; // UnpPtr value for previous loop iteration. + bool FirstWinDone; // At least one dictionary was processed. + size_t WrPtr; // Last written unpacked data position. // Top border of read packed data. @@ -387,6 +392,7 @@ class Unpack:PackDef void UnpackDecode(UnpackThreadData &D); #endif + uint64 AllocWinSize; size_t MaxWinSize; size_t MaxWinMask; diff --git a/unrar/unpack15.cpp b/unrar/unpack15.cpp index 377fd847..700c4e1f 100644 --- a/unrar/unpack15.cpp +++ b/unrar/unpack15.cpp @@ -60,6 +60,9 @@ void Unpack::Unpack15(bool Solid) { UnpPtr&=MaxWinMask; + FirstWinDone|=(PrevPtr>UnpPtr); + PrevPtr=UnpPtr; + if (Inp.InAddr>ReadTop-30 && !UnpReadBuf()) break; if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) @@ -471,11 +474,18 @@ void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) void Unpack::CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; - while (Length--) - { - Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; - UnpPtr=(UnpPtr+1) & MaxWinMask; - } + if (!FirstWinDone && Distance>UnpPtr || Distance>MaxWinSize) + while (Length-- > 0) + { + Window[UnpPtr]=0; + UnpPtr=(UnpPtr+1) & MaxWinMask; + } + else + while (Length-- > 0) + { + Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; + UnpPtr=(UnpPtr+1) & MaxWinMask; + } } diff --git a/unrar/unpack20.cpp b/unrar/unpack20.cpp index 38a045cc..a0e179d6 100644 --- a/unrar/unpack20.cpp +++ b/unrar/unpack20.cpp @@ -37,6 +37,9 @@ void Unpack::Unpack20(bool Solid) { UnpPtr&=MaxWinMask; + FirstWinDone|=(PrevPtr>UnpPtr); + PrevPtr=UnpPtr; + if (Inp.InAddr>ReadTop-30) if (!UnpReadBuf()) break; diff --git a/unrar/unpack30.cpp b/unrar/unpack30.cpp index bb5dce0d..1fb0615a 100644 --- a/unrar/unpack30.cpp +++ b/unrar/unpack30.cpp @@ -50,6 +50,9 @@ void Unpack::Unpack29(bool Solid) { UnpPtr&=MaxWinMask; + FirstWinDone|=(PrevPtr>UnpPtr); + PrevPtr=UnpPtr; + if (Inp.InAddr>ReadBorder) { if (!UnpReadBuf30()) diff --git a/unrar/unpack50.cpp b/unrar/unpack50.cpp index 0dc6af22..075eb691 100644 --- a/unrar/unpack50.cpp +++ b/unrar/unpack50.cpp @@ -20,6 +20,11 @@ void Unpack::Unpack5(bool Solid) { UnpPtr=WrapUp(UnpPtr); + // To combine this code with WrapUp above, we also need to set FirstWinDone + // in CopyString. Performance gain is questionable in this case. + FirstWinDone|=(PrevPtr>UnpPtr); + PrevPtr=UnpPtr; + if (Inp.InAddr>=ReadBorder) { bool FileDone=false; @@ -42,6 +47,7 @@ void Unpack::Unpack5(bool Solid) break; } + // WriteBorder==UnpPtr means that we have MaxWinSize data ahead. if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); @@ -95,6 +101,14 @@ void Unpack::Unpack5(bool Solid) } uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); Distance+=LowDist; + + // Distance can be 0 for multiples of 4 GB as result of size_t + // overflow in 32-bit build. Its lower 32-bit can also erroneously + // fit into dictionary after truncating upper 32-bits. Replace such + // invalid distances with -1, so CopyString sets 0 data for them. + // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. + if (sizeof(Distance)==4 && DBits>=30) + Distance=(size_t)-1; } else { @@ -117,7 +131,7 @@ void Unpack::Unpack5(bool Solid) InsertOldDist(Distance); LastLength=Length; if (Fragmented) - FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinSize); + FragWindow.CopyString(Length,Distance,UnpPtr,FirstWinDone,MaxWinSize); else CopyString(Length,Distance); continue; @@ -133,7 +147,7 @@ void Unpack::Unpack5(bool Solid) { if (LastLength!=0) if (Fragmented) - FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinSize); + FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,FirstWinDone,MaxWinSize); else CopyString(LastLength,OldDist[0]); continue; @@ -150,7 +164,7 @@ void Unpack::Unpack5(bool Solid) uint Length=SlotToLength(Inp,LengthSlot); LastLength=Length; if (Fragmented) - FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinSize); + FragWindow.CopyString(Length,Distance,UnpPtr,FirstWinDone,MaxWinSize); else CopyString(Length,Distance); continue; diff --git a/unrar/unpack50frag.cpp b/unrar/unpack50frag.cpp index b318ebf0..9208405e 100644 --- a/unrar/unpack50frag.cpp +++ b/unrar/unpack50frag.cpp @@ -2,6 +2,7 @@ FragmentedWindow::FragmentedWindow() { memset(Mem,0,sizeof(Mem)); memset(MemSize,0,sizeof(MemSize)); + LastAllocated=0; } @@ -13,6 +14,7 @@ FragmentedWindow::~FragmentedWindow() void FragmentedWindow::Reset() { + LastAllocated=0; for (uint I=0;I=MaxWinSize) + if (Distance>UnpPtr) + { SrcPtr+=MaxWinSize; - // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize - // in a malformed archive. - if (SrcPtr 0) + + if (Distance>MaxWinSize || !FirstWinDone) { - (*this)[UnpPtr]=(*this)[SrcPtr]; - if (++SrcPtr>=MaxWinSize) - SrcPtr-=MaxWinSize; - if (++UnpPtr>=MaxWinSize) - UnpPtr-=MaxWinSize; + while (Length-- > 0) + { + (*this)[UnpPtr]=0; + if (++UnpPtr>=MaxWinSize) + UnpPtr-=MaxWinSize; + } + return; } + } + + while (Length-- > 0) + { + (*this)[UnpPtr]=(*this)[SrcPtr]; + if (++SrcPtr>=MaxWinSize) + SrcPtr-=MaxWinSize; + if (++UnpPtr>=MaxWinSize) + UnpPtr-=MaxWinSize; + } } diff --git a/unrar/unpack50mt.cpp b/unrar/unpack50mt.cpp index baed0e07..3d3fde59 100644 --- a/unrar/unpack50mt.cpp +++ b/unrar/unpack50mt.cpp @@ -392,6 +392,14 @@ void Unpack::UnpackDecode(UnpackThreadData &D) } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; + + // Distance can be 0 for multiples of 4 GB as result of size_t + // overflow in 32-bit build. Its lower 32-bit can also erroneously + // fit into dictionary after truncating upper 32-bits. Replace such + // invalid distances with -1, so CopyString sets 0 data for them. + // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. + if (sizeof(Distance)==4 && DBits>=30) + Distance=(size_t)-1; } else { @@ -458,6 +466,10 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) while (ItemUnpPtr); + PrevPtr=UnpPtr; + if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) { UnpWriteBuf(); @@ -551,6 +563,10 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) while (true) { UnpPtr=WrapUp(UnpPtr); + + FirstWinDone|=(PrevPtr>UnpPtr); + PrevPtr=UnpPtr; + if (D.Inp.InAddr>=ReadBorder) { if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && @@ -611,6 +627,14 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) } uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); Distance+=LowDist; + + // Distance can be 0 for multiples of 4 GB as result of size_t + // overflow in 32-bit build. Its lower 32-bit can also erroneously + // fit into dictionary after truncating upper 32-bits. Replace such + // invalid distances with -1, so CopyString sets 0 data for them. + // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. + if (sizeof(Distance)==4 && DBits>=30) + Distance=(size_t)-1; } else { diff --git a/unrar/unpackinline.cpp b/unrar/unpackinline.cpp index d476c4c5..68aec917 100644 --- a/unrar/unpackinline.cpp +++ b/unrar/unpackinline.cpp @@ -13,6 +13,41 @@ _forceinline void Unpack::InsertOldDist(size_t Distance) _forceinline void Unpack::CopyString(uint Length,size_t Distance) { size_t SrcPtr=UnpPtr-Distance; + + // Perform the correction here instead of "else", so matches crossing + // the window beginning can also be processed by first "if" part. + if (Distance>UnpPtr) // Unlike SrcPtr>=MaxWinSize, it catches invalid distances like 0xfffffff0 in 32-bit build. + { + // Same as WrapDown(SrcPtr), needed because of UnpPtr-Distance above. + // We need the same condition below, so we expanded WrapDown() here. + SrcPtr+=MaxWinSize; + + // About Distance>MaxWinSize check. + // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize + // in a malformed archive. Our WrapDown replacement above might not + // correct it, so to prevent out of bound Window read we check it here. + // Unlike SrcPtr>=MaxWinSize check, it allows MaxWinSize>0x80000000 + // in 32-bit build, which could cause overflow in SrcPtr. + // About !FirstWinDone check. + // If first window hasn't filled yet and it points outside of window, + // set data to 0 instead of copying preceding file data, so result doesn't + // depend on previously extracted files in non-solid archive. + if (Distance>MaxWinSize || !FirstWinDone) + { + // Fill area of specified length with 0 instead of returning. + // So if only the distance is broken and rest of packed data is valid, + // it preserves offsets and allows to continue extraction. + // If we set SrcPtr to random offset instead, let's say, 0, + // we still will be copying preceding file data if UnpPtr is also 0. + while (Length-- > 0) + { + Window[UnpPtr]=0; + UnpPtr=WrapUp(UnpPtr+1); + } + return; + } + } + if (SrcPtr6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. } else - { - SrcPtr=WrapDown(SrcPtr); // Because we called UnpPtr-Distance above. - // SrcPtr can be >=MaxWinSize if distance exceeds MaxWinSize - // in a malformed archive. Depedning on WrapDown implementation, - // it might not correct it, so to prevent out of bound Window read - // we check SrcPtr 0) // Slow copying with all possible precautions. - { - Window[UnpPtr]=Window[WrapUp(SrcPtr++)]; - // We need to have masked UnpPtr after quit from loop, so it must not - // be replaced with 'Window[WrapUp(UnpPtr++)]' - UnpPtr=WrapUp(UnpPtr+1); - } - } + while (Length-- > 0) // Slow copying with all possible precautions. + { + Window[UnpPtr]=Window[WrapUp(SrcPtr++)]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[WrapUp(UnpPtr++)]' + UnpPtr=WrapUp(UnpPtr+1); + } } diff --git a/unrar/version.hpp b/unrar/version.hpp index 3f245504..8e4173f0 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 #define RARVER_BETA 1 -#define RARVER_DAY 25 -#define RARVER_MONTH 10 +#define RARVER_DAY 8 +#define RARVER_MONTH 11 #define RARVER_YEAR 2023 From a657432560f18d942020c9d019ed6aebe118d617 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:08 +0000 Subject: [PATCH 23/76] Added unrar 7.0.4 --- unrar/cmdfilter.cpp | 32 ++++++++++++++++++++------------ unrar/dll.rc | 8 ++++---- unrar/list.cpp | 2 +- unrar/loclang.hpp | 3 ++- unrar/makefile | 2 +- unrar/pathfn.cpp | 27 ++++++++++++++------------- unrar/rardefs.hpp | 4 ++++ unrar/scantree.cpp | 2 +- unrar/ui.hpp | 17 +++++++++-------- unrar/version.hpp | 4 ++-- 10 files changed, 58 insertions(+), 43 deletions(-) diff --git a/unrar/cmdfilter.cpp b/unrar/cmdfilter.cpp index 2a6ff6ee..c20d8d6b 100644 --- a/unrar/cmdfilter.cpp +++ b/unrar/cmdfilter.cpp @@ -35,20 +35,28 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN } else { + // This code doesn't allow to apply -n and -x wildcard masks without + // trailing slash to folders unless these masks are * and *.*. + // See the changes history below. // 2023.03.26: Previously we removed this code completely to let - // -npath\* include empty folders in 'path' too. But then we received - // an email from user not willing -x*.avi to exclude folders like - // dir.avi with non-avi files. Also rar.txt mentions that masks like - // *.avi exclude only files. Initially we wanted masks like -npath\* - // or -xpath\* to match the entire contents of path including empty - // folders and added the special check for "*" and "*.*". - // But this is not very straightforward, when *.* and *.avi are - // processed differently, especially taking into account that - // we can specify the exact folder name without wildcards to process - // it and masks like 'dir*\' can be used to exclude folders. - // So we decided to skip all usual wildcard masks for folders. + // 'rar a arc dir -ndir\path\*' include empty folders in 'path' too. + // But then we received an email from user not willing -x*.avi to + // exclude folders like dir.avi with non-avi files. Also rar.txt + // mentions that masks like *.avi exclude only files. Initially + // we wanted masks like -npath\* or -xpath\* to match the entire + // contents of path including empty folders and added the special + // check for "*" and "*.*". But this is not very straightforward, + // when *.* and *.avi are processed differently, especially taking + // into account that we can specify the exact folder name without + // wildcards to process it and masks like 'dir*\' can be used to + // exclude folders. So we decided to skip all usual wildcard masks + // for folders. + // 2023.11.22: We returned the special check for "*" and "*.*", + // because users expected 'rar a arc dir -xdir\*' to exclude + // everything including subfolders in 'dir'. For now we returned it + // both for -n and -x, but we can limit it to -x only if needed. std::wstring Name=PointToName(CurMask); - if (IsWildcard(Name)/* && wcscmp(Name,L"*")!=0 && wcscmp(Name,L"*.*")!=0*/) + if (IsWildcard(Name) && Name!=L"*" && Name!=L"*.*") continue; } } diff --git a/unrar/dll.rc b/unrar/dll.rc index 2c96b36d..c374bdfd 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 1, 1043 -PRODUCTVERSION 7, 0, 1, 1043 +FILEVERSION 7, 0, 2, 1058 +PRODUCTVERSION 7, 0, 2, 1058 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.0.1\0" - VALUE "ProductVersion", "7.0.1\0" + VALUE "FileVersion", "7.0.2\0" + VALUE "ProductVersion", "7.0.2\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/list.cpp b/unrar/list.cpp index 8292e69f..ad8843b7 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -431,7 +431,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); } else - mprintf(L"???????? "); + mprintf(hd.Dir ? L" ":L"???????? "); // Missing checksum is ok for folder, not for file. } mprintf(L"%ls",Name); } diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 8a61ed0e..8e4ac597 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -385,7 +385,7 @@ #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 MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u GB compression dictionary." -#define MSuggest64bit L"\n64 bit RAR version is necessary." +#define MSuggest64bit L"\n64-bit RAR version is necessary." #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" @@ -402,3 +402,4 @@ #define MDictNotAllowed L"\n%u GB dictionary exceeds %u GB limit and needs more than %u GB memory to unpack." #define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway." #define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data." +#define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary." diff --git a/unrar/makefile b/unrar/makefile index 6561bf00..ce54a024 100644 --- a/unrar/makefile +++ b/unrar/makefile @@ -3,7 +3,7 @@ # Linux using GCC CXX=c++ -CXXFLAGS=-march=native -O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else +CXXFLAGS=-march=native -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 diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index f9f0c4b7..1ed0d2f6 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -800,7 +800,7 @@ static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,ui Pos++; // Skip '+' in the beginning of time mask. } - std::wstring Mask=!GenerateMask.empty() ? GenerateMask:L"yyyymmddhhmmss"; + std::wstring Mask=!GenerateMask.empty() ? GenerateMask.substr(Pos):L"yyyymmddhhmmss"; bool QuoteMode=false; uint MAsMinutes=0; // By default we treat 'M' as months. @@ -870,18 +870,19 @@ static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,ui if (StartWeekDay%7>=4) CurWeek++; - char Field[10][11]; - - sprintf(Field[0],"%04u",rlt.Year); - sprintf(Field[1],"%02u",rlt.Month); - sprintf(Field[2],"%02u",rlt.Day); - sprintf(Field[3],"%02u",rlt.Hour); - sprintf(Field[4],"%02u",rlt.Minute); - sprintf(Field[5],"%02u",rlt.Second); - sprintf(Field[6],"%02u",(uint)CurWeek); - sprintf(Field[7],"%u",(uint)WeekDay+1); - sprintf(Field[8],"%03u",rlt.yDay+1); - sprintf(Field[9],"%05u",ArcNumber); + const size_t FieldSize=11; + char Field[10][FieldSize]; + + snprintf(Field[0],FieldSize,"%04u",rlt.Year); + snprintf(Field[1],FieldSize,"%02u",rlt.Month); + snprintf(Field[2],FieldSize,"%02u",rlt.Day); + snprintf(Field[3],FieldSize,"%02u",rlt.Hour); + snprintf(Field[4],FieldSize,"%02u",rlt.Minute); + snprintf(Field[5],FieldSize,"%02u",rlt.Second); + snprintf(Field[6],FieldSize,"%02u",(uint)CurWeek); + snprintf(Field[7],FieldSize,"%u",(uint)WeekDay+1); + snprintf(Field[8],FieldSize,"%03u",rlt.yDay+1); + snprintf(Field[9],FieldSize,"%05u",ArcNumber); const wchar *MaskChars=L"YMDHISWAEN"; diff --git a/unrar/rardefs.hpp b/unrar/rardefs.hpp index 5be04e06..433010f2 100644 --- a/unrar/rardefs.hpp +++ b/unrar/rardefs.hpp @@ -25,7 +25,11 @@ #define MAXCMTSIZE 0x40000 +#ifdef _WIN_32 +#define DefSFXName L"default32.sfx" +#else #define DefSFXName L"default.sfx" +#endif #define DefSortListName L"rarfiles.lst" // Maximum dictionary allowed by compression. Can be less than diff --git a/unrar/scantree.cpp b/unrar/scantree.cpp index 2b92feec..a6179615 100644 --- a/unrar/scantree.cpp +++ b/unrar/scantree.cpp @@ -65,7 +65,7 @@ SCAN_CODE ScanTree::GetNext(FindData *FD) if (FindCode==SCAN_DONE && GetNextMask()) continue; if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS) - if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name.c_str(),false,MATCH_WILDSUBPATH)) + if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH)) continue; break; } diff --git a/unrar/ui.hpp b/unrar/ui.hpp index d0a90de2..6788bc77 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -32,14 +32,15 @@ enum UIMESSAGE_CODE { 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_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_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, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, diff --git a/unrar/version.hpp b/unrar/version.hpp index 8e4173f0..edc56a39 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 -#define RARVER_BETA 1 -#define RARVER_DAY 8 +#define RARVER_BETA 2 +#define RARVER_DAY 23 #define RARVER_MONTH 11 #define RARVER_YEAR 2023 From 9f17788011b949b12b2d8d0d76819bf1f9796049 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:08 +0000 Subject: [PATCH 24/76] Added unrar 7.0.5 --- unrar/arccmt.cpp | 3 +- unrar/archive.hpp | 3 - unrar/arcread.cpp | 6 +- unrar/blake2sp.cpp | 4 +- unrar/coder.cpp | 8 +- unrar/coder.hpp | 2 +- unrar/crc.cpp | 186 +++++++++++++++++++++++--------------------- unrar/crc.hpp | 4 - unrar/crypt1.cpp | 4 +- unrar/crypt5.cpp | 1 + unrar/dll.rc | 8 +- unrar/file.hpp | 11 +-- unrar/hash.cpp | 152 +++++++++++++++++++++++++++++++++++- unrar/hash.hpp | 19 ++++- unrar/loclang.hpp | 4 +- unrar/model.cpp | 8 +- unrar/model.hpp | 2 +- unrar/os.hpp | 21 ++--- unrar/rar.hpp | 3 - unrar/rartypes.hpp | 2 +- unrar/rarvm.cpp | 2 +- unrar/rawint.hpp | 10 +-- unrar/rawread.cpp | 3 +- unrar/rdwrfn.hpp | 4 - unrar/rijndael.cpp | 12 +-- unrar/rijndael.hpp | 2 +- unrar/rs16.cpp | 16 ++-- unrar/sha256.cpp | 4 +- unrar/timefn.cpp | 16 ++-- unrar/timefn.hpp | 4 +- unrar/ui.hpp | 2 +- unrar/uiconsole.cpp | 2 +- unrar/uisilent.cpp | 2 +- unrar/unpack.hpp | 2 +- unrar/unpack15.cpp | 82 +++++++++---------- unrar/unpack50.cpp | 2 +- unrar/version.hpp | 6 +- 37 files changed, 382 insertions(+), 240 deletions(-) diff --git a/unrar/arccmt.cpp b/unrar/arccmt.cpp index 74283b5c..fc9dcccc 100644 --- a/unrar/arccmt.cpp +++ b/unrar/arccmt.cpp @@ -174,7 +174,8 @@ void Archive::ViewComment() auto EndPos=CmtBuf.find(0x1A); if (EndPos!=std::wstring::npos) CmtSize=EndPos; - mprintf(L"\n"); + mprintf(St(MArcComment)); + mprintf(L":\n"); OutComment(CmtBuf); } } diff --git a/unrar/archive.hpp b/unrar/archive.hpp index 675e63ae..58bdafaa 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -85,9 +85,6 @@ class Archive:public File HEADER_TYPE GetHeaderType() {return CurHeaderType;} CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} -#if 0 - void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); -#endif #ifdef USE_QOPEN bool Open(const std::wstring &Name,uint Mode=FMF_READ) override; int Read(void *Data,size_t Size) override; diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 4b8d99f2..1c59bc2f 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -424,7 +424,7 @@ size_t Archive::ReadHeader15() NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); bool CRCProcessedOnly=hd->CommentInHeader; - ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); + uint HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); if (hd->HeadCRC!=HeaderCRC) { BrokenHeader=true; @@ -507,7 +507,7 @@ size_t Archive::ReadHeader15() break; } - ushort HeaderCRC=Raw.GetCRC15(false); + uint HeaderCRC=Raw.GetCRC15(false); // Old AV header does not have header CRC properly set. // Old Unix owners header didn't include string fields into header size, @@ -649,7 +649,7 @@ size_t Archive::ReadHeader50() } int SizeToRead=int(BlockSize); - SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. + SizeToRead-=int(FirstReadSize-SizeBytes-4); // Adjust overread size bytes if any. uint HeaderSize=4+SizeBytes+(uint)BlockSize; if (SizeToRead<0 || HeaderSizeR, 0, 1 ); // Init root. - for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) + for( uint32 i = 0; i < PARALLELISM_DEGREE; ++i ) blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. S->R.last_node = 1; @@ -49,6 +49,8 @@ void Blake2ThreadData::Update() if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); #endif + // We tried to _forceinline blake2s_update and blake2s_compress_sse, + // but it didn't improve performance. blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; diff --git a/unrar/coder.cpp b/unrar/coder.cpp index 9d971a8a..6282371c 100644 --- a/unrar/coder.cpp +++ b/unrar/coder.cpp @@ -1,8 +1,8 @@ -inline unsigned int RangeCoder::GetChar() +inline byte RangeCoder::GetChar() { - return(UnpackRead->GetChar()); + return UnpackRead->GetChar(); } @@ -11,8 +11,8 @@ void RangeCoder::InitDecoder(Unpack *UnpackRead) RangeCoder::UnpackRead=UnpackRead; low=code=0; - range=uint(-1); - for (int i=0;i < 4;i++) + range=0xffffffff; + for (uint i = 0; i < 4; i++) code=(code << 8) | GetChar(); } diff --git a/unrar/coder.hpp b/unrar/coder.hpp index 7b36ff21..42c3f338 100644 --- a/unrar/coder.hpp +++ b/unrar/coder.hpp @@ -11,7 +11,7 @@ class RangeCoder inline uint GetCurrentShiftCount(uint SHIFT); inline void Decode(); inline void PutChar(unsigned int c); - inline unsigned int GetChar(); + inline byte GetChar(); uint low, code, range; struct SUBRANGE diff --git a/unrar/crc.cpp b/unrar/crc.cpp index 20d0302e..61c61628 100644 --- a/unrar/crc.cpp +++ b/unrar/crc.cpp @@ -20,7 +20,11 @@ #define USE_SLICING #endif -static uint crc_tables[8][256]; // Tables for Slicing-by-8. +static uint crc_tables[16][256]; // Tables for Slicing-by-16. + +#ifdef USE_NEON_CRC32 +static bool CRC_Neon; +#endif // Build the classic CRC32 lookup table. @@ -36,6 +40,19 @@ void InitCRC32(uint *CRCTab) C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1); CRCTab[I]=C; } + +#ifdef USE_NEON_CRC32 + #ifdef _APPLE + // getauxval isn't available in OS X + uint Value=0; + size_t Size=sizeof(Value); + int RetCode=sysctlbyname("hw.optional.armv8_crc32",&Value,&Size,NULL,0); + CRC_Neon=RetCode==0 && Value!=0; + #else + CRC_Neon=(getauxval(AT_HWCAP) & HWCAP_CRC32)!=0; + #endif +#endif + } @@ -47,7 +64,7 @@ static void InitTables() for (uint I=0;I<256;I++) // Build additional lookup tables. { uint C=crc_tables[0][I]; - for (uint J=1;J<8;J++) + for (uint J=1;J<16;J++) { C=crc_tables[0][(byte)C]^(C>>8); crc_tables[J][I]=C; @@ -63,29 +80,66 @@ uint CRC32(uint StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; +#ifdef USE_NEON_CRC32 + if (CRC_Neon) + { + for (;Size>=8;Size-=8,Data+=8) +#ifdef __clang__ + StartCRC = __builtin_arm_crc32d(StartCRC, RawGet8(Data)); +#else + StartCRC = __builtin_aarch64_crc32x(StartCRC, RawGet8(Data)); +#endif + for (;Size>0;Size--,Data++) // Process left data. +#ifdef __clang__ + StartCRC = __builtin_arm_crc32b(StartCRC, *Data); +#else + StartCRC = __builtin_aarch64_crc32b(StartCRC, *Data); +#endif + return StartCRC; + } +#endif + #ifdef USE_SLICING - // Align Data to 8 for better performance and to avoid ALLOW_MISALIGNED + // Align Data to 16 for better performance and to avoid ALLOW_MISALIGNED // check below. - for (;Size>0 && ((size_t)Data & 7);Size--,Data++) + for (;Size>0 && ((size_t)Data & 15)!=0;Size--,Data++) StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); - for (;Size>=8;Size-=8,Data+=8) + // 2023.12.06: We switched to slicing-by-16, which seems to be faster than + // slicing-by-8 on modern CPUs. Slicing-by-32 would require 32 KB for tables + // and could be limited by L1 cache size on some CPUs. + for (;Size>=16;Size-=16,Data+=16) { #ifdef BIG_ENDIAN - StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); - uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); + StartCRC ^= RawGet4(Data); + uint D1 = RawGet4(Data+4); + uint D2 = RawGet4(Data+8); + uint D3 = RawGet4(Data+12); #else + // We avoid RawGet4 here for performance reason, to access uint32 + // directly even if ALLOW_MISALIGNED isn't defined. We can do it, + // because we aligned 'Data' above. StartCRC ^= *(uint32 *) Data; - uint NextData = *(uint32 *) (Data+4); + uint D1 = *(uint32 *) (Data+4); + uint D2 = *(uint32 *) (Data+8); + uint D3 = *(uint32 *) (Data+12); #endif - StartCRC = crc_tables[7][(byte) StartCRC ] ^ - crc_tables[6][(byte)(StartCRC >> 8) ] ^ - crc_tables[5][(byte)(StartCRC >> 16)] ^ - crc_tables[4][(byte)(StartCRC >> 24)] ^ - crc_tables[3][(byte) NextData ] ^ - crc_tables[2][(byte)(NextData >> 8) ] ^ - crc_tables[1][(byte)(NextData >> 16)] ^ - crc_tables[0][(byte)(NextData >> 24)]; + StartCRC = crc_tables[15][(byte) StartCRC ] ^ + crc_tables[14][(byte)(StartCRC >> 8) ] ^ + crc_tables[13][(byte)(StartCRC >> 16)] ^ + crc_tables[12][(byte)(StartCRC >> 24)] ^ + crc_tables[11][(byte) D1 ] ^ + crc_tables[10][(byte)(D1 >> 8) ] ^ + crc_tables[ 9][(byte)(D1 >> 16)] ^ + crc_tables[ 8][(byte)(D1 >> 24)] ^ + crc_tables[ 7][(byte) D2 ] ^ + crc_tables[ 6][(byte)(D2 >> 8)] ^ + crc_tables[ 5][(byte)(D2 >> 16)] ^ + crc_tables[ 4][(byte)(D2 >> 24)] ^ + crc_tables[ 3][(byte) D3 ] ^ + crc_tables[ 2][(byte)(D3 >> 8)] ^ + crc_tables[ 1][(byte)(D3 >> 16)] ^ + crc_tables[ 0][(byte)(D3 >> 24)]; } #endif @@ -111,74 +165,6 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) #endif -#if 0 -static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64. - -void InitCRC64(uint64 *CRCTab) -{ - const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42; - for (uint I=0;I<256;I++) - { - uint64 C=I; - for (uint J=0;J<8;J++) - C=(C & 1) ? (C>>1)^poly: (C>>1); - CRCTab[I]=C; - } -} - - -static void InitTables64() -{ - InitCRC64(crc64_tables[0]); - - for (uint I=0;I<256;I++) // Build additional lookup tables. - { - uint64 C=crc64_tables[0][I]; - for (uint J=1;J<8;J++) - { - C=crc64_tables[0][(byte)C]^(C>>8); - crc64_tables[J][I]=C; - } - } -} - - -// We cannot place the intialization to CRC64(), because we use this function -// in multithreaded mode and it conflicts with multithreading. -struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64; - -uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size) -{ - byte *Data=(byte *)Addr; - - // Align Data to 8 for better performance. - for (;Size>0 && ((size_t)Data & 7)!=0;Size--,Data++) - StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); - - for (byte *DataEnd=Data+Size/8*8; Data> 8 ) ] ^ - crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^ - crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^ - crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^ - crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^ - crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^ - crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ; - } - - for (Size%=8;Size>0;Size--,Data++) // Process left data. - StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); - - return StartCRC; -} #if 0 @@ -187,6 +173,11 @@ struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC; void TestCRC() { + // This function is invoked from global object and _SSE_Version is global + // and can be initialized after this function. So we explicitly initialize + // it here to enable SSE support in Blake2sp. + _SSE_Version=GetSSEVersion(); + const uint FirstSize=300; byte b[FirstSize]; @@ -252,23 +243,38 @@ void TestCRC() const size_t BufSize=0x100000; byte *Buf=new byte[BufSize]; - memset(Buf,0,BufSize); + GetRnd(Buf,BufSize); clock_t StartTime=clock(); r32=0xffffffff; - const uint BufCount=5000; + const uint64 BufCount=5000; for (uint I=0;I>16); + Key15[3]+=ushort(P+(CRCTab[P]>>16)); } } @@ -55,7 +55,7 @@ void CryptData::Crypt15(byte *Data,size_t Count) { Key15[0]+=0x1234; Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; - Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; + Key15[2]-=ushort(CRCTab[(Key15[0] & 0x1fe)>>1]>>16); Key15[0]^=Key15[2]; Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; Key15[3]=rotrs(Key15[3]&0xffff,1,16); diff --git a/unrar/crypt5.cpp b/unrar/crypt5.cpp index bb9b2bab..f0f5e720 100644 --- a/unrar/crypt5.cpp +++ b/unrar/crypt5.cpp @@ -208,6 +208,7 @@ void ConvertHashToMAC(HashValue *Value,byte *Key) Value->CRC32=0; for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8); + Value->CRC32&=0xffffffff; // In case the variable size is larger than 32-bit. } if (Value->Type==HASH_BLAKE2) { diff --git a/unrar/dll.rc b/unrar/dll.rc index c374bdfd..b90a8047 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 2, 1058 -PRODUCTVERSION 7, 0, 2, 1058 +FILEVERSION 7, 0, 3, 1078 +PRODUCTVERSION 7, 0, 3, 1078 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.0.2\0" - VALUE "ProductVersion", "7.0.2\0" + VALUE "FileVersion", "7.0.3\0" + VALUE "ProductVersion", "7.0.3\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/file.hpp b/unrar/file.hpp index 0f0ccb67..a95cc398 100644 --- a/unrar/file.hpp +++ b/unrar/file.hpp @@ -148,14 +148,9 @@ class File #endif static size_t CopyBufferSize() { -#ifdef _WIN_ALL - // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it. - // For copying from HDD to same HDD the best performance was with 256 KB - // buffer in XP and with 1 MB buffer in Win10. - return WinNT()==WNT_WXP ? 0x40000:0x100000; -#else - return 0x100000; -#endif + // Values in 0x100000 - 0x400000 range are ok, but multithreaded CRC32 + // seems to benefit from 0x400000, especially on ARM CPUs. + return 0x400000; } }; diff --git a/unrar/hash.cpp b/unrar/hash.cpp index 106cc608..cde3db91 100644 --- a/unrar/hash.cpp +++ b/unrar/hash.cpp @@ -76,7 +76,7 @@ void DataHash::Init(HASH_TYPE Type,uint MaxThreads) if (Type==HASH_BLAKE2) blake2sp_init(blake2ctx); #ifdef RAR_SMP - DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); + DataHash::MaxThreads=Min(MaxThreads,HASH_POOL_THREADS); #endif } @@ -88,13 +88,19 @@ void DataHash::Update(const void *Data,size_t DataSize) CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); #endif if (HashType==HASH_CRC32) + { +#ifdef RAR_SMP + UpdateCRC32MT(Data,DataSize); +#else CurCRC32=CRC32(CurCRC32,Data,DataSize); +#endif + } if (HashType==HASH_BLAKE2) { #ifdef RAR_SMP - if (MaxThreads>1 && ThPool==NULL) - ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER); + if (MaxThreads>1 && ThPool==nullptr) + ThPool=new ThreadPool(HASH_POOL_THREADS); blake2ctx->ThPool=ThPool; blake2ctx->MaxThreads=MaxThreads; #endif @@ -103,6 +109,146 @@ void DataHash::Update(const void *Data,size_t DataSize) } +#ifdef RAR_SMP +THREAD_PROC(BuildCRC32Thread) +{ + DataHash::CRC32ThreadData *td=(DataHash::CRC32ThreadData *)Data; + + // Use 0 initial value to simplify combining the result with existing CRC32. + // It doesn't affect the first initial 0xffffffff in the data beginning. + // If we used 0xffffffff here, we would need to shift 0xffffffff left to + // block width and XOR it with block CRC32 to reset its initial value to 0. + td->DataCRC=CRC32(0,td->Data,td->DataSize); +} + + +// CRC is linear and distributive over addition, so CRC(a+b)=CRC(a)+CRC(b). +// Since addition in finite field is XOR, we have CRC(a^b)=CRC(a)^CRC(b). +// So CRC(aaabbb) = CRC(aaa000) ^ CRC(000bbb) = CRC(aaa000) ^ CRC(bbb), +// because CRC ignores leading zeroes. Thus to split CRC calculations +// to "aaa" and "bbb" blocks and then to threads we need to be able to +// find CRC(aaa000) knowing "aaa" quickly. We use Galois finite field to +// calculate the power of 2 to get "1000" and multiply it by "aaa". +void DataHash::UpdateCRC32MT(const void *Data,size_t DataSize) +{ + const size_t MinBlock=0x4000; + if (DataSize<2*MinBlock || MaxThreads<2) + { + CurCRC32=CRC32(CurCRC32,Data,DataSize); + return; + } + + if (ThPool==nullptr) + ThPool=new ThreadPool(HASH_POOL_THREADS); + + size_t Threads=MaxThreads; + size_t BlockSize=DataSize/Threads; + if (BlockSizeAddTask(BuildCRC32Thread,(void*)&td[I]); +#else + BuildCRC32Thread((void*)&td[I]); +#endif + } + +#ifdef USE_THREADS + ThPool->WaitDone(); +#endif // USE_THREADS + + uint StdShift=gfExpCRC(uint(8*td[0].DataSize)); + for (size_t I=0;I>=1) + Reversed|=(N & 1)<<(31-I); + return Reversed; +} + + +// Galois field multiplication modulo POLY. +uint DataHash::gfMulCRC(uint A, uint B) +{ + // For reversed 0xEDB88320 polynomial we bit reverse CRC32 before passing + // to this function, so we must use the normal polynomial here. + // We set the highest polynomial bit 33 for proper multiplication + // in case uint is larger than 32-bit. + const uint POLY=uint(0x104c11db7); + + uint R = 0 ; // Multiplication result. + while (A != 0 && B != 0) // If any of multipliers becomes 0, quit early. + { + // For non-zero lowest B bit, add A to result. + R ^= (B & 1)!=0 ? A : 0; + + // Make A twice larger before the next iteration. + // Subtract POLY to keep it modulo POLY if high bit is set. + A = (A << 1) ^ ((A & 0x80000000)!=0 ? POLY : 0); + + B >>= 1; // Move next B bit to lowest position. + } + return R; +} + + +// Calculate 2 power N with square-and-multiply algorithm. +uint DataHash::gfExpCRC(uint N) +{ + uint S = 2; // Starts from base value and contains the current square. + uint R = 1; // Exponentiation result. + while (N > 1) + { + if ((N & 1)!=0) // If N is odd. + R = gfMulCRC(R, S); + S = gfMulCRC(S, S); // Next square. + N >>= 1; + } + // We could change the loop condition to N > 0 and return R at expense + // of one additional gfMulCRC(S, S). + return gfMulCRC(R, S); +} + + void DataHash::Result(HashValue *Result) { Result->Type=HashType; diff --git a/unrar/hash.hpp b/unrar/hash.hpp index 6315680e..0189113e 100644 --- a/unrar/hash.hpp +++ b/unrar/hash.hpp @@ -32,7 +32,24 @@ class DataHash; class DataHash { + public: + struct CRC32ThreadData + { + void *Data; + size_t DataSize; + uint DataCRC; + }; private: + void UpdateCRC32MT(const void *Data,size_t DataSize); + uint BitReverse32(uint N); + uint gfMulCRC(uint A, uint B); + uint gfExpCRC(uint N); + + // Speed gain seems to vanish above 8 CRC32 threads. + static const uint CRC32_POOL_THREADS=8; + // Thread pool must allow at least BLAKE2_THREADS_NUMBER threads. + static const uint HASH_POOL_THREADS=Max(BLAKE2_THREADS_NUMBER,CRC32_POOL_THREADS); + HASH_TYPE HashType; uint CurCRC32; blake2sp_state *blake2ctx; @@ -41,8 +58,6 @@ class DataHash ThreadPool *ThPool; uint MaxThreads; - // Upper limit for maximum threads to prevent wasting threads in pool. - static const uint MaxHashThreads=8; #endif public: DataHash(); diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 8e4ac597..3459e9f7 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -229,12 +229,11 @@ #define MMainHeaderBroken L"\nMain archive header is corrupt" #define MLogFileHead L"\n%s - the file header is corrupt" #define MLogProtectHead L"The data recovery header is corrupt" +#define MArcComment L"\nArchive comment" #define MReadStdinCmt L"\nReading comment from stdin\n" #define MReadCommFrom L"\nReading comment from %s" #define MDelComment L"\nDeleting comment from %s" #define MAddComment L"\nAdding comment to %s" -#define MFCommAdd L"\nAdding file comments" -#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n" #define 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" @@ -285,7 +284,6 @@ #define MListNTACLHead L"\n NTFS security data" #define MListStrmHead L"\n NTFS stream: %s" #define MListUnkHead L"\n Unknown subheader type: 0x%04x" -#define MFileComment L"\nComment: " #define MYes L"Yes" #define MNo L"No" #define MListNoFiles L" 0 files\n" diff --git a/unrar/model.cpp b/unrar/model.cpp index e4f9e3c5..69d9267f 100644 --- a/unrar/model.cpp +++ b/unrar/model.cpp @@ -339,13 +339,13 @@ inline void ModelPPM::UpdateModel() else { cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf); - pc->U.SummFreq += cf; + pc->U.SummFreq += (ushort)cf; } p=pc->U.Stats+ns1; p->Successor=Successor; p->Symbol = fs.Symbol; - p->Freq = cf; - pc->NumStats=++ns1; + p->Freq = (byte)cf; + pc->NumStats=(ushort)++ns1; } MaxContext=MinContext=fs.Successor; return; @@ -542,7 +542,7 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model) Model->CharMask[(*pps)->Symbol]=Model->EscCount; pps++; } while ( --i ); - psee2c->Summ += Model->Coder.SubRange.scale; + psee2c->Summ += (ushort)Model->Coder.SubRange.scale; Model->NumMasked = NumStats; } return true; diff --git a/unrar/model.hpp b/unrar/model.hpp index 52abc89b..2c9fa404 100644 --- a/unrar/model.hpp +++ b/unrar/model.hpp @@ -25,7 +25,7 @@ struct RARPPM_SEE2_CONTEXT : RARPPM_DEF } uint getMean() { - uint RetVal=GET_SHORT16(Summ) >> Shift; + short RetVal=GET_SHORT16(Summ) >> Shift; Summ -= RetVal; return RetVal+(RetVal == 0); } diff --git a/unrar/os.hpp b/unrar/os.hpp index 8bfc05cf..c399e08f 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -35,15 +35,8 @@ #define _UNICODE // Set _T() macro to convert from narrow to wide strings. #endif -#if 0 -// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR, -// but still keep SFX modules XP compatible. -#define WINVER _WIN32_WINNT_VISTA -#define _WIN32_WINNT _WIN32_WINNT_VISTA -#else #define WINVER _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP -#endif #if !defined(ZIPSFX) #define RAR_SMP @@ -158,13 +151,18 @@ #endif #endif -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRYPTO) +#if defined(__aarch64__) && (defined(__ARM_FEATURE_CRYPTO) || defined(__ARM_FEATURE_CRC32)) #include #ifndef _APPLE #include #include #endif -#define USE_NEON // Neon SIMD instructions for AArch64 version. +#ifdef __ARM_FEATURE_CRYPTO +#define USE_NEON_AES +#endif +#ifdef __ARM_FEATURE_CRC32 +#define USE_NEON_CRC32 +#endif #endif #ifdef S_IFLNK @@ -220,12 +218,7 @@ #endif // _UNIX -#if 0 - #define MSGID_INT - typedef int MSGID; -#else typedef const wchar* MSGID; -#endif #ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required. #define SSE_ALIGNMENT 1 diff --git a/unrar/rar.hpp b/unrar/rar.hpp index 633ea0e7..38cd7444 100644 --- a/unrar/rar.hpp +++ b/unrar/rar.hpp @@ -84,9 +84,6 @@ #include "global.hpp" -#if 0 -#include "benchmark.hpp" -#endif diff --git a/unrar/rartypes.hpp b/unrar/rartypes.hpp index 3d3111bc..a612c345 100644 --- a/unrar/rartypes.hpp +++ b/unrar/rartypes.hpp @@ -5,7 +5,7 @@ typedef uint8_t byte; // Unsigned 8 bits. typedef uint16_t ushort; // Preferably 16 bits, but can be more. -typedef unsigned int uint; // 32 bits or more. +typedef unsigned int uint; // Preferably 32 bits, likely can be more. typedef uint32_t uint32; // 32 bits exactly. typedef int32_t int32; // Signed 32 bits exactly. typedef uint64_t uint64; // 64 bits exactly. diff --git a/unrar/rarvm.cpp b/unrar/rarvm.cpp index 8d8675a3..c44e0210 100644 --- a/unrar/rarvm.cpp +++ b/unrar/rarvm.cpp @@ -246,7 +246,7 @@ bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) } else Predicted=PrevByte; - DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); + PrevByte=DestData[I]=(byte)(Predicted-*(SrcData++)); } } for (uint I=PosR,Border=DataSize-2;I>16); D[3]=(byte)(Field>>24); #else - *(uint32 *)Data=Field; + *(uint32 *)Data=(uint32)Field; #endif } @@ -107,12 +107,12 @@ inline uint64 RawGetBE8(const byte *m) // Save integer to memory as big endian. -inline void RawPutBE4(uint32 i,byte *mem) +inline void RawPutBE4(uint i,byte *mem) { #if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) - *(uint32*)mem = _byteswap_ulong(i); + *(uint32*)mem = _byteswap_ulong((uint32)i); #elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) - *(uint32*)mem = __builtin_bswap32(i); + *(uint32*)mem = __builtin_bswap32((uint32)i); #else mem[0]=byte(i>>24); mem[1]=byte(i>>16); diff --git a/unrar/rawread.cpp b/unrar/rawread.cpp index b58b11b3..7abb1339 100644 --- a/unrar/rawread.cpp +++ b/unrar/rawread.cpp @@ -96,8 +96,7 @@ uint RawRead::Get4() { if (ReadPos+3>8; - ((byte *)&T1L)[I]=gfMul(I<<4,M); - ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8; - ((byte *)&T2L)[I]=gfMul(I<<8,M); - ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8; - ((byte *)&T3L)[I]=gfMul(I<<12,M); - ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8; + ((byte *)&T0L)[I]=byte(gfMul(I,M)); + ((byte *)&T0H)[I]=byte(gfMul(I,M)>>8); + ((byte *)&T1L)[I]=byte(gfMul(I<<4,M)); + ((byte *)&T1H)[I]=byte(gfMul(I<<4,M)>>8); + ((byte *)&T2L)[I]=byte(gfMul(I<<8,M)); + ((byte *)&T2H)[I]=byte(gfMul(I<<8,M)>>8); + ((byte *)&T3L)[I]=byte(gfMul(I<<12,M)); + ((byte *)&T3H)[I]=byte(gfMul(I<<12,M)>>8); } size_t Pos=0; diff --git a/unrar/sha256.cpp b/unrar/sha256.cpp index e18a21d6..4c93aba7 100644 --- a/unrar/sha256.cpp +++ b/unrar/sha256.cpp @@ -63,7 +63,7 @@ static void sha256_transform(sha256_context *ctx) for (uint I = 0; I < 64; I++) { - uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I]; + uint32 T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I]; // It is possible to eliminate variable copying if we unroll loop // and rename variables every time. But my test did not show any speed @@ -74,7 +74,7 @@ static void sha256_transform(sha256_context *ctx) v[4] = v[3] + T1; // It works a little faster when moved here from beginning of loop. - uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]); + uint32 T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]); v[3] = v[2]; v[2] = v[1]; diff --git a/unrar/timefn.cpp b/unrar/timefn.cpp index ceac8300..aa90715d 100644 --- a/unrar/timefn.cpp +++ b/unrar/timefn.cpp @@ -71,12 +71,12 @@ void RarTime::SetLocal(RarLocalTime *lt) { #ifdef _WIN_ALL SYSTEMTIME st; - st.wYear=lt->Year; - st.wMonth=lt->Month; - st.wDay=lt->Day; - st.wHour=lt->Hour; - st.wMinute=lt->Minute; - st.wSecond=lt->Second; + st.wYear=(WORD)lt->Year; + st.wMonth=(WORD)lt->Month; + st.wDay=(WORD)lt->Day; + st.wHour=(WORD)lt->Hour; + st.wMinute=(WORD)lt->Minute; + st.wSecond=(WORD)lt->Second; st.wMilliseconds=0; st.wDayOfWeek=0; FILETIME lft; @@ -325,14 +325,14 @@ void RarTime::Adjust(int64 ns) #ifndef SFX_MODULE -const wchar *GetMonthName(int Month) +const wchar *GetMonthName(uint Month) { return uiGetMonthName(Month); } #endif -bool IsLeapYear(int Year) +bool IsLeapYear(uint Year) { return (Year&3)==0 && (Year%100!=0 || Year%400==0); } diff --git a/unrar/timefn.hpp b/unrar/timefn.hpp index 49b61e85..6c265ec0 100644 --- a/unrar/timefn.hpp +++ b/unrar/timefn.hpp @@ -70,7 +70,7 @@ class RarTime void Adjust(int64 ns); }; -const wchar *GetMonthName(int Month); -bool IsLeapYear(int Year); +const wchar *GetMonthName(uint Month); +bool IsLeapYear(uint Year); #endif diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 6788bc77..c51b5d50 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -112,7 +112,7 @@ bool uiAskRepeatWrite(const std::wstring &FileName,bool DiskFull); bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,uint64 MaxDictSize); #ifndef SFX_MODULE -const wchar *uiGetMonthName(int Month); +const wchar *uiGetMonthName(uint Month); #endif class uiMsgStore diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index b58293ba..1b77988c 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -486,7 +486,7 @@ bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,u #ifndef SFX_MODULE -const wchar *uiGetMonthName(int Month) +const wchar *uiGetMonthName(uint Month) { static MSGID MonthID[12]={ MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, diff --git a/unrar/uisilent.cpp b/unrar/uisilent.cpp index a1b49885..806e4681 100644 --- a/unrar/uisilent.cpp +++ b/unrar/uisilent.cpp @@ -74,7 +74,7 @@ bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,u #ifndef SFX_MODULE -const wchar *uiGetMonthName(int Month) +const wchar *uiGetMonthName(uint Month) { return L""; } diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index 0de65687..5ed904a8 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -398,7 +398,7 @@ class Unpack:PackDef bool ExtraDist; // Allow distances up to 1 TB. - uint GetChar() + byte GetChar() { if (Inp.InAddr>BitInput::MAX_SIZE-30) { diff --git a/unrar/unpack15.cpp b/unrar/unpack15.cpp index 700c4e1f..683d945a 100644 --- a/unrar/unpack15.cpp +++ b/unrar/unpack15.cpp @@ -1,40 +1,40 @@ #define STARTL1 2 -static unsigned int DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, +static uint DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, 0xee00,0xf000,0xf200,0xf200,0xffff}; -static unsigned int PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; +static uint PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; #define STARTL2 3 -static unsigned int DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, +static uint DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, 0xf000,0xf200,0xf240,0xffff}; -static unsigned int PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; +static uint PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; #define STARTHF0 4 -static unsigned int DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, +static uint DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, 0xf200,0xf200,0xffff}; -static unsigned int PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; +static uint PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; #define STARTHF1 5 -static unsigned int DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, +static uint DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, 0xf7e0,0xffff}; -static unsigned int PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; +static uint PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; #define STARTHF2 5 -static unsigned int DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, +static uint DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, 0xffff,0xffff}; -static unsigned int PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; +static uint PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; #define STARTHF3 6 -static unsigned int DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, +static uint DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, 0xffff}; -static unsigned int PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; +static uint PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; #define STARTHF4 8 -static unsigned int DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; -static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; +static uint DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; +static uint PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; void Unpack::Unpack15(bool Solid) @@ -119,27 +119,27 @@ void Unpack::Unpack15(bool Solid) void Unpack::ShortLZ() { - static unsigned int ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; - static unsigned int ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, + static uint ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; + static uint ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; - static unsigned int ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; - static unsigned int ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, + static uint ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; + static uint ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; - unsigned int Length,SaveLength; - unsigned int LastDistance; - unsigned int Distance; + uint Length,SaveLength; + uint LastDistance; + uint Distance; int DistancePlace; NumHuf=0; - unsigned int BitField=Inp.fgetbits(); + uint BitField=Inp.fgetbits(); if (LCount==2) { Inp.faddbits(1); if (BitField >= 0x8000) { - CopyString15((unsigned int)LastDist,LastLength); + CopyString15(LastDist,LastLength); return; } BitField <<= 1; @@ -171,7 +171,7 @@ void Unpack::ShortLZ() if (Length == 9) { LCount++; - CopyString15((unsigned int)LastDist,LastLength); + CopyString15(LastDist,LastLength); return; } if (Length == 14) @@ -217,8 +217,8 @@ void Unpack::ShortLZ() if (--DistancePlace != -1) { LastDistance=ChSetA[DistancePlace]; - ChSetA[DistancePlace+1]=LastDistance; - ChSetA[DistancePlace]=Distance; + ChSetA[DistancePlace+1]=(ushort)LastDistance; + ChSetA[DistancePlace]=(ushort)Distance; } Length+=2; OldDist[OldDistPtr++] = ++Distance; @@ -231,10 +231,10 @@ void Unpack::ShortLZ() void Unpack::LongLZ() { - unsigned int Length; - unsigned int Distance; - unsigned int DistancePlace,NewDistancePlace; - unsigned int OldAvr2,OldAvr3; + uint Length; + uint Distance; + uint DistancePlace,NewDistancePlace; + uint OldAvr2,OldAvr3; NumHuf=0; Nlzb+=16; @@ -245,7 +245,7 @@ void Unpack::LongLZ() } OldAvr2=AvrLn2; - unsigned int BitField=Inp.fgetbits(); + uint BitField=Inp.fgetbits(); if (AvrLn2 >= 122) Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); else @@ -289,7 +289,7 @@ void Unpack::LongLZ() } ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; - ChSetB[NewDistancePlace]=Distance; + ChSetB[NewDistancePlace]=(ushort)Distance; Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; Inp.faddbits(7); @@ -323,12 +323,12 @@ void Unpack::LongLZ() void Unpack::HuffDecode() { - unsigned int CurByte,NewBytePlace; - unsigned int Length; - unsigned int Distance; + uint CurByte,NewBytePlace; + uint Length; + uint Distance; int BytePlace; - unsigned int BitField=Inp.fgetbits(); + uint BitField=Inp.fgetbits(); if (AvrPlc > 0x75ff) BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); @@ -395,14 +395,14 @@ void Unpack::HuffDecode() } ChSet[BytePlace]=ChSet[NewBytePlace]; - ChSet[NewBytePlace]=CurByte; + ChSet[NewBytePlace]=(ushort)CurByte; } void Unpack::GetFlagsBuf() { - unsigned int Flags,NewFlagsPlace; - unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + uint Flags,NewFlagsPlace; + uint FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); // Our Huffman table stores 257 items and needs all them in other parts // of code such as when StMode is on, so the first item is control item. @@ -423,7 +423,7 @@ void Unpack::GetFlagsBuf() } ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace]; - ChSetC[NewFlagsPlace]=Flags; + ChSetC[NewFlagsPlace]=(ushort)Flags; } @@ -446,7 +446,7 @@ void Unpack::UnpInitData15(bool Solid) void Unpack::InitHuff() { - for (unsigned int I=0;I<256;I++) + for (ushort I=0;I<256;I++) { ChSet[I]=ChSetB[I]=I<<8; ChSetA[I]=I; diff --git a/unrar/unpack50.cpp b/unrar/unpack50.cpp index 075eb691..be9eba53 100644 --- a/unrar/unpack50.cpp +++ b/unrar/unpack50.cpp @@ -565,7 +565,7 @@ bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) return false; Inp.faddbits((8-Inp.InBit)&7); - byte BlockFlags=Inp.fgetbits()>>8; + byte BlockFlags=byte(Inp.fgetbits()>>8); Inp.faddbits(8); uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. diff --git a/unrar/version.hpp b/unrar/version.hpp index edc56a39..d3c26593 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 -#define RARVER_BETA 2 -#define RARVER_DAY 23 -#define RARVER_MONTH 11 +#define RARVER_BETA 3 +#define RARVER_DAY 13 +#define RARVER_MONTH 12 #define RARVER_YEAR 2023 From 0a08604b53d3d65f90caf83ce47d5189e74a12a3 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:09 +0000 Subject: [PATCH 25/76] Added unrar 7.0.6 --- unrar/cmddata.cpp | 19 ++++++++++++++++--- unrar/dll.rc | 10 +++++----- unrar/filcreat.cpp | 2 +- unrar/getbits.cpp | 4 ++-- unrar/list.cpp | 2 +- unrar/loclang.hpp | 11 ++++++----- unrar/pathfn.cpp | 33 ++++++++++++++++++++++----------- unrar/ui.hpp | 2 +- unrar/uicommon.cpp | 20 +++++++++++--------- unrar/uiconsole.cpp | 3 +++ unrar/ulinks.cpp | 12 ++++++++++++ unrar/unpack.cpp | 4 ++-- unrar/unpack50.cpp | 9 +++++++++ unrar/version.hpp | 8 ++++---- unrar/win32lnk.cpp | 4 ++++ 15 files changed, 99 insertions(+), 44 deletions(-) diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index c050ab6e..b7834d50 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -121,13 +121,26 @@ void CommandData::ParseArg(const wchar *Arg) // Check if last character is the path separator. size_t Length=wcslen(Arg); wchar EndChar=Length==0 ? 0:Arg[Length-1]; - bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar); + // Check if trailing path separator like path\ is present. + bool FolderArg=IsDriveDiv(EndChar) || IsPathDiv(EndChar); + + // 2024.01.05: We were asked to support exotic d:. and d:.. paths. + if (IsDriveLetter(Arg) && Arg[2]=='.' && (Arg[3]==0 || Arg[3]=='.' && Arg[4]==0)) + FolderArg=true; + + // 2024.01.06: FindFile::FastFind check below fails in Windows 10 if + // "." or ".." points at disk root. So we enforce it for "." and ".." + // optionally preceded with some path like "..\..". + size_t L=Length; + if (L>0 && Arg[L-1]=='.' && (L==1 || L>=2 && (IsPathDiv(Arg[L-2]) || + Arg[L-2]=='.' && (L==2 || L>=3 && IsPathDiv(Arg[L-3]))))) + FolderArg=true; wchar CmdChar=toupperw(Command[0]); bool Add=wcschr(L"AFUM",CmdChar)!=NULL; bool Extract=CmdChar=='X' || CmdChar=='E'; bool Repair=CmdChar=='R' && Command[1]==0; - if (EndSeparator && !Add) + if (FolderArg && !Add) ExtrPath=Arg; else if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS)) @@ -190,7 +203,7 @@ void CommandData::ParseEnvVar() #if !defined(SFX_MODULE) // Preprocess those parameters, which must be processed before the rest of -// command line. Return 'false' to stop further processing. +// command line. void CommandData::PreprocessArg(const wchar *Arg) { if (IsSwitch(Arg[0]) && !NoMoreSwitches) diff --git a/unrar/dll.rc b/unrar/dll.rc index b90a8047..fcdeec3d 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 3, 1078 -PRODUCTVERSION 7, 0, 3, 1078 +FILEVERSION 7, 0, 4, 1121 +PRODUCTVERSION 7, 0, 4, 1121 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "7.0.3\0" - VALUE "ProductVersion", "7.0.3\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" + VALUE "FileVersion", "7.0.4\0" + VALUE "ProductVersion", "7.0.4\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/filcreat.cpp b/unrar/filcreat.cpp index facd86a5..6b21aa26 100644 --- a/unrar/filcreat.cpp +++ b/unrar/filcreat.cpp @@ -22,7 +22,7 @@ bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name, // Maybe our long name matches the short name of existing file. // Let's check if we can change the short name. - if (UpdateExistingShortName(Name.c_str())) + if (UpdateExistingShortName(Name)) continue; } // Allow short name check again. It is necessary, because rename and diff --git a/unrar/getbits.cpp b/unrar/getbits.cpp index 407b633a..a5c8c3af 100644 --- a/unrar/getbits.cpp +++ b/unrar/getbits.cpp @@ -30,14 +30,14 @@ BitInput::~BitInput() void BitInput::faddbits(uint Bits) { - // Function wrapped version of inline addbits to save code size. + // Function wrapped version of inline addbits to reduce the code size. addbits(Bits); } uint BitInput::fgetbits() { - // Function wrapped version of inline getbits to save code size. + // Function wrapped version of inline getbits to reduce the code size. return getbits(); } diff --git a/unrar/list.cpp b/unrar/list.cpp index ad8843b7..3eae4de6 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -54,7 +54,7 @@ void ListArchive(CommandData *Cmd) else mprintf(L", %s", St(MListVolume)); if (Arc.Protected) - mprintf(L"%, %s", St(MListRR)); + mprintf(L", %s", St(MListRR)); if (Arc.Locked) mprintf(L", %s", St(MListLock)); if (Arc.Encrypted) diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 3459e9f7..df94a8eb 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -163,7 +163,7 @@ #define MErrRename L"\nCannot rename %s to %s" #define MAbsNextVol L"\nCannot find volume %s" #define MBreak L"\nUser break\n" -#define MAskCreatVol L"\nCreate next volume ?" +#define MAskCreatVol L"\nCreate next volume?" #define MAskNextDisk L"\nDisk full. Insert next" #define MCreatVol L"\n\nCreating %sarchive %s\n" #define MAskNextVol L"\nInsert disk with %s" @@ -210,7 +210,7 @@ #define MCRCFailed L"\n%-20s - checksum error" #define MExtrTest L"\n\nTesting archive %s\n" #define MExtracting L"\n\nExtracting from %s\n" -#define MUseCurPsw L"\n%s - use current password ?" +#define MUseCurPsw L"\n%s - use current password?" #define MCreatDir L"\nCreating %-56s" #define MExtrSkipFile L"\nSkipping %-56s" #define MExtrTestFile L"\nTesting %-56s" @@ -223,7 +223,7 @@ #define MExtrAllOk L"\nAll OK" #define MExtrTotalErr L"\nTotal errors: %ld" #define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n" -#define MAskOverwrite L"\nOverwrite %s ?" +#define MAskOverwrite L"\nOverwrite %s?" #define MAskNewName L"\nEnter new name: " #define MHeaderBroken L"\nCorrupt header is found" #define MMainHeaderBroken L"\nMain archive header is corrupt" @@ -294,7 +294,7 @@ #define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid?" #define MRprNoFiles L"\nNo files found" #define MLogUnexpEOF L"\nUnexpected end of archive" -#define MRepAskReconst L"\nReconstruct archive structure ?" +#define MRepAskReconst L"\nReconstruct archive structure?" #define MRRSearch L"\nSearching for recovery record" #define MAnalyzeFileData L"\nAnalyzing file data" #define MRecRNotFound L"\nData recovery record not found" @@ -311,7 +311,7 @@ #define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n" #define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s" #define MSymLinkExists L"\nWARNING: Symbolic link %s already exists" -#define MAskRetryCreate L"\nCannot create %s. Retry ?" +#define MAskRetryCreate L"\nCannot create %s. Retry?" #define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s" #define MFileRO L"\n%s is read-only" #define MACLGetError L"\nWARNING: Cannot get %s security data\n" @@ -401,3 +401,4 @@ #define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway." #define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data." #define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary." +#define MSkipUnsafeLink L"\nSkipping the potentially unsafe %s -> %s link. For archives from a trustworthy source use -ola to extract it anyway." diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 1ed0d2f6..7c0f9196 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -531,10 +531,14 @@ void MakeNameUsable(std::wstring &Name,bool Extended) // No spaces or dots before the path separator are allowed on Windows // shares. But they are allowed and automatically removed at the end of - // file or folder name, so it is useless to replace them here. + // file or folder name, so we need to replace them only before + // the path separator, but not at the end of file name. // Since such files or folders are created successfully, a supposed - // conversion here would never be invoked. - if ((Name[I]==' ' || Name[I]=='.') && IsPathDiv(Name[I+1])) + // conversion at the end of file name would never be invoked here. + // While converting dots, we preserve "." and ".." path components, + // such as when specifying ".." in the destination path. + if (IsPathDiv(Name[I+1]) && (Name[I]==' ' || Name[I]=='.' && I>0 && + !IsPathDiv(Name[I-1]) && (Name[I-1]!='.' || I>1 && !IsPathDiv(Name[I-2])))) Name[I]='_'; } #else @@ -1077,17 +1081,24 @@ void MakeNameCompatible(std::wstring &Name) if (I+1==Name.size() || IsPathDiv(Name[I+1])) while (I>=0 && (Name[I]=='.' || Name[I]==' ')) { - if (I==0) + if (I==0 && Name[I]==' ') { // Windows 10 Explorer can't rename or delete " " files and folders. - if (Name[I]==' ') - Name[I]='_'; // Convert " /path" to "_/path". - break; // Allow ./path1 paths. - } - // Permit path1/./path2 and ../path1 paths. Leading dots are possible - // if specified by user in the destination path. - if (Name[I]=='.' && I>0 && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) + Name[I]='_'; // Convert " /path" to "_/path". break; + } + if (Name[I]=='.') + { + // 2024.05.01: Permit ./path1, path1/./path2, ../path1, + // path1/../path2 and exotic Win32 d:.\path1, d:..\path1 paths + // requested by user. Leading dots are possible here if specified + // by user in the destination path. + if (I==0 || IsPathDiv(Name[I-1]) || I==2 && IsDriveLetter(Name)) + break; + if (I>=1 && Name[I-1]=='.' && (I==1 || IsPathDiv(Name[I-2]) || + I==3 && IsDriveLetter(Name))) + break; + } Name.erase(I,1); I--; } diff --git a/unrar/ui.hpp b/unrar/ui.hpp index c51b5d50..f37001be 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -40,7 +40,7 @@ enum UIMESSAGE_CODE { UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW, - UIERROR_ADJUSTVALUE, + UIERROR_ADJUSTVALUE, UIERROR_SKIPUNSAFELINK, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, diff --git a/unrar/uicommon.cpp b/unrar/uicommon.cpp index 1329ba85..d4d55128 100644 --- a/unrar/uicommon.cpp +++ b/unrar/uicommon.cpp @@ -20,20 +20,22 @@ UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,std::wstring &Name,int64 FileSiz return UIASKREP_R_REPLACE; #endif - // This check must be after OVERWRITE_AUTORENAME processing or -y switch - // would override -or. - if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL) - { - PrepareToDelete(Name); - return UIASKREP_R_REPLACE; - } - std::wstring NewName=Name; - UIASKREP_RESULT Choice=uiAskReplace(NewName,FileSize,FileTime,Flags); + UIASKREP_RESULT Choice=Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL ? + UIASKREP_R_REPLACE : uiAskReplace(NewName,FileSize,FileTime,Flags); if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) + { PrepareToDelete(Name); + // Overwrite the link itself instead of its target. + // For normal files we prefer to inherit file attributes, permissions + // and hard links. + FindData FD; + if (FindFile::FastFind(Name,&FD,true) && FD.IsLink) + DelFile(Name); + } + if (Choice==UIASKREP_R_REPLACEALL) { Cmd->Overwrite=OVERWRITE_ALL; diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index 1b77988c..cc1c72d6 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -349,6 +349,9 @@ void uiMsgStore::Msg() case UIERROR_ADJUSTVALUE: Log(NULL,St(MAdjustValue),Str[0],Str[1]); break; + case UIERROR_SKIPUNSAFELINK: + Log(NULL,St(MSkipUnsafeLink),Str[0],Str[1]); + break; #ifndef SFX_MODULE case UIMSG_STRING: diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index e501f96b..ea841557 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -101,7 +101,11 @@ static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, // confuse IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName.c_str(),LinkName,TargetW.c_str()))) + { + uiMsg(UIERROR_SKIPUNSAFELINK,Arc.FileHead.FileName,TargetW); + ErrHandler.SetErrorCode(RARX_WARNING); return false; + } UpLink=Target.find("..")!=std::string::npos; return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); } @@ -121,7 +125,11 @@ static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) // We escape ? as \? to avoid "trigraph" warning if (Target.rfind("\\??\\",0)!=std::string::npos || Target.rfind("/\?\?/",0)!=std::string::npos) + { + uiMsg(UIERROR_SLINKCREATE,nullptr,L"\"" + hd->FileName + L"\" -> \"" + hd->RedirName + L"\""); + ErrHandler.SetErrorCode(RARX_WARNING); return false; + } DosSlashToUnix(Target,Target); } @@ -133,6 +141,10 @@ static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) // IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || !IsRelativeSymlinkSafe(Cmd,hd->FileName.c_str(),Name,TargetW.c_str()))) + { + uiMsg(UIERROR_SKIPUNSAFELINK,hd->FileName,TargetW); + ErrHandler.SetErrorCode(RARX_WARNING); return false; + } return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } diff --git a/unrar/unpack.cpp b/unrar/unpack.cpp index 86663efb..99c6f15d 100644 --- a/unrar/unpack.cpp +++ b/unrar/unpack.cpp @@ -36,8 +36,8 @@ Unpack::Unpack(ComprDataIO *DataIO) MaxWinMask=0; // Perform initialization, which should be done only once for all files. - // It prevents crash if first DoUnpack call is later made with wrong - // (true) 'Solid' value. + // It prevents crash if first unpacked file has the wrong "true" Solid flag, + // so first DoUnpack call is made with the wrong "true" Solid value later. UnpInitData(false); #ifndef SFX_MODULE // RAR 1.5 decompression initialization diff --git a/unrar/unpack50.cpp b/unrar/unpack50.cpp index be9eba53..830dfd46 100644 --- a/unrar/unpack50.cpp +++ b/unrar/unpack50.cpp @@ -588,10 +588,19 @@ bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) Header.BlockSize=BlockSize; byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + + // 2024.01.04: In theory the valid block can have Header.BlockSize == 0 + // and Header.TablePresent == false in case the only block purpose is to + // store Header.LastBlockInFile flag if it didn't fit into previous block. + // So we do not reject Header.BlockSize == 0. Though currently RAR doesn't + // seem to issue such zero length blocks. if (CheckSum!=SavedCheckSum) return false; Header.BlockStart=Inp.InAddr; + + // We called Inp.faddbits(8) above, thus Header.BlockStart can't be 0 here. + // So there is no overflow even if Header.BlockSize is 0. ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); Header.LastBlockInFile=(BlockFlags & 0x40)!=0; diff --git a/unrar/version.hpp b/unrar/version.hpp index d3c26593..8b18e8a4 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 -#define RARVER_BETA 3 -#define RARVER_DAY 13 -#define RARVER_MONTH 12 -#define RARVER_YEAR 2023 +#define RARVER_BETA 4 +#define RARVER_DAY 24 +#define RARVER_MONTH 1 +#define RARVER_YEAR 2024 diff --git a/unrar/win32lnk.cpp b/unrar/win32lnk.cpp index 6d3a1bc9..677c2735 100644 --- a/unrar/win32lnk.cpp +++ b/unrar/win32lnk.cpp @@ -67,7 +67,11 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm. if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) || !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + { + uiMsg(UIERROR_SKIPUNSAFELINK,hd->FileName,hd->RedirName); + ErrHandler.SetErrorCode(RARX_WARNING); return false; + } CreatePath(Name,true,Cmd->DisableNames); From 95fe328cc24443bd21a9cfce36b50c9cd859b955 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:09 +0000 Subject: [PATCH 26/76] Added unrar 7.0.7 --- unrar/dll.cpp | 4 ++++ unrar/dll.rc | 8 ++++---- unrar/scantree.cpp | 4 ++-- unrar/unicode.cpp | 6 ++++-- unrar/version.hpp | 6 +++--- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/unrar/dll.cpp b/unrar/dll.cpp index cc5c5672..de8796dc 100644 --- a/unrar/dll.cpp +++ b/unrar/dll.cpp @@ -390,6 +390,10 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); // Now we process extra file information if any. + // It is important to do it in the same ProcessFile(), because caller + // app can rely on this behavior, for example, to overwrite + // the extracted Mark of the Web with propagated from archive + // immediately after ProcessFile() call. // // Archive can be closed if we process volumes, next volume is missing // and current one is already removed or deleted. So we need to check diff --git a/unrar/dll.rc b/unrar/dll.rc index fcdeec3d..3cf8bbb4 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 4, 1121 -PRODUCTVERSION 7, 0, 4, 1121 +FILEVERSION 7, 0, 100, 1154 +PRODUCTVERSION 7, 0, 100, 1154 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.0.4\0" - VALUE "ProductVersion", "7.0.4\0" + VALUE "FileVersion", "7.0.0\0" + VALUE "ProductVersion", "7.0.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/scantree.cpp b/unrar/scantree.cpp index a6179615..43131777 100644 --- a/unrar/scantree.cpp +++ b/unrar/scantree.cpp @@ -104,10 +104,10 @@ bool ScanTree::ExpandFolderMask() { FD.Name+=CurMask.substr(SlashPos); - // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched + // Treat dir*\*, dir*\*.* or dir*\ as dir, so empty 'dir' is also matched // by such mask. Skipping empty dir with dir*\*.* confused some users. std::wstring LastMask=PointToName(FD.Name); - if (LastMask==L"*" || LastMask==L"*.*") + if (LastMask==L"*" || LastMask==L"*.*" || LastMask.empty()) RemoveNameFromPath(FD.Name); ExpandedFolderList.AddString(FD.Name); diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index 997f8b2b..812e35ba 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -677,8 +677,10 @@ int wcsnicomp(const wchar *s1,const wchar *s2,size_t n) // If we specify 'n' exceeding the actual string length, CompareString goes // beyond the trailing zero and compares garbage. So we need to limit 'n' // to real string length. - size_t l1=Min(wcslen(s1)+1,n); - size_t l2=Min(wcslen(s2)+1,n); + size_t sl1=wcslen(s1); // Pre-compute to not call wcslen() in Min() twice. + size_t l1=Min(sl1+1,n); + size_t sl2=wcslen(s2); // Pre-compute to not call wcslen() in Min() twice. + size_t l2=Min(sl2+1,n); return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; #else if (n==0) diff --git a/unrar/version.hpp b/unrar/version.hpp index 8b18e8a4..aca1bcc1 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 0 -#define RARVER_BETA 4 -#define RARVER_DAY 24 -#define RARVER_MONTH 1 +#define RARVER_BETA 0 +#define RARVER_DAY 26 +#define RARVER_MONTH 2 #define RARVER_YEAR 2024 From 9a5d712d99a8b6f1c4e89a9bc0d1a8354ad6f944 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:10 +0000 Subject: [PATCH 27/76] Added unrar 7.0.8 --- unrar/arccmt.cpp | 5 ++-- unrar/arcread.cpp | 63 ++++++++++++++++++++++++---------------------- unrar/cmddata.cpp | 2 +- unrar/crypt.cpp | 6 +++-- unrar/crypt.hpp | 2 +- unrar/crypt5.cpp | 13 +++------- unrar/dll.rc | 8 +++--- unrar/extract.cpp | 8 +++--- unrar/filefn.cpp | 3 ++- unrar/isnt.cpp | 2 +- unrar/options.hpp | 2 +- unrar/os.hpp | 5 ++++ unrar/rdwrfn.cpp | 13 ++++++++-- unrar/rdwrfn.hpp | 2 +- unrar/rijndael.cpp | 1 + unrar/unpack15.cpp | 3 ++- unrar/version.hpp | 8 +++--- 17 files changed, 80 insertions(+), 66 deletions(-) diff --git a/unrar/arccmt.cpp b/unrar/arccmt.cpp index fc9dcccc..f23a7dd7 100644 --- a/unrar/arccmt.cpp +++ b/unrar/arccmt.cpp @@ -101,9 +101,8 @@ bool Archive::DoGetComment(std::wstring &CmtData) // 4x memory for OEM to UTF-8 output here. OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); #endif -// CmtData.resize(UnpDataSize+1); - CharToWide((const char *)UnpData,CmtData); -// CmtData.resize(wcslen(CmtData->data())); + std::string UnpStr((char*)UnpData,UnpDataSize); + CharToWide(UnpStr,CmtData); } } } diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index 1c59bc2f..825da726 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -122,7 +122,7 @@ void Archive::BrokenHeaderMsg() void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info) { uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); - ErrHandler.SetErrorCode(RARX_WARNING); + ErrHandler.SetErrorCode(RARX_FATAL); } @@ -587,10 +587,10 @@ size_t Archive::ReadHeader50() RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); byte PswCheck[SIZE_PSWCHECK]; - HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); + bool EncSet=HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); // Verify password validity. If header is damaged, we cannot rely on // password check value, because it can be damaged too. - if (CryptHead.UsePswCheck && !BrokenHeader && + if (EncSet && CryptHead.UsePswCheck && !BrokenHeader && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P. @@ -1050,35 +1050,38 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) hd->Lg2Count=Raw->Get1(); if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count)); - Raw->GetB(hd->Salt,SIZE_SALT50); - Raw->GetB(hd->InitV,SIZE_INITV); - if (hd->UsePswCheck) + else { - Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); - - // It is important to know if password check data is valid. - // If it is damaged and header CRC32 fails to detect it, - // archiver would refuse to decompress a possibly valid file. - // Since we want to be sure distinguishing a wrong password - // or corrupt file data, we use 64-bit password check data - // and to control its validity we use 32 bits of password - // check data SHA-256 additionally to 32-bit header CRC32. - byte csum[SIZE_PSWCHECK_CSUM]; - Raw->GetB(csum,SIZE_PSWCHECK_CSUM); - - byte Digest[SHA256_DIGEST_SIZE]; - sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest); - - hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; - - // RAR 5.21 and earlier set PswCheck field in service records to 0 - // even if UsePswCheck was present. - if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) - hd->UsePswCheck=0; + Raw->GetB(hd->Salt,SIZE_SALT50); + Raw->GetB(hd->InitV,SIZE_INITV); + if (hd->UsePswCheck) + { + Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); + + // It is important to know if password check data is valid. + // If it is damaged and header CRC32 fails to detect it, + // archiver would refuse to decompress a possibly valid file. + // Since we want to be sure distinguishing a wrong password + // or corrupt file data, we use 64-bit password check data + // and to control its validity we use 32 bits of password + // check data SHA-256 additionally to 32-bit header CRC32. + byte csum[SIZE_PSWCHECK_CSUM]; + Raw->GetB(csum,SIZE_PSWCHECK_CSUM); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest); + + hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + + // RAR 5.21 and earlier set PswCheck field in service records to 0 + // even if UsePswCheck was present. + if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) + hd->UsePswCheck=0; + } + hd->SaltSet=true; + hd->CryptMethod=CRYPT_RAR50; + hd->Encrypted=true; } - hd->SaltSet=true; - hd->CryptMethod=CRYPT_RAR50; - hd->Encrypted=true; } } break; diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index b7834d50..37bcf5b0 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -426,7 +426,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; case '4': // Convert slashes here than before every comparison. - SlashToNative(Switch+3,ArcPath); + SlashToNative(Switch+3,ExclArcPath); break; default: BadSwitch(Switch); diff --git a/unrar/crypt.cpp b/unrar/crypt.cpp index 3a9342cd..d5c1d9d6 100644 --- a/unrar/crypt.cpp +++ b/unrar/crypt.cpp @@ -60,6 +60,8 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, WideToChar(PwdW,PwdA,ASIZE(PwdA)); PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. + bool Success=true; + switch(Method) { #ifndef SFX_MODULE @@ -77,12 +79,12 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, SetKey30(Encrypt,Password,PwdW,Salt); break; case CRYPT_RAR50: - SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); break; } cleandata(PwdA,sizeof(PwdA)); cleandata(PwdW,sizeof(PwdW)); - return true; + return Success; } diff --git a/unrar/crypt.hpp b/unrar/crypt.hpp index f7d97e8a..983d4bf2 100644 --- a/unrar/crypt.hpp +++ b/unrar/crypt.hpp @@ -79,7 +79,7 @@ class CryptData void DecryptBlock20(byte *Buf); void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); - void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); + bool SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); KDF3CacheItem KDF3Cache[4]; uint KDF3CachePos; diff --git a/unrar/crypt5.cpp b/unrar/crypt5.cpp index f0f5e720..2183c8f0 100644 --- a/unrar/crypt5.cpp +++ b/unrar/crypt5.cpp @@ -128,20 +128,12 @@ void pbkdf2(const byte *Pwd, size_t PwdLength, } -void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, +bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, byte *PswCheck) { if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) - { - // Initialize these fields to prevent uninitialized data access warnings - // by analyzing tools when accessing returned data. - if (HashKey!=nullptr) - memset(HashKey,0,SHA256_DIGEST_SIZE); - if (PswCheck!=nullptr) - memset(PswCheck,0,SIZE_PSWCHECK); - return; - } + return false; byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; bool Found=false; @@ -194,6 +186,7 @@ void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, rin.Init(Encrypt, Key, 256, InitV); cleandata(Key,sizeof(Key)); + return true; } diff --git a/unrar/dll.rc b/unrar/dll.rc index 3cf8bbb4..9098a948 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 0, 100, 1154 -PRODUCTVERSION 7, 0, 100, 1154 +FILEVERSION 7, 1, 1, 1213 +PRODUCTVERSION 7, 1, 1, 1213 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.0.0\0" - VALUE "ProductVersion", "7.0.0\0" + VALUE "FileVersion", "7.1.1\0" + VALUE "ProductVersion", "7.1.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index f4ea12c5..f799e6a3 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -584,14 +584,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) #endif byte PswCheck[SIZE_PSWCHECK]; - DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, - Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + bool EncSet=DataIO.SetEncryption(false,Arc.FileHead.CryptMethod, + &FilePassword,Arc.FileHead.SaltSet ? Arc.FileHead.Salt:nullptr, Arc.FileHead.InitV,Arc.FileHead.Lg2Count, Arc.FileHead.HashKey,PswCheck); // If header is damaged, we cannot rely on password check value, // because it can be damaged too. - if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && + if (EncSet && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) { if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. @@ -963,7 +963,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (SetTimeAndSize) { - // We could preallocate more space that really written to broken file + // We could preallocate more space than really written to broken file // or file with crafted header. if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) CurFile.Truncate(); diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index 14316ae3..3cb25892 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -232,8 +232,9 @@ bool IsUnreadable(uint Attr) { #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); -#endif +#else return false; +#endif } diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index a1e95e22..b89b2ce5 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -40,7 +40,7 @@ static bool WMI_IsWindows10() IWbemServices *pSvc = NULL; - hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc); + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,0,NULL,NULL,&pSvc); if (FAILED(hres)) { diff --git a/unrar/options.hpp b/unrar/options.hpp index 8f0f6ad1..1061bd02 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -33,7 +33,7 @@ enum RECURSE_MODE RECURSE_NONE=0, // no recurse switches RECURSE_DISABLE, // switch -r- RECURSE_ALWAYS, // switch -r - RECURSE_WILDCARDS, // switch -r0 + RECURSE_WILDCARDS // switch -r0 }; enum OVERWRITE_MODE diff --git a/unrar/os.hpp b/unrar/os.hpp index c399e08f..b6718462 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include // For automatic pointers. @@ -211,6 +212,10 @@ #endif #endif +#ifdef __VMS +# define LITTLE_ENDIAN +#endif + // Unlike Apple x64, utimensat shall be available in all Apple M1 systems. #if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__) #define UNIX_TIME_NS // Nanosecond time precision in Unix. diff --git a/unrar/rdwrfn.cpp b/unrar/rdwrfn.cpp index 1f006883..a49a8bce 100644 --- a/unrar/rdwrfn.cpp +++ b/unrar/rdwrfn.cpp @@ -246,15 +246,24 @@ void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size) } -void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, +// Return true if encryption or decryption mode is set correctly. +bool ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, SecPassword *Password,const byte *Salt,const byte *InitV, uint Lg2Cnt,byte *HashKey,byte *PswCheck) { -#ifndef RAR_NOCRYPT +#ifdef RAR_NOCRYPT + return false; +#else if (Encrypt) + { Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + return Encryption; + } else + { Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + return Decryption; + } #endif } diff --git a/unrar/rdwrfn.hpp b/unrar/rdwrfn.hpp index 7a13d07c..58685ca3 100644 --- a/unrar/rdwrfn.hpp +++ b/unrar/rdwrfn.hpp @@ -66,7 +66,7 @@ class ComprDataIO void SetFiles(File *SrcFile,File *DestFile); void SetCommand(CmdAdd *Cmd) {Command=Cmd;} void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} - void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + bool SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); diff --git a/unrar/rijndael.cpp b/unrar/rijndael.cpp index 85024e5d..8707b578 100644 --- a/unrar/rijndael.cpp +++ b/unrar/rijndael.cpp @@ -84,6 +84,7 @@ Rijndael::Rijndael() { if (S5[0]==0) GenerateTables(); + m_uRounds = 0; CBCMode = true; // Always true for RAR. #ifdef USE_SSE AES_NI=false; diff --git a/unrar/unpack15.cpp b/unrar/unpack15.cpp index 683d945a..1019c354 100644 --- a/unrar/unpack15.cpp +++ b/unrar/unpack15.cpp @@ -474,7 +474,8 @@ void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) void Unpack::CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; - if (!FirstWinDone && Distance>UnpPtr || Distance>MaxWinSize) + // 2024.04.18: Distance can be 0 in corrupt RAR 1.5 archives. + if (!FirstWinDone && Distance>UnpPtr || Distance>MaxWinSize || Distance==0) while (Length-- > 0) { Window[UnpPtr]=0; diff --git a/unrar/version.hpp b/unrar/version.hpp index aca1bcc1..0183e15a 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 -#define RARVER_MINOR 0 -#define RARVER_BETA 0 -#define RARVER_DAY 26 -#define RARVER_MONTH 2 +#define RARVER_MINOR 1 +#define RARVER_BETA 1 +#define RARVER_DAY 25 +#define RARVER_MONTH 4 #define RARVER_YEAR 2024 From 5823bb16b5a7494407ab0ce917b34e88e9a59f6f Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:11 +0000 Subject: [PATCH 28/76] Added unrar 7.0.9 --- unrar/dll.rc | 8 ++++---- unrar/version.hpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 9098a948..c959c86c 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 1, 1, 1213 -PRODUCTVERSION 7, 1, 1, 1213 +FILEVERSION 7, 1, 100, 1230 +PRODUCTVERSION 7, 1, 100, 1230 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.1\0" - VALUE "ProductVersion", "7.1.1\0" + VALUE "FileVersion", "7.1.0\0" + VALUE "ProductVersion", "7.1.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/version.hpp b/unrar/version.hpp index 0183e15a..6987a9af 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 1 -#define RARVER_BETA 1 -#define RARVER_DAY 25 -#define RARVER_MONTH 4 +#define RARVER_BETA 0 +#define RARVER_DAY 12 +#define RARVER_MONTH 5 #define RARVER_YEAR 2024 From 55a566364a509f0a0f25036525d15e0a0b8effaf Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:11 +0000 Subject: [PATCH 29/76] Added unrar 7.1.1 --- unrar/UnRAR.vcxproj | 2 + unrar/UnRARDll.vcxproj | 2 + unrar/arccmt.cpp | 7 +- unrar/archive.cpp | 2 + unrar/archive.hpp | 5 +- unrar/arcread.cpp | 27 +++++- unrar/array.hpp | 169 ---------------------------------- unrar/blake2s.hpp | 9 +- unrar/cmddata.cpp | 78 ++++++++++++---- unrar/cmddata.hpp | 11 +++ unrar/cmdmix.cpp | 14 +-- unrar/crypt.hpp | 3 +- unrar/dll.cpp | 23 +++-- unrar/dll.hpp | 3 +- unrar/dll.rc | 8 +- unrar/extinfo.hpp | 4 - unrar/extract.cpp | 73 +++++++++++---- unrar/extract.hpp | 2 +- unrar/filefn.cpp | 68 +++++--------- unrar/filefn.hpp | 7 +- unrar/hash.cpp | 4 +- unrar/hash.hpp | 2 + unrar/largepage.cpp | 201 +++++++++++++++++++++++++++++++++++++++++ unrar/largepage.hpp | 54 +++++++++++ unrar/list.cpp | 2 +- unrar/loclang.hpp | 16 +++- unrar/makefile | 7 +- unrar/motw.cpp | 131 +++++++++++++++++++++++++++ unrar/motw.hpp | 26 ++++++ unrar/options.hpp | 13 ++- unrar/os.hpp | 6 +- unrar/rar.hpp | 5 + unrar/rardefs.hpp | 6 +- unrar/smallfn.cpp | 2 - unrar/smallfn.hpp | 1 - unrar/system.cpp | 32 +++++-- unrar/system.hpp | 3 + unrar/timefn.hpp | 12 +-- unrar/ui.hpp | 32 +++---- unrar/uiconsole.cpp | 12 +++ unrar/unpack.cpp | 16 +++- unrar/unpack.hpp | 5 +- unrar/version.hpp | 8 +- unrar/win32acl.cpp | 23 ----- unrar/win32stm.cpp | 132 +++++++++++++++++++-------- 45 files changed, 862 insertions(+), 406 deletions(-) delete mode 100644 unrar/array.hpp create mode 100644 unrar/largepage.cpp create mode 100644 unrar/largepage.hpp create mode 100644 unrar/motw.cpp create mode 100644 unrar/motw.hpp diff --git a/unrar/UnRAR.vcxproj b/unrar/UnRAR.vcxproj index 512bcf15..ac0878f2 100644 --- a/unrar/UnRAR.vcxproj +++ b/unrar/UnRAR.vcxproj @@ -238,8 +238,10 @@ + + diff --git a/unrar/UnRARDll.vcxproj b/unrar/UnRARDll.vcxproj index 72cecd8d..17ea1b4f 100644 --- a/unrar/UnRARDll.vcxproj +++ b/unrar/UnRARDll.vcxproj @@ -374,7 +374,9 @@ + + diff --git a/unrar/arccmt.cpp b/unrar/arccmt.cpp index f23a7dd7..a0fcd7b2 100644 --- a/unrar/arccmt.cpp +++ b/unrar/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/unrar/archive.cpp b/unrar/archive.cpp index c2293de7..3d293ec3 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -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/unrar/archive.hpp b/unrar/archive.hpp index 58bdafaa..895f853b 100644 --- a/unrar/archive.hpp +++ b/unrar/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/unrar/arcread.cpp b/unrar/arcread.cpp index 825da726..ab804636 100644 --- a/unrar/arcread.cpp +++ b/unrar/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/unrar/array.hpp b/unrar/array.hpp deleted file mode 100644 index ac786f71..00000000 --- a/unrar/array.hpp +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef _RAR_ARRAY_ -#define _RAR_ARRAY_ - -extern ErrorHandler ErrHandler; - -template class Array -{ - private: - T *Buffer; - size_t BufSize; - size_t AllocSize; - size_t MaxSize; - public: - Array(); - Array(size_t Size); - Array(const Array &Src); // Copy constructor. - ~Array(); - inline void CleanData(); - inline T& operator [](size_t Item) const; - inline T* operator + (size_t Pos); - inline size_t Size(); // Returns the size in items, not in bytes. - void Add(size_t Items); - void Alloc(size_t Items); - void Reset(); - void SoftReset(); - Array& operator = (const Array &Src); - void Push(T Item); - void Append(T *Item,size_t Count); - T* Addr(size_t Item) {return Buffer+Item;} - void SetMaxSize(size_t Size) {MaxSize=Size;} - T* Begin() {return Buffer;} - T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} -}; - - -template void Array::CleanData() -{ - Buffer=NULL; - BufSize=0; - AllocSize=0; - MaxSize=0; -} - - -template Array::Array() -{ - CleanData(); -} - - -template Array::Array(size_t Size) -{ - CleanData(); - Add(Size); -} - - -// Copy constructor in case we need to pass an object as value. -template Array::Array(const Array &Src) -{ - CleanData(); - Alloc(Src.BufSize); - if (Src.BufSize!=0) - memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); -} - - -template Array::~Array() -{ - if (Buffer!=NULL) - free(Buffer); -} - - -template inline T& Array::operator [](size_t Item) const -{ - return Buffer[Item]; -} - - -template inline T* Array::operator +(size_t Pos) -{ - return Buffer+Pos; -} - - -template inline size_t Array::Size() -{ - return BufSize; -} - - -template void Array::Add(size_t Items) -{ - BufSize+=Items; - if (BufSize>AllocSize) - { - if (MaxSize!=0 && BufSize>MaxSize) - { - ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); - ErrHandler.MemoryError(); - } - - size_t Suggested=AllocSize+AllocSize/4+32; - size_t NewSize=Max(BufSize,Suggested); - - T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); - if (NewBuffer==NULL) - ErrHandler.MemoryError(); - Buffer=NewBuffer; - AllocSize=NewSize; - } -} - - -template void Array::Alloc(size_t Items) -{ - if (Items>AllocSize) - Add(Items-BufSize); - else - BufSize=Items; -} - - -template void Array::Reset() -{ - if (Buffer!=NULL) - { - free(Buffer); - Buffer=NULL; - } - BufSize=0; - AllocSize=0; -} - - -// Reset buffer size, but preserve already allocated memory if any, -// so we can reuse it without wasting time to allocation. -template void Array::SoftReset() -{ - BufSize=0; -} - - -template Array& Array::operator =(const Array &Src) -{ - Reset(); - Alloc(Src.BufSize); - if (Src.BufSize!=0) - memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); - return *this; -} - - -template void Array::Push(T Item) -{ - Add(1); - (*this)[Size()-1]=Item; -} - - -template void Array::Append(T *Items,size_t Count) -{ - size_t CurSize=Size(); - Add(Count); - memcpy(Buffer+CurSize,Items,Count*sizeof(T)); -} - -#endif diff --git a/unrar/blake2s.hpp b/unrar/blake2s.hpp index 06e396a7..90b7885f 100644 --- a/unrar/blake2s.hpp +++ b/unrar/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/unrar/cmddata.cpp b/unrar/cmddata.cpp index 37bcf5b0..0a1852d3 100644 --- a/unrar/cmddata.cpp +++ b/unrar/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 (PosArcName; 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; } diff --git a/unrar/dll.hpp b/unrar/dll.hpp index ac8d53fa..3a8ded9a 100644 --- a/unrar/dll.hpp +++ b/unrar/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/unrar/dll.rc b/unrar/dll.rc index c959c86c..7a8dbf12 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 1, 100, 1230 -PRODUCTVERSION 7, 1, 100, 1230 +FILEVERSION 7, 10, 1, 1407 +PRODUCTVERSION 7, 10, 1, 1407 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.1\0" + VALUE "ProductVersion", "7.10.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extinfo.hpp b/unrar/extinfo.hpp index 11b89fef..4d0967a6 100644 --- a/unrar/extinfo.hpp +++ b/unrar/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/unrar/extract.cpp b/unrar/extract.cpp index f799e6a3..5a3c15d2 100644 --- a/unrar/extract.cpp +++ b/unrar/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; @@ -254,9 +264,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 +501,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 +560,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 +587,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 +617,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 +635,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 +697,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 +999,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 +1021,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 +1437,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 +1448,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 +1508,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/unrar/extract.hpp b/unrar/extract.hpp index 4400057a..18d8f8c9 100644 --- a/unrar/extract.hpp +++ b/unrar/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/unrar/filefn.cpp b/unrar/filefn.cpp index 3cb25892..d23f4f70 100644 --- a/unrar/filefn.cpp +++ b/unrar/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/unrar/filefn.hpp b/unrar/filefn.hpp index 4e061973..8ac7c10f 100644 --- a/unrar/filefn.hpp +++ b/unrar/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/unrar/hash.cpp b/unrar/hash.cpp index cde3db91..d07b2d24 100644 --- a/unrar/hash.cpp +++ b/unrar/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/unrar/hash.hpp b/unrar/hash.hpp index 0189113e..99488e6a 100644 --- a/unrar/hash.hpp +++ b/unrar/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/unrar/largepage.cpp b/unrar/largepage.cpp new file mode 100644 index 00000000..7b574c5f --- /dev/null +++ b/unrar/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/unrar/list.cpp b/unrar/list.cpp index 3eae4de6..15aa73a2 100644 --- a/unrar/list.cpp +++ b/unrar/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/unrar/loclang.hpp b/unrar/loclang.hpp index df94a8eb..3ee42f74 100644 --- a/unrar/loclang.hpp +++ b/unrar/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/unrar/makefile b/unrar/makefile index ce54a024..f7fe61a3 100644 --- a/unrar/makefile +++ b/unrar/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/unrar/motw.cpp b/unrar/motw.cpp new file mode 100644 index 00000000..25698fe7 --- /dev/null +++ b/unrar/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/unrar/motw.hpp b/unrar/motw.hpp new file mode 100644 index 00000000..e24feeac --- /dev/null +++ b/unrar/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/unrar/options.hpp b/unrar/options.hpp index 1061bd02..785755cf 100644 --- a/unrar/options.hpp +++ b/unrar/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_AUTO, QOPEN_NONE, 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/unrar/os.hpp b/unrar/os.hpp index b6718462..f7e841b0 100644 --- a/unrar/os.hpp +++ b/unrar/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/unrar/rar.hpp b/unrar/rar.hpp index 38cd7444..d31ecd86 100644 --- a/unrar/rar.hpp +++ b/unrar/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/unrar/rardefs.hpp b/unrar/rardefs.hpp index 433010f2..378ccc7f 100644 --- a/unrar/rardefs.hpp +++ b/unrar/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/unrar/smallfn.cpp b/unrar/smallfn.cpp index 81259d02..924e195b 100644 --- a/unrar/smallfn.cpp +++ b/unrar/smallfn.cpp @@ -15,5 +15,3 @@ int ToPercentUnlim(int64 N1,int64 N2) return 0; return (int)(N1*100/N2); } - - diff --git a/unrar/smallfn.hpp b/unrar/smallfn.hpp index f53daa8b..1e706a57 100644 --- a/unrar/smallfn.hpp +++ b/unrar/smallfn.hpp @@ -3,6 +3,5 @@ int ToPercent(int64 N1,int64 N2); int ToPercentUnlim(int64 N1,int64 N2); -void RARInitData(); #endif diff --git a/unrar/system.cpp b/unrar/system.cpp index cf9d1836..b8f96cbb 100644 --- a/unrar/system.cpp +++ b/unrar/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/unrar/system.hpp b/unrar/system.hpp index de4c14a6..ff6a6693 100644 --- a/unrar/system.hpp +++ b/unrar/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/unrar/timefn.hpp b/unrar/timefn.hpp index 6c265ec0..5b11e867 100644 --- a/unrar/timefn.hpp +++ b/unrar/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/unrar/ui.hpp b/unrar/ui.hpp index f37001be..7f8dac78 100644 --- a/unrar/ui.hpp +++ b/unrar/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/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index cc1c72d6..a70b7a9d 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -245,6 +245,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/unrar/unpack.cpp b/unrar/unpack.cpp index 99c6f15d..1b132937 100644 --- a/unrar/unpack.cpp +++ b/unrar/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/unrar/unpack.hpp b/unrar/unpack.hpp index 5ed904a8..8fa287aa 100644 --- a/unrar/unpack.hpp +++ b/unrar/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/unrar/version.hpp b/unrar/version.hpp index 6987a9af..02e499ec 100644 --- a/unrar/version.hpp +++ b/unrar/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 1 +#define RARVER_DAY 5 +#define RARVER_MONTH 11 #define RARVER_YEAR 2024 diff --git a/unrar/win32acl.cpp b/unrar/win32acl.cpp index 36cb8525..091838fe 100644 --- a/unrar/win32acl.cpp +++ b/unrar/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/unrar/win32stm.cpp b/unrar/win32stm.cpp index 0c9c72a4..0c7e88a2 100644 --- a/unrar/win32stm.cpp +++ b/unrar/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; } From 1c763802911e45fc5631d31e9438786a22e05242 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:12 +0000 Subject: [PATCH 30/76] Added unrar 7.1.2 --- unrar/archive.cpp | 2 +- unrar/consio.cpp | 40 +++++++++++++++++++++++----------------- unrar/consio.hpp | 4 ++-- unrar/dll.cpp | 2 ++ unrar/dll.rc | 8 ++++---- unrar/errhnd.hpp | 1 + unrar/extract.cpp | 14 ++++++++++---- unrar/options.hpp | 2 +- unrar/uiconsole.cpp | 6 ++---- unrar/unicode.cpp | 8 ++++++-- unrar/version.hpp | 6 +++--- unrar/volume.cpp | 2 +- 12 files changed, 56 insertions(+), 39 deletions(-) diff --git a/unrar/archive.cpp b/unrar/archive.cpp index 3d293ec3..f90b6199 100644 --- a/unrar/archive.cpp +++ b/unrar/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); } } diff --git a/unrar/consio.cpp b/unrar/consio.cpp index 023c3f3c..6c0787ce 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -255,7 +255,7 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPas #ifndef SILENT -bool getwstr(std::wstring &str) +void getwstr(std::wstring &str) { // Print buffered prompt title function before waiting for input. fflush(stderr); @@ -281,8 +281,8 @@ bool getwstr(std::wstring &str) if (ReadSize<=0) { // Looks like stdin is a null device. We can enter to infinite loop - // calling Ask(), so let's better exit. - ErrHandler.Exit(RARX_USERBREAK); + // calling Ask() or set an empty password, so let's better exit. + ErrHandler.ReadError(L"stdin"); } StrA[ReadSize]=0; @@ -297,20 +297,26 @@ bool getwstr(std::wstring &str) else { std::vector 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;J VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 10, 1, 1407 -PRODUCTVERSION 7, 10, 1, 1407 +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.10.1\0" - VALUE "ProductVersion", "7.10.1\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/unrar/errhnd.hpp b/unrar/errhnd.hpp index 3b558b10..065350f9 100644 --- a/unrar/errhnd.hpp +++ b/unrar/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/unrar/extract.cpp b/unrar/extract.cpp index 5a3c15d2..d9f37c41 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -188,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; } diff --git a/unrar/options.hpp b/unrar/options.hpp index 785755cf..4fa68413 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -52,7 +52,7 @@ enum ARC_METADATA ARCMETA_RESTORE // -amr }; -enum QOPEN_MODE { QOPEN_AUTO, QOPEN_NONE, 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 }; diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index a70b7a9d..71559f6d 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/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; } diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index 812e35ba..e6d4977b 100644 --- a/unrar/unicode.cpp +++ b/unrar/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/unrar/version.hpp b/unrar/version.hpp index 02e499ec..1fa53272 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 10 -#define RARVER_BETA 1 -#define RARVER_DAY 5 -#define RARVER_MONTH 11 +#define RARVER_BETA 2 +#define RARVER_DAY 4 +#define RARVER_MONTH 12 #define RARVER_YEAR 2024 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index e1715155..e0b5243b 100644 --- a/unrar/volume.cpp +++ b/unrar/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) From d21e05b1f7a4792279b611dc61c89187f0804af5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:12 +0000 Subject: [PATCH 31/76] Added unrar 7.1.3 --- unrar/archive.cpp | 2 + unrar/archive.hpp | 2 + unrar/arcread.cpp | 14 ++++++- unrar/dll.cpp | 6 +-- unrar/dll.rc | 10 ++--- unrar/extract.cpp | 4 +- unrar/filefn.cpp | 2 + unrar/list.cpp | 23 ++++++++++- unrar/rar.cpp | 5 ++- unrar/scantree.cpp | 40 +++++++++++------- unrar/strfn.cpp | 5 ++- unrar/strfn.hpp | 2 +- unrar/threadmisc.cpp | 36 ++++++++++++++++ unrar/threadpool.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++- unrar/ulinks.cpp | 8 +++- unrar/unpack.cpp | 1 - unrar/unpack.hpp | 1 - unrar/unpack20.cpp | 6 ++- unrar/unpack50.cpp | 3 +- unrar/version.hpp | 8 ++-- 20 files changed, 231 insertions(+), 44 deletions(-) diff --git a/unrar/archive.cpp b/unrar/archive.cpp index f90b6199..4fc2a893 100644 --- a/unrar/archive.cpp +++ b/unrar/archive.cpp @@ -26,10 +26,12 @@ Archive::Archive(CommandData *InitCmd) FailedHeaderDecryption=false; BrokenHeader=false; LastReadBlock=0; + CurHeaderType=HEAD_UNKNOWN; CurBlockPos=0; NextBlockPos=0; + RecoveryPercent=-1; MainHead.Reset(); CryptHead={}; diff --git a/unrar/archive.hpp b/unrar/archive.hpp index 895f853b..db1cb533 100644 --- a/unrar/archive.hpp +++ b/unrar/archive.hpp @@ -47,6 +47,7 @@ class Archive:public File bool DummyCmd; CommandData *Cmd; + int RecoveryPercent; RarTime LatestTime; int LastReadBlock; @@ -65,6 +66,7 @@ class Archive:public File size_t SearchBlock(HEADER_TYPE HeaderType); size_t SearchSubBlock(const wchar *Type); size_t SearchRR(); + int GetRecoveryPercent() {return RecoveryPercent;} size_t ReadHeader(); void CheckArc(bool EnableBroken); void CheckOpen(const std::wstring &Name); diff --git a/unrar/arcread.cpp b/unrar/arcread.cpp index ab804636..cf9d0ef5 100644 --- a/unrar/arcread.cpp +++ b/unrar/arcread.cpp @@ -106,7 +106,7 @@ void Archive::UnexpEndArcMsg() if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) { uiMsg(UIERROR_UNEXPEOF,FileName); - if (CurHeaderType!=HEAD_FILE) + if (CurHeaderType!=HEAD_FILE && CurHeaderType!=HEAD_UNKNOWN) uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName); ErrHandler.SetErrorCode(RARX_WARNING); @@ -904,6 +904,16 @@ size_t Archive::ReadHeader50() if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) MainComment=true; + // For RAR5 format we read the user specified recovery percent here. + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.size()>0) + { + // It is stored as a single byte up to RAR 6.02 and as vint since + // 6.10, where we extended the maximum RR size from 99% to 1000%. + RawRead RawPercent; + RawPercent.Read(hd->SubData.data(),hd->SubData.size()); + RecoveryPercent=(int)RawPercent.GetV(); + + } if (BadCRC) // Add the file name to broken header message displayed above. uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); @@ -1308,7 +1318,7 @@ size_t Archive::ReadHeader14() std::string FileName(NameSize,0); Raw.GetB((byte *)&FileName[0],NameSize); std::string NameA; - IntToExt(FileName,NameA); + OemToExt(FileName,NameA); CharToWide(NameA,FileHead.FileName); ConvertNameCase(FileHead.FileName); ConvertFileHeader(&FileHead); diff --git a/unrar/dll.cpp b/unrar/dll.cpp index 4086409b..8ceedf9a 100644 --- a/unrar/dll.cpp +++ b/unrar/dll.cpp @@ -49,7 +49,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) AnsiArcName=r->ArcName; #ifdef _WIN_ALL if (!AreFileApisANSI()) - IntToExt(r->ArcName,AnsiArcName); + OemToExt(r->ArcName,AnsiArcName); #endif } @@ -369,7 +369,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa // We must not apply OemToCharBuffA directly to DestPath, // because we do not know DestPath length and OemToCharBuffA // does not stop at 0. - IntToExt(ExtrPathA,ExtrPathA); + OemToExt(ExtrPathA,ExtrPathA); #endif CharToWide(ExtrPathA,Data->Cmd.ExtrPath); AddEndSlash(Data->Cmd.ExtrPath); @@ -381,7 +381,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa // We must not apply OemToCharBuffA directly to DestName, // because we do not know DestName length and OemToCharBuffA // does not stop at 0. - IntToExt(DestNameA,DestNameA); + OemToExt(DestNameA,DestNameA); #endif CharToWide(DestNameA,Data->Cmd.DllDestName); } diff --git a/unrar/dll.rc b/unrar/dll.rc index 47182ee5..c838187b 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 10, 2, 1436 -PRODUCTVERSION 7, 10, 2, 1436 +FILEVERSION 7, 10, 3, 1480 +PRODUCTVERSION 7, 10, 3, 1480 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "7.10.2\0" - VALUE "ProductVersion", "7.10.2\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2024\0" + VALUE "FileVersion", "7.10.3\0" + VALUE "ProductVersion", "7.10.3\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index d9f37c41..c433fb89 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -1606,8 +1606,10 @@ void CmdExtract::AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool New if (!Arc.FileHead.SplitBefore) { - if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. + if (!MatchFound && !Arc.FileHead.Solid && !Arc.FileHead.Dir && + Arc.FileHead.RedirType==FSREDIR_NONE && Arc.FileHead.Method!=0) { + // Can start extraction from here. // We would gain nothing and unnecessarily complicate extraction // if we set StartName for first volume or StartPos for first // archived file. diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index d23f4f70..d600c06b 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -562,6 +562,8 @@ void ResetFileCache(const std::wstring &Name) + + // Delete symbolic links in file path, if any, and replace them by directories. // Prevents extracting files outside of destination folder with symlink chains. bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked) diff --git a/unrar/list.cpp b/unrar/list.cpp index 15aa73a2..04225ee5 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -110,10 +110,21 @@ void ListArchive(CommandData *Cmd) } break; case HEAD_SERVICE: + // For service blocks dependent on previous block, such as ACL + // or NTFS stream, we use "file matched" flag of host file. + // Independent blocks like RR are matched separately, + // so we can list them by their name. Also we match even + // dependent blocks separately if "vta -idn" are set. User may + // want to see service blocks only in this case. + if (!Arc.SubHead.SubBlock || Cmd->DisableNames) + FileMatched=Cmd->IsProcessFile(Arc.SubHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0; if (FileMatched && !Bare) { + // Here we set DisableNames parameter to true regardless of + // Cmd->DisableNames. If "vta -idn" are set together, user + // wants to see service blocks like RR only. if (Technical && ShowService) - ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,Cmd->DisableNames); + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,false); } break; } @@ -249,7 +260,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo if (hd.SplitAfter) wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr)); else - swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); + swprintf(RatioStr,ASIZE(RatioStr),L"%u%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); wchar DateStr[50]; hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); @@ -315,6 +326,14 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); + + if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_RR)) + { + // Display the original -rrN percent if available. + int RecoveryPercent=Arc.GetRecoveryPercent(); + if (RecoveryPercent>0) // It can be -1 if failed to detect. + mprintf(L"\n%12ls: %u%%",L"RR%", RecoveryPercent); + } } bool WinTitles=false; #ifdef _WIN_ALL diff --git a/unrar/rar.cpp b/unrar/rar.cpp index 075e768f..cd20dbec 100644 --- a/unrar/rar.cpp +++ b/unrar/rar.cpp @@ -47,7 +47,10 @@ int main(int argc, char *argv[]) { case 'T': case 'V': - Cmd->Command[0]=UpperCmd; + // Also copy 't' and 'a' modifiers for -v[t,a], if present. + Cmd->Command.clear(); + for (char *c=Switch+1;*c!=0;c++) + Cmd->Command+=etoupper(*c); break; case '?': Cmd->OutHelp(RARX_SUCCESS); diff --git a/unrar/scantree.cpp b/unrar/scantree.cpp index 43131777..17b56373 100644 --- a/unrar/scantree.cpp +++ b/unrar/scantree.cpp @@ -212,23 +212,36 @@ bool ScanTree::GetNextMask() UnixSlashToDos(CurMask,CurMask); #endif + // We shall set it before appending the path separator to \\server\share + // UNC mask below, so "rar a -ep1 arc \\server\share" includes paths + // starting from "share\". + SpecPathLength=GetNamePos(CurMask); + // We prefer to scan entire disk if mask like \\server\share\ or c:\ - // is specified regardless of recursion mode. Use \\server\share\*.* - // or c:\*.* mask to scan only the root directory. - if (CurMask.size()>2 && CurMask[0]=='\\' && CurMask[1]=='\\') - { - auto Slash=CurMask.find('\\',2); - if (Slash!=std::wstring::npos) + // is specified even without -r, but not with -r-. Use \\server\share\*.*, + // c:\*.* mask or -r- to scan only the root directory. Note that UNC names + // are possible both in Win32 and Unix, just with proper path separators. + if (Recurse!=RECURSE_DISABLE) + if (CurMask.size()>2 && CurMask[0]==CPATHDIVIDER && CurMask[1]==CPATHDIVIDER) { - Slash=CurMask.find('\\',Slash+1); - // If backslash is found and it is the last string character. - ScanEntireDisk=Slash!=std::wstring::npos && Slash+1==CurMask.size(); + auto Slash=CurMask.find(CPATHDIVIDER,2); + if (Slash!=std::wstring::npos) + { + Slash=CurMask.find(CPATHDIVIDER,Slash+1); + // If path separator is mssing or it is the last string character. + ScanEntireDisk=Slash==std::wstring::npos || + Slash!=std::wstring::npos && Slash+1==CurMask.size(); + + // Win32 FindFirstFile fails for \\server\share names without + // the trailing backslash. So we add it here. + if (Slash==std::wstring::npos) + CurMask+=CPATHDIVIDER; + } } - } - else - ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; - + else + ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + // Calculate the name position again, because we could modify UNC path above. auto NamePos=GetNamePos(CurMask); std::wstring Name=CurMask.substr(NamePos); if (Name.empty()) @@ -238,7 +251,6 @@ bool ScanTree::GetNextMask() AddEndSlash(CurMask); CurMask+=MASKALL; } - SpecPathLength=NamePos; Depth=0; OrigCurMask=CurMask; diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index 3fdaca59..6ad51c8f 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -12,7 +12,8 @@ const wchar *NullToEmpty(const wchar *Str) } -void IntToExt(const std::string &Src,std::string &Dest) +// Convert from OEM encoding. +void OemToExt(const std::string &Src,std::string &Dest) { #ifdef _WIN_ALL if (std::addressof(Src)!=std::addressof(Dest)) @@ -44,7 +45,7 @@ void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding) std::string NameA; if (Encoding==ACTW_OEM) { - IntToExt(Src,NameA); + OemToExt(Src,NameA); Src=NameA.data(); } CharToWide(Src,Dest); diff --git a/unrar/strfn.hpp b/unrar/strfn.hpp index fd4793c5..8f8b1bc1 100644 --- a/unrar/strfn.hpp +++ b/unrar/strfn.hpp @@ -3,7 +3,7 @@ const char* NullToEmpty(const char *Str); const wchar* NullToEmpty(const wchar *Str); -void IntToExt(const std::string &Src,std::string &Dest); +void OemToExt(const std::string &Src,std::string &Dest); enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8}; void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding); diff --git a/unrar/threadmisc.cpp b/unrar/threadmisc.cpp index 7a6ec782..753ea96b 100644 --- a/unrar/threadmisc.cpp +++ b/unrar/threadmisc.cpp @@ -123,6 +123,42 @@ uint GetNumberOfCPU() return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1; #endif #else // !_UNIX + +#ifdef WIN32_CPU_GROUPS + // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask + // "Starting with Windows 11 and Windows Server 2022, on a system with + // more than 64 processors, process and thread affinities span all + // processors in the system, across all processor groups, by default." + // Supposing there are 80 CPUs in 2 processor groups 40 CPUs each. + // Looks like, beginning from Windows 11 an app can use them all by default, + // not resorting to processor groups API. But if we use GetProcessAffinityMask + // to count CPUs, we would be limited to 40 CPUs only. So we call + // GetActiveProcessorCount() if it is available anf if there are multiple + // processor groups. For a single group we prefer the affinity aware + // GetProcessAffinityMask(). Out thread pool code handles the case + // with restricted processor group affinity. So we avoid the complicated + // code to calculate all processor groups affinity here, such as using + // GetLogicalProcessorInformationEx, and resort to GetActiveProcessorCount(). + HMODULE hKernel=GetModuleHandle(L"kernel32.dll"); + if (hKernel!=nullptr) + { + typedef DWORD (WINAPI *GETACTIVEPROCESSORCOUNT)(WORD GroupNumber); + GETACTIVEPROCESSORCOUNT pGetActiveProcessorCount=(GETACTIVEPROCESSORCOUNT)GetProcAddress(hKernel,"GetActiveProcessorCount"); + typedef WORD (WINAPI *GETACTIVEPROCESSORGROUPCOUNT)(); + GETACTIVEPROCESSORGROUPCOUNT pGetActiveProcessorGroupCount=(GETACTIVEPROCESSORGROUPCOUNT)GetProcAddress(hKernel,"GetActiveProcessorGroupCount"); + if (pGetActiveProcessorCount!=nullptr && pGetActiveProcessorGroupCount!=nullptr && + pGetActiveProcessorGroupCount()>1) + { + // Once the thread pool called SetThreadGroupAffinity(), + // GetProcessAffinityMask() below will return 0. So we shall always + // use GetActiveProcessorCount() here if there are multiple processor + // groups, which makes SetThreadGroupAffinity() call possible. + DWORD Count=pGetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + return Count; + } + } +#endif + DWORD_PTR ProcessMask; DWORD_PTR SystemMask; diff --git a/unrar/threadpool.cpp b/unrar/threadpool.cpp index 8c63a8bd..1d211316 100644 --- a/unrar/threadpool.cpp +++ b/unrar/threadpool.cpp @@ -1,5 +1,6 @@ #include "rar.hpp" + #ifdef RAR_SMP #include "threadmisc.cpp" @@ -92,11 +93,103 @@ ThreadPool::~ThreadPool() void ThreadPool::CreateThreads() { - for(uint I=0;I1) // If we have multiple processor groups. + { + if (I>=CumulativeGroupSize) // Filled the processor group, go to next. + { + if (++CurGroupNumber>=GroupCount) + { + // If we exceeded the group number, such as when user specified + // -mt64 for lower core count, start assigning from beginning. + CurGroupNumber=0; + CumulativeGroupSize=0; + } + // Current group size. + CurGroupSize=pGetActiveProcessorCount(CurGroupNumber); + // Size of all preceding groups including the current. + CumulativeGroupSize+=CurGroupSize; + } + GROUP_AFFINITY GroupAffinity; + pGetThreadGroupAffinity(hThread,&GroupAffinity); + + // Since normally before Windows 11 all threads belong to same source + // group, we could set this value only once. But we set it every time + // in case we'll decide for some reason to use it to rearrange threads + // from different source groups in Windows 11+. + uint SrcGroupSize=pGetActiveProcessorCount(GroupAffinity.Group); + + // Shifting by 64 would be the undefined behavior, so we treat 64 separately. + KAFFINITY SrcGroupMask=(KAFFINITY)(SrcGroupSize==64 ? (uint64)0xffffffffffffffff:(uint64(1)<RedirName,Target); if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) { - // Cannot create Windows absolute path symlinks in Unix. Only relative path - // Windows symlinks can be created here. RAR 5.0 used \??\ prefix + // Windows absolute path symlinks in Unix. RAR 5.0 used \??\ prefix // for Windows absolute symlinks, since RAR 5.1 /??/ is used. // We escape ? as \? to avoid "trigraph" warning if (Target.rfind("\\??\\",0)!=std::string::npos || Target.rfind("/\?\?/",0)!=std::string::npos) { +#if 0 // 2024.12.26: Not used anymore. We unpack absolute Windows symlinks even in Unix. uiMsg(UIERROR_SLINKCREATE,nullptr,L"\"" + hd->FileName + L"\" -> \"" + hd->RedirName + L"\""); ErrHandler.SetErrorCode(RARX_WARNING); return false; +#endif + + // 2024.12.26: User asked to unpack absolute Windows symlinks even in Unix. + Target=Target.substr(4); // Remove \??\ prefix. } DosSlashToUnix(Target,Target); } diff --git a/unrar/unpack.cpp b/unrar/unpack.cpp index 1b132937..a5950ea1 100644 --- a/unrar/unpack.cpp +++ b/unrar/unpack.cpp @@ -22,7 +22,6 @@ Unpack::Unpack(ComprDataIO *DataIO) Window=NULL; Fragmented=false; Suspended=false; - UnpAllBuf=false; UnpSomeRead=false; ExtraDist=false; #ifdef RAR_SMP diff --git a/unrar/unpack.hpp b/unrar/unpack.hpp index 8fa287aa..397480a5 100644 --- a/unrar/unpack.hpp +++ b/unrar/unpack.hpp @@ -285,7 +285,6 @@ class Unpack:PackDef int64 DestUnpSize; bool Suspended; - bool UnpAllBuf; bool UnpSomeRead; int64 WrittenFileSize; bool FileExtracted; diff --git a/unrar/unpack20.cpp b/unrar/unpack20.cpp index a0e179d6..f62ee108 100644 --- a/unrar/unpack20.cpp +++ b/unrar/unpack20.cpp @@ -159,7 +159,11 @@ void Unpack::UnpWriteBuf20() { UnpIO->UnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); UnpIO->UnpWrite(Window,UnpPtr); - UnpAllBuf=true; + + // 2024.12.24: Before 7.10 we set "UnpAllBuf=true" here. It was needed for + // Pack::PrepareSolidAppend(). Since both UnpAllBuf and FirstWinDone + // variables indicate the same thing and we set FirstWinDone in other place + // anyway, we replaced UnpAllBuf with FirstWinDone and removed this code. } else UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); diff --git a/unrar/unpack50.cpp b/unrar/unpack50.cpp index 830dfd46..137ce8fa 100644 --- a/unrar/unpack50.cpp +++ b/unrar/unpack50.cpp @@ -511,8 +511,7 @@ void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr) { if (EndPtr!=StartPtr) UnpSomeRead=true; - if (EndPtr Date: Sat, 28 Feb 2026 01:18:13 +0000 Subject: [PATCH 32/76] Added unrar 7.1.4 --- unrar/cmddata.hpp | 2 +- unrar/dll.rc | 8 ++++---- unrar/motw.cpp | 11 +++++++---- unrar/unicode.cpp | 30 +++++++++++++++++++++++++++++- unrar/version.hpp | 6 +++--- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/unrar/cmddata.hpp b/unrar/cmddata.hpp index 73082443..37882205 100644 --- a/unrar/cmddata.hpp +++ b/unrar/cmddata.hpp @@ -8,7 +8,7 @@ #define CUSTOM_CMDLINE_PARSER #endif -#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst" +#define DefaultStoreList L"7z;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst" enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; diff --git a/unrar/dll.rc b/unrar/dll.rc index c838187b..2097cdf2 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 10, 3, 1480 -PRODUCTVERSION 7, 10, 3, 1480 +FILEVERSION 7, 10, 100, 1506 +PRODUCTVERSION 7, 10, 100, 1506 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.10.3\0" - VALUE "ProductVersion", "7.10.3\0" + VALUE "FileVersion", "7.10.0\0" + VALUE "ProductVersion", "7.10.0\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/motw.cpp b/unrar/motw.cpp index 25698fe7..455f38c3 100644 --- a/unrar/motw.cpp +++ b/unrar/motw.cpp @@ -82,14 +82,17 @@ void MarkOfTheWeb::CreateZoneIdStream(const std::wstring &Name,StringList &MotwL const wchar *Ext=ExtPos==std::wstring::npos ? L"":&Name[ExtPos+1]; bool Matched=false; - wchar *CurMask; + const 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); + // Also we added the fast path to wcsicomp for English only strings. + // When extracting 100000 files with "Exe and office" masks set + // this loop spent 85ms with this optimization and wcsicomp optimized + // for English strings, 415ms with this optimization only, 475ms with + // wcsicomp optimized only and 795ms without both optimizations. + bool FastCmp=CurMask[0]=='*' && CurMask[1]=='.' && wcspbrk(CurMask+2,L"*?")==NULL; if (FastCmp && wcsicomp(Ext,CurMask+2)==0 || !FastCmp && CmpName(CurMask,Name,MATCH_NAMES)) { Matched=true; diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index e6d4977b..f33bfa43 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -656,6 +656,34 @@ bool IsTextUtf8(const byte *Src,size_t SrcSize) int wcsicomp(const wchar *s1,const wchar *s2) { + // If strings are English or numeric, perform the fast comparison. + // It improves speed in cases like comparing against a lot of MOTW masks. + bool FastMode=true; + while (true) + { + // English uppercase, English lowercase and digit flags. + bool u1=*s1>='A' && *s1<='Z', l1=*s1>='a' && *s1<='z', d1=*s1>='0' && *s1<='9'; + bool u2=*s2>='A' && *s2<='Z', l2=*s2>='a' && *s2<='z', d2=*s2>='0' && *s2<='9'; + + // Fast comparison is impossible if both characters are not alphanumeric or 0. + if (!u1 && !l1 && !d1 && *s1!=0 && !u2 && !l2 && !d2 && *s2!=0) + { + FastMode=false; + break; + } + // Convert lowercase to uppercase, keep numeric and not alphanumeric as is. + wchar c1 = l1 ? *s1-'a'+'A' : *s1; + wchar c2 = l2 ? *s2-'a'+'A' : *s2; + if (c1 != c2) + return *s1 < *s2 ? -1 : 1; + if (*s1==0) + break; + s1++; + s2++; + } + if (FastMode) + return 0; + #ifdef _WIN_ALL return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else @@ -664,7 +692,7 @@ int wcsicomp(const wchar *s1,const wchar *s2) wchar u1 = towupper(*s1); wchar u2 = towupper(*s2); if (u1 != u2) - return u1 < u2 ? -1 : 1; + return *s1 < *s2 ? -1 : 1; if (*s1==0) break; s1++; diff --git a/unrar/version.hpp b/unrar/version.hpp index 028e71d4..2248e169 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 10 -#define RARVER_BETA 3 -#define RARVER_DAY 17 -#define RARVER_MONTH 1 +#define RARVER_BETA 0 +#define RARVER_DAY 12 +#define RARVER_MONTH 2 #define RARVER_YEAR 2025 From 320f84b6aea61e563099ed7a7bbbaa2bd57d6f3c Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:14 +0000 Subject: [PATCH 33/76] Added unrar 7.1.5 --- unrar/cmddata.cpp | 12 +++++++++++- unrar/consio.cpp | 8 ++++++++ unrar/consio.hpp | 1 + unrar/dll.rc | 8 ++++---- unrar/file.cpp | 12 ++++++++++-- unrar/version.hpp | 6 +++--- 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 0a1852d3..b8f1bf05 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -1114,8 +1114,18 @@ void CommandData::ProcessCommand() OutHelp(RARX_USERERROR); #endif } + + // Since messages usually include '\n' in the beginning, we also issue + // the final '\n'. It is especially important in Unix, where otherwise + // the shell can display the prompt on the same line as the last message. + // mprintf is blocked with -idq and if error messages had been displayed + // in this mode, we use eprintf to separate them from shell prompt. + // If nothing was displayed with -idq, we avoid the excessive empty line. if (!BareOutput) - mprintf(L"\n"); + if (MsgStream==MSG_ERRONLY && IsConsoleOutputPresent()) + eprintf(L"\n"); + else + mprintf(L"\n"); } diff --git a/unrar/consio.cpp b/unrar/consio.cpp index 6c0787ce..72e7951c 100644 --- a/unrar/consio.cpp +++ b/unrar/consio.cpp @@ -4,6 +4,7 @@ static MESSAGE_TYPE MsgStream=MSG_STDOUT; static RAR_CHARSET RedirectCharset=RCH_DEFAULT; static bool ProhibitInput=false; +static bool ConsoleOutputPresent=false; static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; @@ -69,6 +70,8 @@ void ProhibitConsoleInput() #ifndef SILENT static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { + ConsoleOutputPresent=true; + // No need for PrintfPrepareFmt here, vwstrprintf calls it. std::wstring s=vwstrprintf(fmt,arglist); @@ -406,3 +409,8 @@ void OutComment(const std::wstring &Comment) mprintf(L"\n"); } + +bool IsConsoleOutputPresent() +{ + return ConsoleOutputPresent; +} diff --git a/unrar/consio.hpp b/unrar/consio.hpp index cf131d9f..d8e6f82c 100644 --- a/unrar/consio.hpp +++ b/unrar/consio.hpp @@ -6,6 +6,7 @@ void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); void ProhibitConsoleInput(); void OutComment(const std::wstring &Comment); +bool IsConsoleOutputPresent(); #ifndef SILENT bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password); diff --git a/unrar/dll.rc b/unrar/dll.rc index 2097cdf2..574885cc 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 10, 100, 1506 -PRODUCTVERSION 7, 10, 100, 1506 +FILEVERSION 7, 11, 1, 1520 +PRODUCTVERSION 7, 11, 1, 1520 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.10.0\0" - VALUE "ProductVersion", "7.10.0\0" + VALUE "FileVersion", "7.11.1\0" + VALUE "ProductVersion", "7.11.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/file.cpp b/unrar/file.cpp index 2d0d785c..601c7eaf 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -801,10 +801,18 @@ bool File::IsDevice() #ifndef SFX_MODULE int64 File::Copy(File &Dest,int64 Length) { - std::vector Buffer(File::CopyBufferSize()); - int64 CopySize=0; bool CopyAll=(Length==INT64NDF); + // Adjust the buffer size to data size. So we do not waste too much time + // to vector initialization when copying many small data blocks like + // when updating an archive with many small files. + size_t BufSize=File::CopyBufferSize(); + if (!CopyAll && Length<(int64)BufSize) + BufSize=(size_t)Length; + + std::vector Buffer(BufSize); + int64 CopySize=0; + while (CopyAll || Length>0) { Wait(); diff --git a/unrar/version.hpp b/unrar/version.hpp index 2248e169..eff6dcf5 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 -#define RARVER_MINOR 10 -#define RARVER_BETA 0 -#define RARVER_DAY 12 +#define RARVER_MINOR 11 +#define RARVER_BETA 1 +#define RARVER_DAY 26 #define RARVER_MONTH 2 #define RARVER_YEAR 2025 From 366007b41a11eb8bf97c16c46d40ba5aff839f5e Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:14 +0000 Subject: [PATCH 34/76] Added unrar 7.2.1 --- unrar/cmddata.cpp | 75 +++++++++++++++++---- unrar/cmddata.hpp | 2 +- unrar/cmdfilter.cpp | 6 ++ unrar/cmdmix.cpp | 2 +- unrar/crypt.cpp | 4 ++ unrar/dll.cpp | 4 +- unrar/dll.rc | 8 +-- unrar/errhnd.cpp | 3 +- unrar/extract.cpp | 10 ++- unrar/file.cpp | 17 +++-- unrar/file.hpp | 5 +- unrar/filefn.cpp | 2 +- unrar/getbits.cpp | 2 +- unrar/isnt.cpp | 7 +- unrar/loclang.hpp | 33 +++++----- unrar/motw.cpp | 2 +- unrar/options.hpp | 24 ++++--- unrar/os.hpp | 3 + unrar/pathfn.cpp | 154 ++++++++++++++++++++++++++++---------------- unrar/rawread.cpp | 11 ++++ unrar/rawread.hpp | 1 + unrar/scantree.cpp | 8 ++- unrar/strfn.cpp | 19 ++++++ unrar/strfn.hpp | 4 ++ unrar/timefn.cpp | 45 +++++-------- unrar/ui.hpp | 1 + unrar/ulinks.cpp | 3 +- unrar/unicode.cpp | 30 ++++++++- unrar/version.hpp | 6 +- unrar/volume.cpp | 10 +++ unrar/win32lnk.cpp | 4 +- unrar/win32stm.cpp | 5 +- 32 files changed, 354 insertions(+), 156 deletions(-) diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index b8f1bf05..6245d014 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -69,9 +69,9 @@ void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) break; if (!FirstParam) // First parameter is the executable name. if (Preprocess) - PreprocessArg(Param.data()); + PreprocessArg(Param.c_str()); else - ParseArg(Param.data()); + ParseArg(Param.c_str()); } #else for (int I=1;I=MAXPASSWORD) uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); Password.Set(Switch+2); @@ -867,7 +869,52 @@ void CommandData::ProcessSwitch(const wchar *Switch) switch(toupperw(Switch[1])) { case 0: + case '=': Solid|=SOLID_NORMAL; + if (Switch[1]=='=') + { + uint Par=0; + for (const wchar *S=Switch+2;*S!=0;S++) + { + if (IsDigit(*S)) + Par=Par*10+*S-'0'; + switch(toupperw(*S)) + { + case '-': + Solid=SOLID_NONE; + break; + case 'D': + Solid|=SOLID_VOLUME_DEPENDENT; + break; + case 'E': + Solid|=SOLID_FILEEXT; + break; + case 'F': + Solid|=SOLID_COUNT; + SolidCount=Par; + break; + case 'K': + Solid|=SOLID_BLOCK_SIZE; + SolidBlockSize=Par*1024LL; + break; + case 'M': + Solid|=SOLID_BLOCK_SIZE; + SolidBlockSize=Par*1024LL*1024LL; + break; + case 'G': + Solid|=SOLID_BLOCK_SIZE; + SolidBlockSize=Par*1024LL*1024LL*1024LL; + break; + case 'R': + Solid=SOLID_RESET; + break; + case 'V': + Solid|=SOLID_VOLUME_INDEPENDENT; + break; + } + + } + } break; case '-': Solid=SOLID_NONE; @@ -883,15 +930,20 @@ void CommandData::ProcessSwitch(const wchar *Switch) break; case 'I': ProhibitConsoleInput(); + // We do not assign the archive name automatically for -si + // if archive name is omitted and require the archive name to + // present always. Otherwise for"type arc.rar|rar x -si arc2.rar" + // if arc2.rar is a dummy archive name or file inside of arc.rar, + // which needs to be extracted. UseStdin=Switch[2] ? Switch+2:L"stdin"; break; case 'L': if (IsDigit(Switch[2])) - FileSizeLess=GetVolSize(Switch+2,1); + FileSizeLess=GetModSize(Switch+2,1); break; case 'M': if (IsDigit(Switch[2])) - FileSizeMore=GetVolSize(Switch+2,1); + FileSizeMore=GetModSize(Switch+2,1); break; case 'C': { @@ -949,12 +1001,6 @@ void CommandData::ProcessSwitch(const wchar *Switch) case 'T': switch(toupperw(Switch[1])) { - case 'K': - ArcTime=ARCTIME_KEEP; - break; - case 'L': - ArcTime=ARCTIME_LATEST; - break; case 'O': SetTimeFilters(Switch+2,true,true); break; @@ -1230,7 +1276,8 @@ void CommandData::ReportWrongSwitches(RARFORMAT Format) #endif -int64 CommandData::GetVolSize(const wchar *S,uint DefMultiplier) +// Get size for string with optional trailing modifiers like "100m". +int64 CommandData::GetModSize(const wchar *S,uint DefMultiplier) { int64 Size=0,FloatingDivider=0; for (uint I=0;S[I]!=0;I++) @@ -1247,7 +1294,7 @@ int64 CommandData::GetVolSize(const wchar *S,uint DefMultiplier) { const wchar *ModList=L"bBkKmMgGtT"; const wchar *Mod=wcschr(ModList,S[wcslen(S)-1]); - if (Mod==NULL) + if (Mod==nullptr) Size*=DefMultiplier; else for (ptrdiff_t I=2;I<=Mod-ModList;I+=2) diff --git a/unrar/cmddata.hpp b/unrar/cmddata.hpp index 37882205..3681d7ce 100644 --- a/unrar/cmddata.hpp +++ b/unrar/cmddata.hpp @@ -24,7 +24,7 @@ class CommandData:public RAROptions void SetTimeFilters(const wchar *Mod,bool Before,bool Age); void SetStoreTimeMode(const wchar *S); #endif - int64 GetVolSize(const wchar *S,uint DefMultiplier); + int64 GetModSize(const wchar *S,uint DefMultiplier); bool FileLists; bool NoMoreSwitches; diff --git a/unrar/cmdfilter.cpp b/unrar/cmdfilter.cpp index c20d8d6b..2591a45f 100644 --- a/unrar/cmdfilter.cpp +++ b/unrar/cmdfilter.cpp @@ -20,6 +20,12 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN Args->Rewind(); while (Args->GetString(CurMask)) { +#ifdef _WIN_ALL + // 2025.09.11: Unix allows DOS slashes as a part of file name, so we do not + // convert it for Unix. In Windows we wish -xdir\file and -xdir/file both + // to exclude the file. + UnixSlashToDos(CurMask,CurMask); +#endif wchar LastMaskChar=GetLastChar(CurMask); bool DirMask=IsPathDiv(LastMaskChar); // Mask for directories only. diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 7a7b5bc3..58b0e09c 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -66,7 +66,7 @@ void CommandData::OutHelp(RAR_EXIT ExitCode) MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwMLP, MCHelpSwN,MCHelpSwNa,MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL, MCHelpSwOM,MCHelpSwOP,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,MCHelpSwR, - MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA, + MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwTA, MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr, MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY #endif diff --git a/unrar/crypt.cpp b/unrar/crypt.cpp index d5c1d9d6..5e8a578f 100644 --- a/unrar/crypt.cpp +++ b/unrar/crypt.cpp @@ -54,6 +54,10 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, wchar PwdW[MAXPASSWORD]; Password->Get(PwdW,ASIZE(PwdW)); + + if (wcslen(PwdW)>=MAXPASSWORD_RAR) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD_RAR-1); + PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. char PwdA[MAXPASSWORD]; diff --git a/unrar/dll.cpp b/unrar/dll.cpp index 8ceedf9a..45be5a21 100644 --- a/unrar/dll.cpp +++ b/unrar/dll.cpp @@ -331,7 +331,7 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return ERAR_SUCCESS; + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode()); } @@ -428,7 +428,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return Data->Cmd.DllError; + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode()); } diff --git a/unrar/dll.rc b/unrar/dll.rc index 574885cc..7a74df15 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 11, 1, 1520 -PRODUCTVERSION 7, 11, 1, 1520 +FILEVERSION 7, 20, 1, 1764 +PRODUCTVERSION 7, 20, 1, 1764 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.11.1\0" - VALUE "ProductVersion", "7.11.1\0" + VALUE "FileVersion", "7.20.1\0" + VALUE "ProductVersion", "7.20.1\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/errhnd.cpp b/unrar/errhnd.cpp index b07e2f66..b347678f 100644 --- a/unrar/errhnd.cpp +++ b/unrar/errhnd.cpp @@ -256,7 +256,8 @@ void ErrorHandler::SetErrorCode(RAR_EXIT Code) ExitCode=Code; break; case RARX_CRC: - if (ExitCode!=RARX_BADPWD) + // 2025.10.25: RARX_OPEN is set if next volume is missing. + if (ExitCode!=RARX_BADPWD && ExitCode!=RARX_OPEN) ExitCode=Code; break; case RARX_FATAL: diff --git a/unrar/extract.cpp b/unrar/extract.cpp index c433fb89..e74c9d2e 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -68,7 +68,7 @@ void CmdExtract::DoExtract() } Cmd->ArcNames.Rewind(); - while (Cmd->GetArcName(ArcName)) + for (uint ArcCount=0;Cmd->GetArcName(ArcName);ArcCount++) { if (Cmd->ManualPassword) Cmd->Password.Clean(); // Clean user entered password before processing next archive. @@ -77,6 +77,12 @@ void CmdExtract::DoExtract() UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit(). while (true) { + // 2025.05.11: Add the empty line between tested archives here instead + // of printing two leading "\n" in "\n\nExtracting from", which caused + // the extra empty line after the copyright message. + if (ArcCount>0) + mprintf(L"\n"); + EXTRACT_ARC_CODE Code=ExtractArchive(); if (Code!=EXTRACT_ARC_REPEAT) break; @@ -704,6 +710,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) if (!CheckWinLimit(Arc,ArcFileName)) return false; + // 2025.09.03: OpenIndiana info is likely outdated, see https://www.illumos.org/issues/2000 // 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. @@ -838,6 +845,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // processed correctly. SlashToNative(Arc.FileHead.RedirName,RedirName); + // Ensure that target is inside of destination folder. ConvertPath(&RedirName,&RedirName); std::wstring NameExisting; diff --git a/unrar/file.cpp b/unrar/file.cpp index 601c7eaf..c6fab173 100644 --- a/unrar/file.cpp +++ b/unrar/file.cpp @@ -14,7 +14,7 @@ File::File() AllowExceptions=true; PreserveAtime=false; #ifdef _WIN_ALL - CreateMode=FMF_UNDEFINED; + // CreateMode=FMF_UNDEFINED; #endif ReadErrorMode=FREM_ASK; TruncatedAfterReadError=false; @@ -100,6 +100,12 @@ bool File::Open(const std::wstring &Name,uint Mode) #else int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); + + // 2025.06.09: We can't just set O_DIRECT for Unix like we set + // FILE_FLAG_SEQUENTIAL_SCAN for Windows to minimize disk caching. + // O_DIRECT might impose alignment requirements for data size, data address + // and file offset. Also it might not be supported by some file systems + // and fail with an error. #ifdef O_BINARY flags|=O_BINARY; #if defined(_AIX) && defined(_LARGE_FILE_API) @@ -174,6 +180,7 @@ bool File::WOpen(const std::wstring &Name) bool File::Create(const std::wstring &Name,uint Mode) { + // 2025.09.03: Likely outdated info, see https://www.illumos.org/issues/2000 // OpenIndiana based NAS and CIFS shares fail to set the file time if file // was created in read+write mode and some data was written and not flushed // before SetFileTime call. So we should use the write only mode if we plan @@ -181,7 +188,7 @@ bool File::Create(const std::wstring &Name,uint Mode) bool WriteMode=(Mode & FMF_WRITE)!=0; bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; #ifdef _WIN_ALL - CreateMode=Mode; + // CreateMode=Mode; uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; @@ -671,8 +678,10 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) // Workaround for OpenIndiana NAS time bug. If we cannot create a file // in write only mode, we need to flush the write buffer before calling // SetFileTime or file time will not be changed. - if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) - FlushFileBuffers(hFile); + // 2025.09.03: Removed this code as likely redundant now, + // see https://www.illumos.org/issues/2000 + // if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + // FlushFileBuffers(hFile); bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); diff --git a/unrar/file.hpp b/unrar/file.hpp index a95cc398..67c17549 100644 --- a/unrar/file.hpp +++ b/unrar/file.hpp @@ -41,7 +41,7 @@ enum FILE_MODE_FLAGS { FMF_STANDARDNAMES=32, // Mode flags are not defined yet. - FMF_UNDEFINED=256 + // FMF_UNDEFINED=256 }; enum FILE_READ_ERROR_MODE { @@ -73,8 +73,7 @@ class File bool AllowDelete; bool AllowExceptions; #ifdef _WIN_ALL - bool NoSequentialRead; - uint CreateMode; + // uint CreateMode; #endif bool PreserveAtime; bool TruncatedAfterReadError; diff --git a/unrar/filefn.cpp b/unrar/filefn.cpp index d600c06b..1f34d266 100644 --- a/unrar/filefn.cpp +++ b/unrar/filefn.cpp @@ -587,7 +587,7 @@ bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::w size_t SkipLength=SkipPart.size(); - if (SkipLength>0 && Path.rfind(SkipPart,0)!=0) + if (SkipLength>0 && !starts_with(Path,SkipPart)) SkipLength=0; // Parameter validation, not really needed now. // Do not check parts already checked in previous path to improve performance. diff --git a/unrar/getbits.cpp b/unrar/getbits.cpp index a5c8c3af..c414548a 100644 --- a/unrar/getbits.cpp +++ b/unrar/getbits.cpp @@ -44,7 +44,7 @@ uint BitInput::fgetbits() void BitInput::SetExternalBuffer(byte *Buf) { - if (InBuf!=NULL && !ExternalBuffer) + if (InBuf!=nullptr && !ExternalBuffer) delete[] InBuf; InBuf=Buf; ExternalBuffer=true; diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index b89b2ce5..d62e861b 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -22,7 +22,9 @@ DWORD WinNT() return Result; } - +// Since Windows 10 development is stopped, we can assume that its build +// number never reaches 22000. So we do not need WMI anymore. +#if 0 // Replace it with documented Windows 11 check when available. #include #include @@ -89,6 +91,7 @@ static bool WMI_IsWindows10() return Win10; } +#endif // Replace it with actual check when available. @@ -101,7 +104,7 @@ bool IsWindows11OrGreater() WinVer.dwOSVersionInfoSize=sizeof(WinVer); GetVersionEx(&WinVer); IsWin11=WinVer.dwMajorVersion>10 || - WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); + WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000/* && !WMI_IsWindows10()*/; IsSet=true; } return IsWin11; diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index 3ee42f74..dd941032 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -13,18 +13,18 @@ #define MBeta L"beta" #define Mx86 L"x86" #define Mx64 L"x64" -#define MMonthJan L"Jan" -#define MMonthFeb L"Feb" -#define MMonthMar L"Mar" -#define MMonthApr L"Apr" +#define MMonthJan L"January" +#define MMonthFeb L"February" +#define MMonthMar L"March" +#define MMonthApr L"April" #define MMonthMay L"May" -#define MMonthJun L"Jun" -#define MMonthJul L"Jul" -#define MMonthAug L"Aug" -#define MMonthSep L"Sep" -#define MMonthOct L"Oct" -#define MMonthNov L"Nov" -#define MMonthDec L"Dec" +#define MMonthJun L"June" +#define MMonthJul L"July" +#define MMonthAug L"August" +#define MMonthSep L"September" +#define MMonthOct L"October" +#define MMonthNov L"November" +#define MMonthDec L"December" #define MRARTitle1 L"\nUsage: rar - - " #define MUNRARTitle1 L"\nUsage: unrar - - " #define MRARTitle2 L"\n <@listfiles...> " @@ -122,15 +122,14 @@ #define MCHelpSwRI L"\n ri

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" #define MCHelpSwRR L"\n rr[N] Add data recovery record" #define MCHelpSwRV L"\n rv[N] Create recovery volumes" -#define MCHelpSwS L"\n s[,v[-],e] Create solid archive" -#define MCHelpSwSm L"\n s- Disable solid archiving" +#define MCHelpSwS L"\n s[=] Create solid archive" #define MCHelpSwSC L"\n sc[obj] Specify the character set" #define MCHelpSwSFX L"\n sfx[name] Create SFX archive" #define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" #define MCHelpSwSL L"\n sl[u] Process files with size less than specified" #define MCHelpSwSM L"\n sm[u] Process files with size more than specified" #define MCHelpSwT L"\n t Test files after archiving" -#define MCHelpSwTK L"\n tk Keep original archive time" +#define MCHelpSwTK L"\n tk[] Keep the original or set the specified archive time" #define MCHelpSwTL L"\n tl Set archive time to latest file" #define MCHelpSwTN L"\n tn[mcao] Process files newer than time" #define MCHelpSwTO L"\n to[mcao] Process files older than time" @@ -210,8 +209,8 @@ #define MAddAnalyze L"\nAnalyzing archived files: " #define MRepacking L"\nRepacking archived files: " #define MCRCFailed L"\n%-20s - checksum error" -#define MExtrTest L"\n\nTesting archive %s\n" -#define MExtracting L"\n\nExtracting from %s\n" +#define MExtrTest L"\nTesting archive %s\n" +#define MExtracting L"\nExtracting from %s\n" #define MUseCurPsw L"\n%s - use current password?" #define MCreatDir L"\nCreating %-56s" #define MExtrSkipFile L"\nSkipping %-56s" @@ -323,7 +322,7 @@ #define MStreamBroken L"\nERROR: %s stream data are corrupt\n" #define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n" #define MInvalidName L"\nERROR: Invalid file name %s" -#define MProcessArc L"\n\nProcessing archive %s" +#define MProcessArc L"\nProcessing archive %s" #define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name" #define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" #define MUnknownOption L"\nERROR: Unknown option: %s" diff --git a/unrar/motw.cpp b/unrar/motw.cpp index 455f38c3..51588d1a 100644 --- a/unrar/motw.cpp +++ b/unrar/motw.cpp @@ -56,7 +56,7 @@ void MarkOfTheWeb::ReadZoneIdStream(const std::wstring &FileName,bool AllFields) // and either raw or cleaned stream data on output. int MarkOfTheWeb::ParseZoneIdStream(std::string &Stream) { - if (Stream.rfind("[ZoneTransfer]",0)==std::string::npos) + if (!starts_with(Stream,"[ZoneTransfer]")) return -1; // Not a valid Mark of the Web. Prefer the archive MOTW if any. std::string::size_type ZoneId=Stream.find("ZoneId=",0); diff --git a/unrar/options.hpp b/unrar/options.hpp index 4fa68413..b66b4d28 100644 --- a/unrar/options.hpp +++ b/unrar/options.hpp @@ -1,7 +1,7 @@ #ifndef _RAR_OPTIONS_ #define _RAR_OPTIONS_ -#define DEFAULT_RECOVERY -3 +#define DEFAULT_RECOVERY 3 #define DEFAULT_RECVOLUMES -10 @@ -15,10 +15,18 @@ enum PATH_EXCL_MODE { EXCL_ABSPATH // -ep3 (the full path with the disk letter) }; -enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4, - SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16}; +enum { + SOLID_NONE=0, // Non-solid mode. + SOLID_NORMAL=1, // Standard solid mode. + SOLID_COUNT=2, // Reset the solid data after specified file count. + SOLID_FILEEXT=4, // Reset the solid data for every new file extension. + SOLID_VOLUME_DEPENDENT=8, // Preserve solid data in all volumes. + SOLID_VOLUME_INDEPENDENT=16, // Reset solid data in all volumes. + SOLID_RESET=32, // Reset solid data for newly added files. + SOLID_BLOCK_SIZE=64 // Reset solid data after the specified size. +}; -enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST}; +enum ARCTIME_MODE {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST,ARCTIME_SPECIFIED}; enum EXTTIME_MODE { EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX @@ -137,7 +145,7 @@ class RAROptions OVERWRITE_MODE Overwrite; int Method; HASH_TYPE HashType; - int Recovery; + uint Recovery; int RecVolNumber; ARC_METADATA ArcMetadata; bool DisablePercentage; @@ -145,8 +153,9 @@ class RAROptions bool DisableDone; bool DisableNames; bool PrintVersion; - int Solid; - int SolidCount; + uint Solid; + uint SolidCount; + uint64 SolidBlockSize; bool ClearArc; bool AddArcOnly; bool DisableComment; @@ -159,7 +168,6 @@ class RAROptions bool AllYes; bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. bool DisableSortSolid; - int ArcTime; int ConvertNames; bool ProcessOwners; bool SaveSymLinks; diff --git a/unrar/os.hpp b/unrar/os.hpp index f7e841b0..32218db4 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -52,6 +52,8 @@ #pragma comment(lib, "Shlwapi.lib") #include #pragma comment(lib, "PowrProf.lib") +#include +#pragma comment(lib, "Psapi.lib") // For GetProcessMemoryInfo(). #include #include #include @@ -137,6 +139,7 @@ #include #include #include +#include // Needed for ptrdiff_t in some UnRAR source builds. #include #include #include diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index 7c0f9196..b8383e53 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -169,7 +169,7 @@ bool IsWildcard(const std::wstring &Str) size_t StartPos=0; #ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. - if (Str.rfind(L"\\\\?\\",0)==0) + if (starts_with(Str,L"\\\\?\\")) StartPos=4; #endif return Str.find_first_of(L"*?",StartPos)!=std::wstring::npos; @@ -294,6 +294,7 @@ void GetRarDataPath(std::wstring &Path,bool Create) { Path.clear(); + // This option to change %AppData% location is documented in wunrar.chm. HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) @@ -328,14 +329,35 @@ bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create) }; if (Number==0) { - char *EnvStr=getenv("HOME"); - if (EnvStr!=NULL) + const char *EnvStr=getenv("HOME"); + if (EnvStr!=nullptr) CharToWide(EnvStr,Path); else Path=ConfPath[0]; return true; } - Number--; + if (Number==1) // According to XDG Base Directory Specification. + { + const char *EnvStr=getenv("XDG_CONFIG_HOME"); + if (EnvStr!=nullptr && *EnvStr!=0) + { + CharToWide(EnvStr,Path); + MakeName(Path,L"rar",Path); + } + else + { + const char *EnvStr=getenv("HOME"); + if (EnvStr!=nullptr) + { + CharToWide(EnvStr,Path); + MakeName(Path,L".config/rar",Path); + } + else + Path=ConfPath[0]; + } + return true; + } + Number-=2; if (Number>=ASIZE(ConfPath)) return false; Path=ConfPath[Number]; @@ -512,7 +534,15 @@ bool IsNameUsable(const std::wstring &Name) void MakeNameUsable(std::wstring &Name,bool Extended) { - for (size_t I=0;I5 && starts_with(Name,L"\\\\?\\") && IsDriveLetter(&Name[4])) + StartPos=6; +#endif + + for (size_t I=StartPos;I|\"":L"?*",Name[I])!=NULL || Extended && (uint)Name[I]<32) @@ -804,7 +834,8 @@ static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,ui Pos++; // Skip '+' in the beginning of time mask. } - std::wstring Mask=!GenerateMask.empty() ? GenerateMask.substr(Pos):L"yyyymmddhhmmss"; + // Set the default mask for -ag or -ag+, use the specified otherwise. + std::wstring Mask=GenerateMask.size()>Pos ? GenerateMask.substr(Pos):L"yyyymmddhhmmss"; bool QuoteMode=false; uint MAsMinutes=0; // By default we treat 'M' as months. @@ -823,13 +854,21 @@ static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,ui if (CurChar=='D' || CurChar=='Y') MAsMinutes=0; // Treat 'M' in HHDDMMYY and HHYYMMDD as month. - if (MAsMinutes>0 && CurChar=='M') - { - // Replace minutes with 'I'. We use 'M' both for months and minutes, - // so we treat as minutes only those 'M', which are found after hours. - Mask[I]='I'; - MAsMinutes--; - } + if (CurChar=='M') + if (MAsMinutes>0) + { + // Replace minutes with 'I'. We use 'M' both for months and minutes, + // so we treat as minutes only those 'M', which are found after hours. + Mask[I]='I'; + MAsMinutes--; + } + else + { + // Treat 3 or more 'M' as a month name and replace with 'O'. + if (I+2=4) CurWeek++; - const size_t FieldSize=11; - char Field[10][FieldSize]; + const size_t FieldSize=20; + wchar Field[12][FieldSize]; + + swprintf(Field[0],FieldSize,L"%04u",rlt.Year); + swprintf(Field[1],FieldSize,L"%02u",rlt.Month); + swprintf(Field[2],FieldSize,L"%02u",rlt.Day); + swprintf(Field[3],FieldSize,L"%02u",rlt.Hour); + swprintf(Field[4],FieldSize,L"%02u",rlt.Minute); + swprintf(Field[5],FieldSize,L"%02u",rlt.Second); + swprintf(Field[6],FieldSize,L"%02u",(uint)CurWeek); + swprintf(Field[7],FieldSize,L"%u",(uint)WeekDay+1); + swprintf(Field[8],FieldSize,L"%03u",rlt.yDay+1); + swprintf(Field[9],FieldSize,L"%05u",ArcNumber); + + const wchar *WeekDayName[]={L"Sunday",L"Monday",L"Tuesday",L"Wednesday", + L"Thursday",L"Friday",L"Saturday"}; + + wcsncpyz(Field[10],WeekDayName[rlt.wDay],FieldSize); + wcsncpyz(Field[11],GetMonthName(rlt.Month-1),FieldSize); - snprintf(Field[0],FieldSize,"%04u",rlt.Year); - snprintf(Field[1],FieldSize,"%02u",rlt.Month); - snprintf(Field[2],FieldSize,"%02u",rlt.Day); - snprintf(Field[3],FieldSize,"%02u",rlt.Hour); - snprintf(Field[4],FieldSize,"%02u",rlt.Minute); - snprintf(Field[5],FieldSize,"%02u",rlt.Second); - snprintf(Field[6],FieldSize,"%02u",(uint)CurWeek); - snprintf(Field[7],FieldSize,"%u",(uint)WeekDay+1); - snprintf(Field[8],FieldSize,"%03u",rlt.yDay+1); - snprintf(Field[9],FieldSize,"%05u",ArcNumber); + int LField[sizeof(Field)/sizeof(Field[0])]; // Field lengths. + for (size_t I=0;I=0) + DateText[J++]=Field[FieldPos][LField[FieldPos]-CField[FieldPos]--]; + DateText[J]=0; } - DateText[++J]=0; } if (Prefix) @@ -1081,12 +1132,6 @@ void MakeNameCompatible(std::wstring &Name) if (I+1==Name.size() || IsPathDiv(Name[I+1])) while (I>=0 && (Name[I]=='.' || Name[I]==' ')) { - if (I==0 && Name[I]==' ') - { - // Windows 10 Explorer can't rename or delete " " files and folders. - Name[I]='_'; // Convert " /path" to "_/path". - break; - } if (Name[I]=='.') { // 2024.05.01: Permit ./path1, path1/./path2, ../path1, @@ -1099,8 +1144,9 @@ void MakeNameCompatible(std::wstring &Name) I==3 && IsDriveLetter(Name))) break; } - Name.erase(I,1); - I--; + + Name[I]='_'; + break; } // Rename reserved device names, such as aux.txt to _aux.txt. diff --git a/unrar/rawread.cpp b/unrar/rawread.cpp index 7abb1339..ac4f2691 100644 --- a/unrar/rawread.cpp +++ b/unrar/rawread.cpp @@ -74,6 +74,17 @@ void RawRead::Read(byte *SrcData,size_t Size) } +// Move unread data to beginning of buffer and adjust buffer size. +void RawRead::Compact() +{ + if (ReadPos127) + { + IsLowAscii=false; + break; + } + if (IsLowAscii) + { + Dest.resize(SrcSize); + for (size_t I=0;Src[I]!=0;I++) + Dest[I]=Src[I]; + return; + } + #if defined(_WIN_ALL) // Console Windows RAR. if (Encoding==ACTW_UTF8) UtfToWide(Src,Dest); diff --git a/unrar/strfn.hpp b/unrar/strfn.hpp index 8f8b1bc1..ed77e5a7 100644 --- a/unrar/strfn.hpp +++ b/unrar/strfn.hpp @@ -44,6 +44,10 @@ int wcsicompc(const std::wstring &s1,const std::wstring &s2); int wcsnicompc(const wchar *s1,const wchar *s2,size_t n); int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n); +// std::[w]string::starts_with() replacement for C++17 and earlier. +inline bool starts_with(const std::string &s,const std::string &p) {return s.rfind(p,0)==0;} +inline bool starts_with(const std::wstring &s,const std::wstring &p) {return s.rfind(p,0)==0;} + void itoa(int64 n,char *Str,size_t MaxSize); void itoa(int64 n,wchar *Str,size_t MaxSize); void fmtitoa(int64 n,wchar *Str,size_t MaxSize); diff --git a/unrar/timefn.cpp b/unrar/timefn.cpp index aa90715d..79830f00 100644 --- a/unrar/timefn.cpp +++ b/unrar/timefn.cpp @@ -79,38 +79,25 @@ void RarTime::SetLocal(RarLocalTime *lt) st.wSecond=(WORD)lt->Second; st.wMilliseconds=0; st.wDayOfWeek=0; - FILETIME lft; - if (SystemTimeToFileTime(&st,&lft)) - { - FILETIME ft; - - if (WinNT() < WNT_VISTA) - { - // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP. - LocalFileTimeToFileTime(&lft,&ft); - } - else - { - // Reverse procedure which we do in GetLocal. - SYSTEMTIME st1,st2; - FileTimeToSystemTime(&lft,&st2); // st2 might be unequal to st, because we added lt->Reminder to lft. - TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1); - SystemTimeToFileTime(&st1,&ft); - - // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. - FILETIME rft; - SystemTimeToFileTime(&st2,&rft); - uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)- - INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ - INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime); - ft.dwLowDateTime=(DWORD)Corrected; - ft.dwHighDateTime=(DWORD)(Corrected>>32); - } - SetWinFT(&ft); + FILETIME ft; + if (WinNT() < WNT_VISTA) + { + // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP. + FILETIME lft; + SystemTimeToFileTime(&st,&lft); + LocalFileTimeToFileTime(&lft,&ft); } else - Reset(); + { + // Reverse procedure which we do in GetLocal. + SYSTEMTIME st1; + TzSpecificLocalTimeToSystemTime(NULL,&st,&st1); + SystemTimeToFileTime(&st1,&ft); + } + + SetWinFT(&ft); + #else struct tm t; diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 7f8dac78..1da24f50 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -41,6 +41,7 @@ enum UIMESSAGE_CODE { UIERROR_ULINKREAD, UIERROR_ULINKEXIST, UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, UIERROR_DIRNAMEEXISTS, UIERROR_TRUNCPSW, UIERROR_ADJUSTVALUE, UIERROR_SKIPUNSAFELINK, + UIERROR_AMBIGUOUSDATA, UIMSG_FIRST, UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, diff --git a/unrar/ulinks.cpp b/unrar/ulinks.cpp index 3a57d66b..4dece439 100644 --- a/unrar/ulinks.cpp +++ b/unrar/ulinks.cpp @@ -122,8 +122,7 @@ static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) // Windows absolute path symlinks in Unix. RAR 5.0 used \??\ prefix // for Windows absolute symlinks, since RAR 5.1 /??/ is used. // We escape ? as \? to avoid "trigraph" warning - if (Target.rfind("\\??\\",0)!=std::string::npos || - Target.rfind("/\?\?/",0)!=std::string::npos) + if (starts_with(Target,"\\??\\") || starts_with(Target,"/\?\?/")) { #if 0 // 2024.12.26: Not used anymore. We unpack absolute Windows symlinks even in Unix. uiMsg(UIERROR_SLINKCREATE,nullptr,L"\"" + hd->FileName + L"\" -> \"" + hd->RedirName + L"\""); diff --git a/unrar/unicode.cpp b/unrar/unicode.cpp index f33bfa43..a9d103db 100644 --- a/unrar/unicode.cpp +++ b/unrar/unicode.cpp @@ -674,8 +674,14 @@ int wcsicomp(const wchar *s1,const wchar *s2) // Convert lowercase to uppercase, keep numeric and not alphanumeric as is. wchar c1 = l1 ? *s1-'a'+'A' : *s1; wchar c2 = l2 ? *s2-'a'+'A' : *s2; + + // If characters mistmatch, to return a proper value we must compare + // already converted, case insensitive characters instead of original ones. + // So we place a.txt before B.txt and can perform the correct case + // insensitive binary search in different string lists. if (c1 != c2) - return *s1 < *s2 ? -1 : 1; + return c1 < c2 ? -1 : 1; + if (*s1==0) break; s1++; @@ -691,8 +697,13 @@ int wcsicomp(const wchar *s1,const wchar *s2) { wchar u1 = towupper(*s1); wchar u2 = towupper(*s2); + + // If characters mistmatch, to return a proper value we must compare + // already converted, case insensitive characters instead of original ones. + // So we place a.txt before B.txt and can perform the correct case + // insensitive binary search in different string lists. if (u1 != u2) - return *s1 < *s2 ? -1 : 1; + return u1 < u2 ? -1 : 1; if (*s1==0) break; s1++; @@ -759,6 +770,21 @@ std::wstring::size_type wcscasestr(const std::wstring &str, const std::wstring & #ifndef SFX_MODULE wchar* wcslower(wchar *s) { + // If string doesn't contain non-English or uppercase English characters, + // we can return immediately and avoid costly system calls. + bool AlreadyLower=true; + for (wchar *c=s;*c!=0;c++) + { + uint u=(uint)*c; + if (u>=128 || (u>='A' && u<='Z')) + { + AlreadyLower=false; + break; + } + } + if (AlreadyLower) + return s; + #ifdef _WIN_ALL // _wcslwr requires setlocale and we do not want to depend on setlocale // in Windows. Also CharLower involves less overhead. diff --git a/unrar/version.hpp b/unrar/version.hpp index eff6dcf5..5b8ae881 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 -#define RARVER_MINOR 11 +#define RARVER_MINOR 20 #define RARVER_BETA 1 -#define RARVER_DAY 26 -#define RARVER_MONTH 2 +#define RARVER_DAY 28 +#define RARVER_MONTH 10 #define RARVER_YEAR 2025 diff --git a/unrar/volume.cpp b/unrar/volume.cpp index e0b5243b..e12324e0 100644 --- a/unrar/volume.cpp +++ b/unrar/volume.cpp @@ -59,6 +59,15 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma FailedOpen=true; #endif +#ifdef _UNIX + // open() function in Unix can open a directory. But if directory has a name + // of next volume, it would result in read error and Retry/Quit prompt. + // So we skip directories in advance here. This check isn't needed + // in Windows, where opening directories fails here. + if (FileExist(NextName) && IsDir(GetFileAttr(NextName))) + FailedOpen=true; +#endif + uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; if (!FailedOpen) @@ -119,6 +128,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma if (FailedOpen) { + ErrHandler.SetErrorCode(RARX_OPEN); uiMsg(UIERROR_MISSINGVOL,NextName); Arc.Open(Arc.FileName,OpenMode); Arc.Seek(PosBeforeClose,SEEK_SET); diff --git a/unrar/win32lnk.cpp b/unrar/win32lnk.cpp index 677c2735..65a38c27 100644 --- a/unrar/win32lnk.cpp +++ b/unrar/win32lnk.cpp @@ -51,10 +51,10 @@ bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)Buf.data(); // Remove \??\ NTFS junction prefix of present. - bool WinPrefix=SubstName.rfind(L"\\??\\",0)!=std::wstring::npos; + bool WinPrefix=starts_with(SubstName,L"\\??\\"); std::wstring PrintName=WinPrefix ? SubstName.substr(4):SubstName; - if (WinPrefix && PrintName.rfind(L"UNC\\",0)!=std::wstring::npos) + if (WinPrefix && starts_with(PrintName,L"UNC\\")) PrintName=L"\\"+PrintName.substr(3); // Convert UNC\server\share to \\server\share. size_t PrintLength=PrintName.size(); diff --git a/unrar/win32stm.cpp b/unrar/win32stm.cpp index 0c7e88a2..296eaf13 100644 --- a/unrar/win32stm.cpp +++ b/unrar/win32stm.cpp @@ -52,7 +52,7 @@ void ExtractStreams20(Archive &Arc,const std::wstring &FileName) std::wstring StreamName; CharToWide(Arc.StreamHead.StreamName,StreamName); - if (StreamName[0]!=':') + if (StreamName[0]!=':' || StreamName.find_first_of(L"\\/")!=std::wstring::npos) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); @@ -128,7 +128,8 @@ void ExtractStreams20(Archive &Arc,const std::wstring &FileName) void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode) { std::wstring StreamName=GetStreamNameNTFS(Arc); - if (StreamName[0]!=':') + + if (StreamName[0]!=':' || StreamName.find_first_of(L"\\/")!=std::wstring::npos) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); From 9babd4f983c5cfef8166421ce595e635cbeb4e53 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:15 +0000 Subject: [PATCH 35/76] Added unrar 7.2.2 --- unrar/cmddata.cpp | 2 +- unrar/cmdmix.cpp | 2 -- unrar/dll.rc | 8 ++++---- unrar/loclang.hpp | 2 +- unrar/rs16.cpp | 2 +- unrar/ui.hpp | 4 +++- unrar/uicommon.cpp | 1 - unrar/version.hpp | 6 +++--- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/unrar/cmddata.cpp b/unrar/cmddata.cpp index 6245d014..03a1a9fe 100644 --- a/unrar/cmddata.cpp +++ b/unrar/cmddata.cpp @@ -653,7 +653,7 @@ void CommandData::ProcessSwitch(const wchar *Switch) // 2023.07.22: For 4 GB and less we also check that it is power of 2, // so archives are compatible with RAR 5.0+. // We allow Size>PACK_MAX_DICT here, so we can use -md[x] to unpack - // archives created by future versions with higher PACK_MAX_DICTþ + // archives created by future versions with higher PACK_MAX_DICT. uint Flags; if ((Size=Archive::GetWinSize(Size,Flags))==0 || Size<=0x100000000ULL && !IsPow2(Size)) diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 58b0e09c..474bacd9 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -75,8 +75,6 @@ void CommandData::OutHelp(RAR_EXIT ExitCode) for (uint I=0;I VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 20, 1, 1764 -PRODUCTVERSION 7, 20, 1, 1764 +FILEVERSION 7, 20, 2, 1787 +PRODUCTVERSION 7, 20, 2, 1787 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.20.1\0" - VALUE "ProductVersion", "7.20.1\0" + VALUE "FileVersion", "7.20.2\0" + VALUE "ProductVersion", "7.20.2\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index dd941032..c0ced538 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -5,7 +5,7 @@ #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 MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %.3s %d" #define MRegTo L"\nRegistered to %s\n" #define MShare L"\nTrial version Type 'rar -?' for help\n" #define MRegKeyWarning L"\nAvailable license key is valid only for %s\n" diff --git a/unrar/rs16.cpp b/unrar/rs16.cpp index 7a77464a..1c54a02a 100644 --- a/unrar/rs16.cpp +++ b/unrar/rs16.cpp @@ -146,7 +146,7 @@ void RSCoder16::MakeDecoderMatrix() } -// Apply Gauss–Jordan elimination to find inverse of decoder matrix. +// Apply Gauss-Jordan elimination to find inverse of decoder matrix. // We have the square NDxND matrix, but we do not store its trivial // diagonal "1" rows matching valid data, so we work with NExND matrix. // Our original Cauchy matrix does not contain 0, so we skip search diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 1da24f50..975e72d4 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -68,7 +68,8 @@ enum UIMESSAGE_CODE { // Flags for uiAskReplace function. enum UIASKREP_FLAGS { - UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4 + UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4, + UIASKREP_F_SINGLEFILE=8 }; // Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only @@ -80,6 +81,7 @@ enum UIASKREP_RESULT { UIASKREP_RESULT uiAskReplace(std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,std::wstring &Name,int64 FileSize,RarTime *FileTime,uint Flags); +bool GetAutoRenamedName(std::wstring &Name); void uiInit(SOUND_NOTIFY_MODE Sound); diff --git a/unrar/uicommon.cpp b/unrar/uicommon.cpp index d4d55128..f346510f 100644 --- a/unrar/uicommon.cpp +++ b/unrar/uicommon.cpp @@ -1,4 +1,3 @@ -static bool GetAutoRenamedName(std::wstring &Name); static SOUND_NOTIFY_MODE uiSoundNotify; void uiInit(SOUND_NOTIFY_MODE Sound) diff --git a/unrar/version.hpp b/unrar/version.hpp index 5b8ae881..56e31239 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 20 -#define RARVER_BETA 1 -#define RARVER_DAY 28 -#define RARVER_MONTH 10 +#define RARVER_BETA 2 +#define RARVER_DAY 20 +#define RARVER_MONTH 11 #define RARVER_YEAR 2025 From 40a78729549c82ee6c9cc4609251186dca65115a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 01:18:16 +0000 Subject: [PATCH 36/76] Added unrar 7.2.3 --- unrar/cmdmix.cpp | 2 +- unrar/crypt.cpp | 5 ++++- unrar/dll.rc | 8 ++++---- unrar/extract.cpp | 2 +- unrar/list.cpp | 17 +++++++++-------- unrar/loclang.hpp | 11 +++++++++-- unrar/pathfn.cpp | 6 +----- unrar/strfn.cpp | 4 ++-- unrar/ui.hpp | 1 + unrar/uiconsole.cpp | 12 +++++++++++- unrar/uisilent.cpp | 6 ++++++ unrar/version.hpp | 6 +++--- 12 files changed, 52 insertions(+), 28 deletions(-) diff --git a/unrar/cmdmix.cpp b/unrar/cmdmix.cpp index 474bacd9..2decf308 100644 --- a/unrar/cmdmix.cpp +++ b/unrar/cmdmix.cpp @@ -2,7 +2,7 @@ void CommandData::OutTitle() { if (BareOutput || DisableCopyright) return; -#if defined(__GNUC__) && defined(SFX_MODULE) +#ifdef SFX_MODULE mprintf(St(MCopyrightS)); #else #ifndef SILENT diff --git a/unrar/crypt.cpp b/unrar/crypt.cpp index 5e8a578f..9c754ffe 100644 --- a/unrar/crypt.cpp +++ b/unrar/crypt.cpp @@ -55,7 +55,10 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, wchar PwdW[MAXPASSWORD]; Password->Get(PwdW,ASIZE(PwdW)); - if (wcslen(PwdW)>=MAXPASSWORD_RAR) + // Display this warning only when encrypting. Users complained that + // it is distracting when decrypting. It still can be useful when encrypting, + // so users do not waste time to excessively long passwords. + if (Encrypt && wcslen(PwdW)>=MAXPASSWORD_RAR) uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD_RAR-1); PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. diff --git a/unrar/dll.rc b/unrar/dll.rc index 06291822..744e13ec 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 20, 2, 1787 -PRODUCTVERSION 7, 20, 2, 1787 +FILEVERSION 7, 20, 3, 1815 +PRODUCTVERSION 7, 20, 3, 1815 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.20.2\0" - VALUE "ProductVersion", "7.20.2\0" + VALUE "FileVersion", "7.20.3\0" + VALUE "ProductVersion", "7.20.3\0" VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" VALUE "OriginalFilename", "Unrar.dll\0" } diff --git a/unrar/extract.cpp b/unrar/extract.cpp index e74c9d2e..096b6089 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -816,7 +816,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) uint64 Preallocated=0; if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && - Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() && + Arc.FileHead.PackSize>Arc.FileHead.UnpSize/1024 && Arc.IsSeekable() && (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) { CurFile.Prealloc(Arc.FileHead.UnpSize); diff --git a/unrar/list.cpp b/unrar/list.cpp index 04225ee5..a9af7777 100644 --- a/unrar/list.cpp +++ b/unrar/list.cpp @@ -141,15 +141,15 @@ void ListArchive(CommandData *Cmd) if (Verbose) { - mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); - mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, + mprintf(L"\n----------- ---------- ---------- ----- ---------- ----- -------- ----"); + mprintf(L"\n%22ls %10ls %3d%% %-27ls %u",UnpSizeText, PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), VolNumText,FileCount); } else { - mprintf(L"\n----------- --------- ---------- ----- ----"); - mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); + mprintf(L"\n----------- ---------- ---------- ----- ----"); + mprintf(L"\n%22ls %-16ls %u",UnpSizeText,VolNumText,FileCount); } SumFileCount+=FileCount; @@ -212,13 +212,13 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo { mprintf(L"\n%ls",St(MListTitleV)); if (!DisableNames) - mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + mprintf(L"\n----------- ---------- ---------- ----- ---------- ----- -------- ----"); } else { mprintf(L"\n%ls",St(MListTitleL)); if (!DisableNames) - mprintf(L"\n----------- --------- ---------- ----- ----"); + mprintf(L"\n----------- ---------- ---------- ----- ----"); } // Must be set even in DisableNames mode to suppress "0 files" output // unless no files are matched. @@ -432,10 +432,11 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo return; } - mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); + // 2025.12.01: Size field width incremented to properly align 1+ GB sizes. + mprintf(L"\n%c%10ls %10ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); if (Verbose) - mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); + mprintf(L"%10ls %4ls ",PackSizeText,RatioStr); mprintf(L" %ls ",DateStr); diff --git a/unrar/loclang.hpp b/unrar/loclang.hpp index c0ced538..06837f0b 100644 --- a/unrar/loclang.hpp +++ b/unrar/loclang.hpp @@ -25,6 +25,13 @@ #define MMonthOct L"October" #define MMonthNov L"November" #define MMonthDec L"December" +#define MWeekDayMon L"Monday" +#define MWeekDayTue L"Tuesday" +#define MWeekDayWed L"Wednesday" +#define MWeekDayThu L"Thursday" +#define MWeekDayFri L"Friday" +#define MWeekDaySat L"Saturday" +#define MWeekDaySun L"Sunday" #define MRARTitle1 L"\nUsage: rar - - " #define MUNRARTitle1 L"\nUsage: unrar - - " #define MRARTitle2 L"\n <@listfiles...> " @@ -254,8 +261,8 @@ #define MListLock L"lock" #define MListEnc L"encrypted" #define MListEncHead L"encrypted headers" -#define MListTitleL L" Attributes Size Date Time Name" -#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name" +#define MListTitleL L" Attributes Size Date Time Name" +#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name" #define MListName L"Name" #define MListType L"Type" #define MListFile L"File" diff --git a/unrar/pathfn.cpp b/unrar/pathfn.cpp index b8383e53..24661666 100644 --- a/unrar/pathfn.cpp +++ b/unrar/pathfn.cpp @@ -926,11 +926,7 @@ static void GenArcName(std::wstring &ArcName,const std::wstring &GenerateMask,ui swprintf(Field[7],FieldSize,L"%u",(uint)WeekDay+1); swprintf(Field[8],FieldSize,L"%03u",rlt.yDay+1); swprintf(Field[9],FieldSize,L"%05u",ArcNumber); - - const wchar *WeekDayName[]={L"Sunday",L"Monday",L"Tuesday",L"Wednesday", - L"Thursday",L"Friday",L"Saturday"}; - - wcsncpyz(Field[10],WeekDayName[rlt.wDay],FieldSize); + wcsncpyz(Field[10],uiGetWeekDayName(rlt.wDay),FieldSize); wcsncpyz(Field[11],GetMonthName(rlt.Month-1),FieldSize); int LField[sizeof(Field)/sizeof(Field[0])]; // Field lengths. diff --git a/unrar/strfn.cpp b/unrar/strfn.cpp index 3a254aa8..ed8f91fe 100644 --- a/unrar/strfn.cpp +++ b/unrar/strfn.cpp @@ -483,8 +483,8 @@ void PrintfPrepareFmt(const wchar *Org,std::wstring &Cvt) if (Org[Src]=='%' && (Src==0 || Org[Src-1]!='%')) { size_t SPos=Src+1; - // Skipping a possible width specifier like %-50s. - while (IsDigit(Org[SPos]) || Org[SPos]=='-') + // Skipping a possible width specifier like %-50s or %.3s. + while (IsDigit(Org[SPos]) || Org[SPos]=='-' || Org[SPos]=='.') SPos++; if (Org[SPos]=='s') { diff --git a/unrar/ui.hpp b/unrar/ui.hpp index 975e72d4..e25c111a 100644 --- a/unrar/ui.hpp +++ b/unrar/ui.hpp @@ -116,6 +116,7 @@ bool uiDictLimit(CommandData *Cmd,const std::wstring &FileName,uint64 DictSize,u #ifndef SFX_MODULE const wchar *uiGetMonthName(uint Month); +const wchar *uiGetWeekDayName(uint Day); #endif class uiMsgStore diff --git a/unrar/uiconsole.cpp b/unrar/uiconsole.cpp index 71559f6d..cb7d58b5 100644 --- a/unrar/uiconsole.cpp +++ b/unrar/uiconsole.cpp @@ -505,7 +505,17 @@ const wchar *uiGetMonthName(uint Month) MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec }; - return St(MonthID[Month]); + return Month Date: Sat, 28 Feb 2026 01:18:16 +0000 Subject: [PATCH 37/76] Added unrar 7.2.4 --- unrar/dll.rc | 10 +++++----- unrar/version.hpp | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unrar/dll.rc b/unrar/dll.rc index 744e13ec..33ff097e 100644 --- a/unrar/dll.rc +++ b/unrar/dll.rc @@ -2,8 +2,8 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 7, 20, 3, 1815 -PRODUCTVERSION 7, 20, 3, 1815 +FILEVERSION 7, 20, 100, 1861 +PRODUCTVERSION 7, 20, 100, 1861 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP { @@ -14,9 +14,9 @@ FILETYPE VFT_APP VALUE "CompanyName", "Alexander Roshal\0" VALUE "ProductName", "RAR decompression library\0" VALUE "FileDescription", "RAR decompression library\0" - VALUE "FileVersion", "7.20.3\0" - VALUE "ProductVersion", "7.20.3\0" - VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0" + VALUE "FileVersion", "7.20.0\0" + VALUE "ProductVersion", "7.20.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2026\0" VALUE "OriginalFilename", "Unrar.dll\0" } } diff --git a/unrar/version.hpp b/unrar/version.hpp index 448fe10e..3fc76682 100644 --- a/unrar/version.hpp +++ b/unrar/version.hpp @@ -1,6 +1,6 @@ #define RARVER_MAJOR 7 #define RARVER_MINOR 20 -#define RARVER_BETA 3 -#define RARVER_DAY 18 -#define RARVER_MONTH 12 -#define RARVER_YEAR 2025 +#define RARVER_BETA 0 +#define RARVER_DAY 1 +#define RARVER_MONTH 2 +#define RARVER_YEAR 2026 From 0131eef66c83d9de9aeb7a66ad243f9b7b3b15eb Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 28 Feb 2026 21:04:17 +0000 Subject: [PATCH 38/76] Use zend_long in lieu of long where appropriate --- php_rar.h | 4 ++-- rar_navigation.c | 30 ++++++++++++------------------ rararch.c | 34 +++++++++++++++++----------------- rarentry.c | 31 +++++++++++++------------------ 4 files changed, 44 insertions(+), 55 deletions(-) diff --git a/php_rar.h b/php_rar.h index 9bdee067..9021f266 100644 --- a/php_rar.h +++ b/php_rar.h @@ -248,7 +248,7 @@ typedef struct _rar_find_output { int found; size_t position; struct RARHeaderDataEx * header; - unsigned long packed_size; + zend_ulong packed_size; int eof; } rar_find_output; #define RAR_SEARCH_INDEX 0x01U @@ -297,7 +297,7 @@ extern zend_class_entry *rar_class_entry_ptr; void minit_rarentry(TSRMLS_D); void _rar_entry_to_zval(zval *parent, struct RARHeaderDataEx *entry, - unsigned long packed_size, + zend_ulong packed_size, size_t index, zval *entry_object TSRMLS_DC); diff --git a/rar_navigation.c b/rar_navigation.c index bf5136c4..59530e06 100644 --- a/rar_navigation.c +++ b/rar_navigation.c @@ -49,7 +49,7 @@ typedef struct _rar_find_state { struct _rar_unique_entry { size_t id; /* position in the entries_array */ struct RARHeaderDataEx entry; /* last entry */ - unsigned long packed_size; + zend_ulong packed_size; int depth; /* number of directory separators */ size_t name_wlen; /* excluding L'\0' terminator */ }; @@ -336,7 +336,7 @@ int _rar_list_files(rar_file_t *rar TSRMLS_DC) /* {{{ */ int result = 0; size_t capacity = 0; int first_file_check = TRUE; - unsigned long packed_size = 0UL; + zend_ulong packed_size = 0; struct _rar_entries *ents; if (rar->entries != NULL) { @@ -378,22 +378,16 @@ int _rar_list_files(rar_file_t *rar TSRMLS_DC) /* {{{ */ /* reset packed size if not split before */ if ((entry.Flags & RHDF_SPLITBEFORE) == 0) - packed_size = 0UL; - - /* we would exceed size of ulong. cap at ulong_max - * equivalent to packed_size + entry.PackSize > ULONG_MAX, - * but without overflowing */ - if (ULONG_MAX - packed_size < entry.PackSize) - packed_size = ULONG_MAX; - else { - packed_size += entry.PackSize; - if (entry.PackSizeHigh != 0) { -#if ULONG_MAX > 0xffffffffUL - packed_size += ((unsigned long) entry.PackSizeHigh) << 32; -#else - packed_size = ULONG_MAX; /* cap */ -#endif - } + packed_size = 0; + + /* accumulate packed size; cap at ZEND_LONG_MAX (the PHP int ceiling) */ + { + zend_ulong entry_packed = ((zend_ulong)entry.PackSizeHigh << 32) | entry.PackSize; + if (entry_packed > (zend_ulong)ZEND_LONG_MAX || + packed_size > (zend_ulong)ZEND_LONG_MAX - entry_packed) + packed_size = (zend_ulong)ZEND_LONG_MAX; + else + packed_size += entry_packed; } if (entry.Flags & RHDF_SPLITAFTER) /* do not commit */ diff --git a/rararch.c b/rararch.c index 7cbfa26e..fd700d60 100644 --- a/rararch.c +++ b/rararch.c @@ -105,8 +105,8 @@ static void rararch_ce_free_object_storage(ze_rararch_object *object TSRMLS_DC); /* {{{ RarArchive handlers */ static int rararch_handlers_preamble(handler_this_t *object, rar_file_t **rar TSRMLS_DC); -static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, long *index, int quiet TSRMLS_DC); -static int rararch_count_elements(handler_this_t *object, long *count TSRMLS_DC); +static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, zend_long *index, int quiet TSRMLS_DC); +static int rararch_count_elements(handler_this_t *object, zend_long *count TSRMLS_DC); #if PHP_MAJOR_VERSION < 7 static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); #else @@ -420,7 +420,7 @@ static int rararch_handlers_preamble(handler_this_t *object, /* {{{ rararch_dimensions_preamble - semi-strict parsing of int argument */ static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, - long *index, + zend_long *index, int quiet TSRMLS_DC) { if (offset == NULL) { @@ -444,23 +444,23 @@ static int rararch_dimensions_preamble(rar_file_t *rar, return FAILURE; } else if (type == IS_DOUBLE) { - if (d > (double) LONG_MAX || d < (double) LONG_MIN) { + if (d > (double) ZEND_LONG_MAX || d < (double) ZEND_LONG_MIN) { RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING, "Dimension index is out of integer bounds"); return FAILURE; } - *index = (long) d; + *index = (zend_long) d; } } else if (Z_TYPE_P(offset) == IS_DOUBLE) { - if (Z_DVAL_P(offset) > (double) LONG_MAX || - Z_DVAL_P(offset) < (double) LONG_MIN) { + if (Z_DVAL_P(offset) > (double) ZEND_LONG_MAX || + Z_DVAL_P(offset) < (double) ZEND_LONG_MIN) { RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING, "Dimension index is out of integer bounds"); return FAILURE; } - *index = (long) Z_DVAL_P(offset); + *index = (zend_long) Z_DVAL_P(offset); } else if (Z_TYPE_P(offset) == IS_OBJECT) { #if PHP_MAJOR_VERSION < 8 @@ -527,9 +527,9 @@ static int rararch_dimensions_preamble(rar_file_t *rar, return FAILURE; } - if (*index < 0L) { + if (*index < 0) { RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING, - "Dimension index must be non-negative, given %ld", *index); + "Dimension index must be non-negative, given " ZEND_LONG_FMT, *index); return FAILURE; } if ((size_t) *index >= _rar_entry_count(rar)) { @@ -544,21 +544,21 @@ static int rararch_dimensions_preamble(rar_file_t *rar, /* }}} */ /* {{{ RarArchive count_elements handler */ -static int rararch_count_elements(handler_this_t *object, long *count TSRMLS_DC) +static int rararch_count_elements(handler_this_t *object, zend_long *count TSRMLS_DC) { rar_file_t *rar = NULL; size_t entry_count; if (rararch_handlers_preamble(object, &rar TSRMLS_CC) == FAILURE) { - *count = 0L; + *count = 0; return SUCCESS; /* intentional */ } entry_count = _rar_entry_count(rar); - if (entry_count > LONG_MAX) - entry_count = (size_t) LONG_MAX; + if (entry_count > ZEND_LONG_MAX) + entry_count = (size_t) ZEND_LONG_MAX; - *count = (long) entry_count; + *count = (zend_long) entry_count; return SUCCESS; } @@ -571,7 +571,7 @@ static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_ static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int type, zval *rv) #endif { - long index; + zend_long index; rar_file_t *rar = NULL; struct _rar_find_output *out; zval *ret = NULL; @@ -626,7 +626,7 @@ static void rararch_write_dimension(handler_this_t *object, zval *offset, zval * /* {{{ RarArchive has_dimension handler */ static int rararch_has_dimension(handler_this_t *object, zval *offset, int check_empty TSRMLS_DC) { - long index; + zend_long index; rar_file_t *rar = NULL; (void) check_empty; /* don't care */ diff --git a/rarentry.c b/rarentry.c index 5e680f61..8fb9711c 100644 --- a/rarentry.c +++ b/rarentry.c @@ -55,7 +55,7 @@ static void _rar_dos_date_to_text(unsigned dos_time, char *date_string); * will have its refcount increased */ void _rar_entry_to_zval(zval *parent, struct RARHeaderDataEx *entry, - unsigned long packed_size, + zend_ulong packed_size, size_t position, zval *object TSRMLS_DC) /* {{{ */ @@ -65,7 +65,7 @@ void _rar_entry_to_zval(zval *parent, char *filename; int filename_size, filename_len; - long unp_size; /* zval stores PHP ints as long, so use that here */ + zend_long unp_size; /* zend_long is the portable PHP integer type (always 64-bit in PHP 7+) */ zval *parent_copy = parent; #if PHP_MAJOR_VERSION < 7 /* allocate zval on the heap */ @@ -85,22 +85,17 @@ void _rar_entry_to_zval(zval *parent, zend_update_property(rar_class_entry_ptr, obj, "rarfile", sizeof("rararch") - 1, parent_copy TSRMLS_CC); -#if ULONG_MAX > 0xffffffffUL - unp_size = ((long) entry->UnpSize) + (((long) entry->UnpSizeHigh) << 32); -#else - /* for 32-bit long, at least don't give negative values */ - if ((unsigned long) entry->UnpSize > (unsigned long) LONG_MAX - || entry->UnpSizeHigh != 0) - unp_size = LONG_MAX; - else - unp_size = (long) entry->UnpSize; -#endif + { + zend_ulong raw_size = (zend_ulong)entry->UnpSizeHigh << 32 | entry->UnpSize; + unp_size = raw_size > (zend_ulong)ZEND_LONG_MAX + ? ZEND_LONG_MAX : (zend_long)raw_size; + } filename_size = sizeof(entry->FileNameW) * 4; filename = (char*) emalloc(filename_size); - if (packed_size > (unsigned long) LONG_MAX) - packed_size = LONG_MAX; + if (packed_size > (zend_ulong) ZEND_LONG_MAX) + packed_size = (zend_ulong) ZEND_LONG_MAX; _rar_wide_to_utf(entry->FileNameW, filename, filename_size); /* OK; safe usage below: */ filename_len = _rar_strnlen(filename, filename_size); @@ -109,7 +104,7 @@ void _rar_entry_to_zval(zval *parent, * direct call to rarentry_object_handlers.write_property * zend_update_property_x updates the scope accordingly */ zend_update_property_long(rar_class_entry_ptr, obj, "position", - sizeof("position") - 1, (long) position TSRMLS_CC); + sizeof("position") - 1, (zend_long) position TSRMLS_CC); zend_update_property_stringl(rar_class_entry_ptr, obj, "name", sizeof("name") - 1, filename, filename_len TSRMLS_CC); zend_update_property_long(rar_class_entry_ptr, obj, "unpacked_size", @@ -176,7 +171,7 @@ void _rar_entry_to_zval(zval *parent, #define REG_RAR_CLASS_CONST_LONG(const_name, value) \ zend_declare_class_constant_long(rar_class_entry_ptr, const_name, \ - sizeof(const_name) - 1, (long) value TSRMLS_CC) + sizeof(const_name) - 1, (zend_long) value TSRMLS_CC) #define REG_RAR_PROPERTY(name, comment) \ _rar_decl_priv_prop_null(rar_class_entry_ptr, name, sizeof(name) -1, \ @@ -585,7 +580,7 @@ PHP_METHOD(rarentry, isDirectory) { zval *tmp; zval *entry_obj = getThis(); - long flags; + zend_long flags; int is_dir; RAR_RETNULL_ON_ARGS(); @@ -604,7 +599,7 @@ PHP_METHOD(rarentry, isEncrypted) { zval *tmp; zval *entry_obj = getThis(); - long flags; + zend_long flags; int is_encrypted; RAR_RETNULL_ON_ARGS(); From 77cc02a1db5085d8d21c067309ffb14229766383 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 05:02:12 +0000 Subject: [PATCH 39/76] Fix RAR5 chunk extraction spurious warning on Windows with multi-core CPUs On Windows, RAR_SMP is defined and Unpack5MT is used when MaxUserThreads > 1 (i.e. on any multi-core machine). Unlike Unpack5, Unpack5MT does not set FileExtracted = true on completion, leaving it false after SetDestSize() clears it. In chunk extraction mode (suspendAfterInit = true), this caused RARProcessFileChunk to return *finished = false even after writing all data, triggering a second unnecessary call that wrote 0 bytes and fired the spurious "Extraction reported as unfinished but no data read" warning. Fix by skipping Unpack5MT when suspendAfterInit is true, falling through to Unpack5 which correctly implements the suspend/resume protocol. The existing comment in DoUnpack already noted that Unpack5MT must not be used in suspended mode; the condition just wasn't enforced. Remove the now-stale Appveyor skip from test 101. Co-Authored-By: Claude Sonnet 4.6 --- tests/101.phpt | 1 - unrar/unpack.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/101.phpt b/tests/101.phpt index 4d372b6c..d5209112 100644 --- a/tests/101.phpt +++ b/tests/101.phpt @@ -2,7 +2,6 @@ Supports version 5 RAR files --SKIPIF-- Date: Sat, 28 Feb 2026 01:09:46 +0000 Subject: [PATCH 40/76] Migrate CI from Azure Pipelines/Appveyor to GitHub Actions Replace the old Azure Pipelines + Appveyor setup with GitHub Actions: - Add .github/workflows/tests.yml with Linux (matrix) and Windows jobs - Add .github/scripts/build-and-test.sh (shared build+test script) - Add .github/scripts/update-docker-shas.sh (Docker image SHA lockfile updater) - Add .github/docker-image-shas.yml (pinned OCI index digests for Linux jobs) - Add Justfile for local test targets mirroring CI - Add unrar_update.md documenting the unrar update process - Remove azure-pipelines.yml, azure-template.yml, appveyor.yml, appveyor.bat, test_funcs.sh --- .github/docker-image-shas.yml | 17 ++++++ .github/scripts/build-and-test.sh | 35 +++++++++++ .github/scripts/update-docker-shas.sh | 47 +++++++++++++++ .github/workflows/tests.yml | 76 ++++++++++++++++++++++++ .gitignore | 2 + Justfile | 73 +++++++++++++++++++++++ README.md | 2 - appveyor.bat | 31 ---------- appveyor.yml | 26 -------- azure-pipelines.yml | 85 --------------------------- azure-template.yml | 65 -------------------- config.w32 | 2 +- report.xml | 0 test_funcs.sh | 70 ---------------------- unrar_update.md | 26 ++++++++ 15 files changed, 277 insertions(+), 280 deletions(-) create mode 100644 .github/docker-image-shas.yml create mode 100755 .github/scripts/build-and-test.sh create mode 100755 .github/scripts/update-docker-shas.sh create mode 100644 .github/workflows/tests.yml create mode 100644 Justfile delete mode 100644 appveyor.bat delete mode 100644 appveyor.yml delete mode 100644 azure-pipelines.yml delete mode 100644 azure-template.yml delete mode 100644 report.xml delete mode 100644 test_funcs.sh create mode 100644 unrar_update.md diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml new file mode 100644 index 00000000..2c709db1 --- /dev/null +++ b/.github/docker-image-shas.yml @@ -0,0 +1,17 @@ +# Docker image SHA lock file. +# Maps image tags to their multi-arch OCI index digests (amd64 + arm64). +# Regenerate with: .github/scripts/update-docker-shas.sh + +datadog/dd-appsec-php-ci: + php-7.0-debug: "sha256:0a9aaeaf79bd4c578eac6dedabf6ac131537266f44562158ce67fdb37c794916" + php-7.0-release-zts: "sha256:4bb5fac6fbd3124234d062328c68f6c51027aa95f13e4e8b14afd7c8de518ec6" + php-7.1-debug: "sha256:500007ad057d9e71b2bb9079a2f8ba3f1ecfbb26d112db69e17d6007b4e857b5" + php-7.1-release-zts: "sha256:d997b9f99c28967872bd0949f8572b089daaadb6ceaa1e856ccc76e07e2ba6b7" + php-7.2-debug: "sha256:4c5f111f8e84fcb7dcc9e3cc13e1277d0702b04cd33286ce33db885485d1f025" + php-7.2-release-zts: "sha256:634b857d74c3d77b88986ceb088be2dd5bc30151bc08c2b536443984e6659d6e" + php-7.3-debug: "sha256:efa81f79783097478a434578226fe9a3b8fe84abda33168034aaea60c197c73b" + php-7.3-release-zts: "sha256:c713df299596a9615f88cfe73c29b0a1f9faf32e5e6fa62fa07ee839313cd57e" + php-7.4-debug: "sha256:b8a9e982179189122d73feb896c1a1e8578a92fc9a023dabc825f45db8299c22" + php-7.4-release-zts: "sha256:6492e3334e722b106352180ec9f0cbee8dd81f008e3537d03f4b8da3522f49e1" + php-8.0-debug: "sha256:900ceae7487db1e3652de2880c181e572fdf053673bcda8ff47abf664ff74d39" + php-8.0-release-zts: "sha256:b6243199f6aea0792a97583c9036f0b191ad9efb96ea337632fbaca76289a4da" diff --git a/.github/scripts/build-and-test.sh b/.github/scripts/build-and-test.sh new file mode 100755 index 00000000..a56b5865 --- /dev/null +++ b/.github/scripts/build-and-test.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Build the extension and run the test suite. +# Expected to run inside a datadog/dd-appsec-php-ci container from the repo root. +set -euo pipefail + +# Clean up artifacts from any previous build so stale objects don't survive a +# PHP-version switch (safe in CI where the workspace is always fresh). +if [ -f Makefile ]; then + make -f Makefile distclean +fi + +phpize +./configure --with-php-config="$(which php-config)" +make -f Makefile -j"$(nproc)" + +# The generated Makefile silences and ignores errors on the `if` commands; +# undo that so test failures surface properly. +sed -i 's/-@if/@if/' Makefile + +ret=0 +TEST_PHP_EXECUTABLE="$(which php)" \ +TEST_PHP_JUNIT=report.xml \ +REPORT_EXIT_STATUS=1 \ +NO_INTERACTION=1 \ +TESTS="--set-timeout 300 --show-diff" \ + make -f Makefile test || ret=$? + +found=$(find tests -name '*.mem' | wc -l) +if [ "$found" -gt 0 ]; then + echo "Found $found memory leak(s):" + find tests -name '*.mem' -print -exec cat {} \; + ret=1 +fi + +exit "$ret" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh new file mode 100755 index 00000000..73e3f0d2 --- /dev/null +++ b/.github/scripts/update-docker-shas.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Refresh docker-image-shas.yml with current OCI index digests. +# The index digest covers all platforms (amd64 + arm64); Docker resolves the +# right platform image from it at pull time. +# Justfile and tests.yml read from docker-image-shas.yml directly — no patching needed. +set -euo pipefail + +IMAGE="datadog/dd-appsec-php-ci" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOCK_FILE="$SCRIPT_DIR/../docker-image-shas.yml" + +TAGS=( + php-7.0-debug php-7.0-release-zts + php-7.1-debug php-7.1-release-zts + php-7.2-debug php-7.2-release-zts + php-7.3-debug php-7.3-release-zts + php-7.4-debug php-7.4-release-zts + php-8.0-debug php-8.0-release-zts +) + +get_index_digest() { + # The top-level "digest" field in the Hub tags API is the manifest-list + # (OCI index) digest, not a per-platform image digest. + curl -fsSL "https://hub.docker.com/v2/repositories/${IMAGE}/tags/$1" \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])" +} + +# Collect all digests first so we fail fast before touching any file. +declare -A DIGESTS +for tag in "${TAGS[@]}"; do + echo "Fetching $tag ..." >&2 + DIGESTS[$tag]=$(get_index_digest "$tag") +done + +# ── Update docker-image-shas.yml ───────────────────────────────────────────── + +{ + echo "# Docker image SHA lock file." + echo "# Maps image tags to their multi-arch OCI index digests (amd64 + arm64)." + echo "# Regenerate with: .github/scripts/update-docker-shas.sh" + echo "" + echo "${IMAGE}:" + for tag in "${TAGS[@]}"; do + printf " %-20s \"%s\"\n" "${tag}:" "${DIGESTS[$tag]}" + done +} > "$LOCK_FILE" +echo "Updated $LOCK_FILE" >&2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..968b4ebe --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,76 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + generate-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + - id: set-matrix + run: | + python3 <<'EOF' + import yaml, json, os + with open('.github/docker-image-shas.yml') as f: + data = yaml.safe_load(f) + image = 'datadog/dd-appsec-php-ci' + includes = [] + for tag, sha in data[image].items(): + # tag: "php-7.0-debug" or "php-7.0-release-zts" + ver, variant = tag[len('php-'):].split('-', 1) + includes.append({'php': ver, 'variant': variant, 'image_sha': sha}) + with open(os.environ['GITHUB_OUTPUT'], 'a') as f: + f.write('matrix=' + json.dumps({'include': includes}) + '\n') + EOF + + linux: + name: PHP ${{ matrix.php }} (${{ matrix.variant }}) + needs: generate-matrix + runs-on: ubuntu-latest + container: + image: datadog/dd-appsec-php-ci@${{ matrix.image_sha }} + options: --user root + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and test + run: bash .github/scripts/build-and-test.sh + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-php${{ matrix.php }}-${{ matrix.variant }} + path: report.xml + + windows: + name: Windows PHP 8.0 TS + runs-on: windows-2022 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and test + uses: php/php-windows-builder/extension@v1 + with: + php-version: '8.0' + arch: x64 + ts: ts + args: --enable-rar=shared + test-workers: 1 + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-windows-php8.0-ts + path: '*.xml' diff --git a/.gitignore b/.gitignore index 86886db5..acede34d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ /php-rar.creator.user /compile_commands.json /.clangd +/report.xml +/.worktrees diff --git a/Justfile b/Justfile new file mode 100644 index 00000000..bc51bff9 --- /dev/null +++ b/Justfile @@ -0,0 +1,73 @@ +# Build and test inside the matching CI Docker image. +# Image SHAs are read from .github/docker-image-shas.yml (multi-arch OCI index digests). +# To refresh SHAs: .github/scripts/update-docker-shas.sh + +# ── Images ──────────────────────────────────────────────────────────────────── + +_base := "datadog/dd-appsec-php-ci@" +_shas := ".github/docker-image-shas.yml" + +image_7_0_debug := _base + `grep 'php-7.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_0_release_zts := _base + `grep 'php-7.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_1_debug := _base + `grep 'php-7.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_1_release_zts := _base + `grep 'php-7.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_2_debug := _base + `grep 'php-7.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_2_release_zts := _base + `grep 'php-7.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_3_debug := _base + `grep 'php-7.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_3_release_zts := _base + `grep 'php-7.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_4_debug := _base + `grep 'php-7.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_4_release_zts := _base + `grep 'php-7.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_0_debug := _base + `grep 'php-8.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_0_release_zts := _base + `grep 'php-8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` + +_run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root" + +# ── Default ─────────────────────────────────────────────────────────────────── + +default: + @just --list + +# ── Individual targets ──────────────────────────────────────────────────────── + +test-7_0-debug: + {{_run}} {{image_7_0_debug}} .github/scripts/build-and-test.sh +test-7_0-release-zts: + {{_run}} {{image_7_0_release_zts}} .github/scripts/build-and-test.sh + +test-7_1-debug: + {{_run}} {{image_7_1_debug}} .github/scripts/build-and-test.sh +test-7_1-release-zts: + {{_run}} {{image_7_1_release_zts}} .github/scripts/build-and-test.sh + +test-7_2-debug: + {{_run}} {{image_7_2_debug}} .github/scripts/build-and-test.sh +test-7_2-release-zts: + {{_run}} {{image_7_2_release_zts}} .github/scripts/build-and-test.sh + +test-7_3-debug: + {{_run}} {{image_7_3_debug}} .github/scripts/build-and-test.sh +test-7_3-release-zts: + {{_run}} {{image_7_3_release_zts}} .github/scripts/build-and-test.sh + +test-7_4-debug: + {{_run}} {{image_7_4_debug}} .github/scripts/build-and-test.sh +test-7_4-release-zts: + {{_run}} {{image_7_4_release_zts}} .github/scripts/build-and-test.sh + +test-8_0-debug: + {{_run}} {{image_8_0_debug}} .github/scripts/build-and-test.sh +test-8_0-release-zts: + {{_run}} {{image_8_0_release_zts}} .github/scripts/build-and-test.sh + +# ── Per-version aggregates (sequential to avoid workspace conflicts) ─────────── + +test-7_0: test-7_0-debug test-7_0-release-zts +test-7_1: test-7_1-debug test-7_1-release-zts +test-7_2: test-7_2-debug test-7_2-release-zts +test-7_3: test-7_3-debug test-7_3-release-zts +test-7_4: test-7_4-debug test-7_4-release-zts +test-8_0: test-8_0-debug test-8_0-release-zts + +# ── All Linux targets ───────────────────────────────────────────────────────── + +test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 diff --git a/README.md b/README.md index d354d4fb..d02cb54f 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,4 @@ unrar/LICENSE.txt for details. Some modifications have been applied to the UnRAR library, mainly to allow streaming extraction of files without using threads. -[![Build Status Appveyor](https://ci.appveyor.com/api/projects/status/cbgpepx6kyax2198/branch/master?svg=true)](https://ci.appveyor.com/project/cataphract/php-rar/branch/master) -[![Build Status Travis](https://travis-ci.org/cataphract/php-rar.svg?branch=master)](https://travis-ci.org/cataphract/php-rar) [![codecov](https://codecov.io/gh/cataphract/php-rar/branch/master/graph/badge.svg)](https://codecov.io/gh/cataphract/php-rar) diff --git a/appveyor.bat b/appveyor.bat deleted file mode 100644 index 24dd8e94..00000000 --- a/appveyor.bat +++ /dev/null @@ -1,31 +0,0 @@ -echo Running appveyor.bat -echo on - -CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" - -IF NOT EXIST "C:\projects\php-sdk" ( - wget -nv http://windows.php.net/downloads/php-sdk/php-sdk-binary-tools-20110915.zip - 7z x -y php-sdk-binary-tools-20110915.zip -oC:\projects\php-sdk -) -IF NOT EXIST "C:\projects\php-src\Release_TS\php7ts.lib" ( - git clone --depth=1 --branch=PHP-7.1 https://github.com/php/php-src C:\projects\php-src - wget -nv http://windows.php.net/downloads/php-sdk/deps-7.1-vc14-x86.7z - 7z x -y deps-7.1-vc14-x86.7z -oC:\projects\php-src - CALL C:\projects\php-sdk\bin\phpsdk_setvars.bat - cd C:\projects\php-src - CALL buildconf.bat - CALL configure.bat --disable-all --enable-cli --with-config-file-scan-dir=C:\projects\extension\bin\modules.d --with-prefix=%APPVEYOR_BUILD_FOLDER%\bin --with-php-build=deps - nmake -) ELSE ( - echo php7ts.lib already exists - cd C:\projects\php-src - CALL C:\projects\php-sdk\bin\phpsdk_setvars.bat -) - -CALL buildconf.bat --force --add-modules-dir=%APPVEYOR_BUILD_FOLDER%\.. -CALL configure.bat --disable-all --enable-cli --enable-rar=shared --with-config-file-scan-dir=C:\projects\extension\bin\modules.d --with-prefix=%APPVEYOR_BUILD_FOLDER%\bin --with-php-build=deps -nmake || exit /b -rmdir Release_TS\php-rar /S /Q -del /S /Q "Release_TS\*.sbr" - -copy %APPVEYOR_BUILD_FOLDER%\run-tests8.php C:\projects\php-src\run-tests.php diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 6560b7f5..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Based on igbinary's appveyor config. - -version: '{branch}.{build}' -install: - - cmd: choco feature enable -n=allowGlobalConfirmation - - cmd: cinst wget - - cmd: mkdir %APPVEYOR_BUILD_FOLDER%\bin - -build_script: - - cmd: "%APPVEYOR_BUILD_FOLDER%\\appveyor.bat" - -test_script: - - cmd: cd C:\projects\php-src - - cmd: set NO_INTERACTION=1 - - cmd: set REPORT_EXIT_STATUS=1 - - cmd: set TZ=UTC - - cmd: 'nmake test TESTS="--show-diff --set-timeout 30 -d extension=C:\projects\php-src\Release_TS\php_rar.dll %APPVEYOR_BUILD_FOLDER%\tests"' - -after_test: -# - cmd: dir /S C:\projects\php-src\Release_TS - - cmd: 'del C:\projects\php-src\Release_TS\php_rar.*' - -cache: - - C:\ProgramData\chocolatey\lib -> appveyor.yml - - C:\projects\php-src - - C:\projects\php-sdk diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 0f5fb944..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,85 +0,0 @@ -jobs: - - template: azure-template.yml - parameters: - name: php_5_3_valgrind - displayName: PHP 5.3 ZTS (valgrind, clang) - phpVersion: '5.3' - clang: true - valgrind: true - zts: true - - - template: azure-template.yml - parameters: - name: php_5_4 - displayName: PHP 5.4 - phpVersion: '5.4' - - - template: azure-template.yml - parameters: - name: php_5_5 - displayName: PHP 5.5 - phpVersion: '5.5' - - - template: azure-template.yml - parameters: - name: php_5_6 - displayName: PHP 5.6 - phpVersion: '5.6' - - - template: azure-template.yml - parameters: - name: php_5_6_valgrind - displayName: PHP 5.6 ZTS (valgrind, clang) - phpVersion: '5.6' - clang: true - valgrind: true - zts: true - - - template: azure-template.yml - parameters: - name: php_7_0 - displayName: PHP 7.0 - phpVersion: '7.0' - - - template: azure-template.yml - parameters: - name: php_7_1 - displayName: PHP 7.1 - phpVersion: '7.1' - - - template: azure-template.yml - parameters: - name: php_7_2 - displayName: PHP 7.2 - phpVersion: '7.2' - - - template: azure-template.yml - parameters: - name: php_7_3 - displayName: PHP 7.3 - phpVersion: '7.3' - - - template: azure-template.yml - parameters: - name: php_7_4 - displayName: PHP 7.4 ZTS (valgrind, clang) - phpVersion: '7.4' - clang: true - valgrind: true - zts: true - - - template: azure-template.yml - parameters: - name: php_8_0 - displayName: PHP 8.0 ZTS - phpVersion: '8.0' - zts: true - publishCoverage: true - - - template: azure-template.yml - parameters: - name: php_8_0_valgrind - displayName: PHP 8.0 (valgrind, clang) - phpVersion: '8.0' - clang: true - valgrind: true diff --git a/azure-template.yml b/azure-template.yml deleted file mode 100644 index b23fda08..00000000 --- a/azure-template.yml +++ /dev/null @@ -1,65 +0,0 @@ -parameters: - name: ~ - displayName: ~ - phpVersion: bundled - imageName: ubuntu-20.04 - zts: false - clang: false - publishCoverage: false - valgrind: false - -jobs: - - job: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - pool: - vmImage: ${{ parameters.imageName }} - - variables: - ${{ if eq(parameters.clang, true) }}: - CC: clang - CXX: clang++ - - steps: - - checkout: self - displayName: Checkout - clean: true - submodules: recursive - - - bash: | - source test_funcs.sh - install_php ${{ parameters.phpVersion }} ${{ parameters.zts }} - displayName: Install PHP ${{ parameters.phpVersion }} - - - ${{ if eq(parameters.valgrind, true) }}: - - bash: > - sudo apt-get install valgrind - displayName: "Install Valgrind" - - - script: | - source test_funcs.sh - build_ext ${{ parameters.phpVersion }} ${{ parameters.zts }} ${{ parameters.publishCoverage }} - displayName: Build extension - - - script: | - source test_funcs.sh - run_tests ${{ parameters.phpVersion }} ${{ parameters.zts }} ${{ parameters.publishCoverage }} - ${{ if eq(parameters.valgrind, true) }}: - env: - RUN_TESTS_FLAGS: -m - displayName: Run tests - - - task: PublishTestResults@2 - displayName: Publish test results - condition: succeededOrFailed() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: 'report.xml' - searchFolder: '$(System.DefaultWorkingDirectory)' - mergeTestResults: true - - - ${{ if eq(parameters.publishCoverage, true) }}: - - bash: > - bash <(curl -s https://codecov.io/bash) - displayName: "Publish coverage (Codecov)" - env: - CODECOV_TOKEN: $(CODECOV_TOKEN) diff --git a/config.w32 b/config.w32 index b72a7c09..a767ace9 100644 --- a/config.w32 +++ b/config.w32 @@ -18,7 +18,7 @@ if (PHP_RAR != "no") { crc.cpp rijndael.cpp crypt.cpp \ rawread.cpp \ rs.cpp smallfn.cpp \ - isnt.cpp rar.cpp consio.cpp \ + isnt.cpp consio.cpp \ scantree.cpp archive.cpp strfn.cpp \ strlist.cpp \ getbits.cpp hash.cpp \ diff --git a/report.xml b/report.xml deleted file mode 100644 index e69de29b..00000000 diff --git a/test_funcs.sh b/test_funcs.sh deleted file mode 100644 index 2d135920..00000000 --- a/test_funcs.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -e - -JOBS=3 - -function package_name { - local readonly version=$1 zts=$2 - local zts_suffix='' - if [[ $zts = 'true' ]]; then - zts_suffix='-zts' - fi - - echo "php-${version}${zts_suffix}-bare-dbg" -} - -function prefix { - local readonly version=$1 zts=$2 - - echo "/opt/$(package_name $version $zts)" -} - -function build_ext { - local readonly version=$1 zts=$2 coverage=$3 - local readonly prefix=$(prefix $1 $2) - local cflags= cxxflags= ldflags= - "$prefix"/bin/phpize - if [[ $coverage == true ]]; then - cflags=--coverage - cxxflags=--coverage - ldflags=--coverage - fi - CFLAGS="$cflags" CXXFLAGS="$cxxflags" LDFLAGS="$ldflags" \ - ./configure --with-php-config="$prefix/bin/php-config" - make -j $JOBS -} - -function do_tests { - local readonly prefix=$1 - local found_leaks= dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - local ret=0 - sed -i s/-@if/@if/ Makefile - TEST_PHP_EXECUTABLE="$prefix/bin/php" \ - TEST_PHP_JUNIT=report.xml \ - REPORT_EXIT_STATUS=1 \ - NO_INTERACTION=1 \ - TESTS="--set-timeout 300 --show-diff $RUN_TESTS_FLAGS" make test \ - || ret=$? - found_leaks=$(find tests -name '*.mem' | wc -l) - if [[ $found_leaks -gt 0 ]]; then - echo "Found $found_leaks leaks. Failing." - find tests -name "*.mem" -print -exec cat {} \; - fi - return $ret -} - -function install_php { - local readonly version=$1 zts=$2 - local readonly url="$MIRROR/php-$version.tar.gz" - - sudo apt-get install -y gnupg - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 5D98E7264E3F3D89463B314B12229434A9F003C9 - echo deb [arch=amd64] http://artefacto-test.s3.amazonaws.com/php-bare-dbg bionic main | sudo tee -a /etc/apt/sources.list - sudo apt-get update - sudo apt-get install -y $(package_name $version $zts) -} - -function run_tests { - set -e - set -o pipefail - do_tests "$(prefix $1 $2)" -} diff --git a/unrar_update.md b/unrar_update.md new file mode 100644 index 00000000..dd7857f5 --- /dev/null +++ b/unrar_update.md @@ -0,0 +1,26 @@ +This file contains the procedure to update to a new version of unrar. + +There is a separate branch, `unrar` with the unaltered source code of unrar. +Each commit is a new version of `unrar`. They are committed sequentially by +version number, as `unrar` doesn't generally have minor updates for older +versions. + +The commits have messages like "Added unrar 5.9.4". + +This branch is then merged to master, with the subtree strategy. + +To update to new versions of unrar, follow this procedure: + +1. Check the latest version of unrar from https://www.rarlab.com/rar_add.htm. + Look for the link with the name "UnRAR source". Not the url pattern of the + link, as this will be needed for step 3. +2. Setup a worktree with the unrar branch. Check the latest commit. This will + indicate what the last unrar version that was handled. +3. Attempt to download the next patch version. If you get a 404, attempt the + next minor version, otherwise the next major version. +4. Extract the contents into the `unrar` directory of the repository, + completely replacing the previous contents. +5. Commit with the correct message. +6. If you handled the latest version, you are done. Otherwise, go to step 3. + + From 4a4e67ec33005a9ead47b182532a1af601232542 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 17:24:44 +0000 Subject: [PATCH 41/76] Moderninze run-tests.php; use it on windows CI too --- .github/workflows/tests.yml | 1 + .gitignore | 2 +- Makefile.frag | 5 +- config.m4 | 2 +- run-tests8.php => run-tests-rar.php | 686 ++++++++++++++-------------- 5 files changed, 341 insertions(+), 355 deletions(-) rename run-tests8.php => run-tests-rar.php (89%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 968b4ebe..9cae514c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,6 +66,7 @@ jobs: arch: x64 ts: ts args: --enable-rar=shared + test-runner: ${{ github.workspace }}/run-tests-rar.php test-workers: 1 - name: Upload test results diff --git a/.gitignore b/.gitignore index acede34d..1a7d8cb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o *.lo +/run-tests.php /tests/*.sh /tests/*.exp /tests/*.diff @@ -7,7 +8,6 @@ /tests/*.php /tests/*.out /tests/*.mem -/run-tests.php /modules /missing /.deps diff --git a/Makefile.frag b/Makefile.frag index 6c96fd40..f035c194 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -1,6 +1,5 @@ +.PHONY: replace-run-tests replace-run-tests: - @if ! grep -q 'Minimum required PHP version: 5\.3\.0' run-tests.php; then \ - cp run-tests8.php run-tests.php; \ - fi + cp run-tests-rar.php run-tests.php test: replace-run-tests diff --git a/config.m4 b/config.m4 index e1ee121e..fe43203c 100644 --- a/config.m4 +++ b/config.m4 @@ -68,6 +68,6 @@ if test "$PHP_RAR" != "no"; then PHP_REQUIRE_CXX() PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", RAR_SHARED_LIBADD) - PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -Wall -fvisibility=hidden -I@ext_srcdir@/unrar) + PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -Wall -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar) PHP_ADD_BUILD_DIR($ext_builddir/unrar) fi diff --git a/run-tests8.php b/run-tests-rar.php similarity index 89% rename from run-tests8.php rename to run-tests-rar.php index 062dabb4..c0578bcd 100644 --- a/run-tests8.php +++ b/run-tests-rar.php @@ -23,12 +23,12 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ +/* $Id: 2c93f53ab453fd06f92da4eb75b5ff00d0a9ae7c $ */ /* Let there be no top-level code beyond this point: * Only functions and classes, thanks! * - * Minimum required PHP version: 5.3.0 + * Minimum required PHP version: 7.1.0 */ function show_usage() @@ -97,12 +97,12 @@ function show_usage() Do not delete 'all' files, 'php' test file, 'skip' or 'clean' file. - --set-timeout [n] - Set timeout for individual tests, where [n] is the number of + --set-timeout + Set timeout for individual tests, where is the number of seconds. The default value is 60 seconds, or 300 seconds when testing for memory leaks. - --context [n] + --context Sets the number of lines of surrounding context to print for diffs. The default value is 3. @@ -113,18 +113,14 @@ function show_usage() 'mem'. The result types get written independent of the log format, however 'diff' only exists when a test fails. - --show-slow [n] - Show all tests that took longer than [n] milliseconds to run. + --show-slow + Show all tests that took longer than milliseconds to run. --no-clean Do not execute clean section if any. --color --no-color Do/Don't colorize the result type in the test result. - --repeat [n] - Run the tests multiple times in the same process and check the - output of the last execution (CLI SAPI only). - HELP; } @@ -151,7 +147,7 @@ function main() $repeat, $result_tests_file, $slow_min_ms, $start_time, $switch, $temp_source, $temp_target, $test_cnt, $test_dirs, $test_files, $test_idx, $test_list, $test_results, $testfile, - $user_tests, $valgrind, $sum_results, $shuffle, $file_cache, $num_repeats; + $user_tests, $valgrind, $sum_results, $shuffle, $file_cache; // Parallel testing global $workers, $workerID; global $context_line_count; @@ -195,7 +191,7 @@ function main() error_reporting(E_ALL); - $environment = empty($_ENV) ? array() : $_ENV; + $environment = $_ENV ?? []; // Some configurations like php.ini-development set variables_order="GPCS" // not "EGPCS", in which case $_ENV is NOT populated. Detect if the $_ENV @@ -234,6 +230,65 @@ function main() $php_cgi = null; $phpdbg = null; + if (getenv('TEST_PHP_EXECUTABLE')) { + $php = getenv('TEST_PHP_EXECUTABLE'); + + if ($php == 'auto') { + $php = TEST_PHP_SRCDIR . '/sapi/cli/php'; + putenv("TEST_PHP_EXECUTABLE=$php"); + + if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { + $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi'; + + if (file_exists($php_cgi)) { + putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); + } else { + $php_cgi = null; + } + } + } + $environment['TEST_PHP_EXECUTABLE'] = $php; + } + + if (getenv('TEST_PHP_CGI_EXECUTABLE')) { + $php_cgi = getenv('TEST_PHP_CGI_EXECUTABLE'); + + if ($php_cgi == 'auto') { + $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi'; + putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); + } + + $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi; + } + + if (!getenv('TEST_PHPDBG_EXECUTABLE')) { + if (IS_WINDOWS && file_exists(dirname($php) . "/phpdbg.exe")) { + $phpdbg = realpath(dirname($php) . "/phpdbg.exe"); + } elseif (file_exists(dirname($php) . "/../../sapi/phpdbg/phpdbg")) { + $phpdbg = realpath(dirname($php) . "/../../sapi/phpdbg/phpdbg"); + } elseif (file_exists("./sapi/phpdbg/phpdbg")) { + $phpdbg = realpath("./sapi/phpdbg/phpdbg"); + } elseif (file_exists(dirname($php) . "/phpdbg")) { + $phpdbg = realpath(dirname($php) . "/phpdbg"); + } else { + $phpdbg = null; + } + if ($phpdbg) { + putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); + } + } + + if (getenv('TEST_PHPDBG_EXECUTABLE')) { + $phpdbg = getenv('TEST_PHPDBG_EXECUTABLE'); + + if ($phpdbg == 'auto') { + $phpdbg = TEST_PHP_SRCDIR . '/sapi/phpdbg/phpdbg'; + putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); + } + + $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg; + } + if (getenv('TEST_PHP_LOG_FORMAT')) { $log_format = strtoupper(getenv('TEST_PHP_LOG_FORMAT')); } else { @@ -252,18 +307,18 @@ function main() if (getenv('SHOW_ONLY_GROUPS')) { $SHOW_ONLY_GROUPS = explode(",", getenv('SHOW_ONLY_GROUPS')); } else { - $SHOW_ONLY_GROUPS = array(); + $SHOW_ONLY_GROUPS = []; } // Check whether user test dirs are requested. if (getenv('TEST_PHP_USER')) { $user_tests = explode(',', getenv('TEST_PHP_USER')); } else { - $user_tests = array(); + $user_tests = []; } - $exts_to_test = array(); - $ini_overwrites = array( + $exts_to_test = []; + $ini_overwrites = [ 'output_handler=', 'open_basedir=', 'disable_functions=', @@ -298,7 +353,7 @@ function main() 'zend.exception_ignore_args=0', 'zend.exception_string_param_max_len=15', 'short_open_tag=0', - ); + ]; $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0'; @@ -309,18 +364,18 @@ function main() // Determine the tests to be run. - $test_files = array(); - $redir_tests = array(); - $test_results = array(); - $PHP_FAILED_TESTS = array( - 'BORKED' => array(), - 'FAILED' => array(), - 'WARNED' => array(), - 'LEAKED' => array(), - 'XFAILED' => array(), - 'XLEAKED' => array(), - 'SLOW' => array() - ); + $test_files = []; + $redir_tests = []; + $test_results = []; + $PHP_FAILED_TESTS = [ + 'BORKED' => [], + 'FAILED' => [], + 'WARNED' => [], + 'LEAKED' => [], + 'XFAILED' => [], + 'XLEAKED' => [], + 'SLOW' => [] + ]; // If parameters given assume they represent selected tests to run. $result_tests_file = false; @@ -350,14 +405,13 @@ function main() $shuffle = false; $workers = null; $context_line_count = 3; - $num_repeats = 1; - $cfgtypes = array('show', 'keep'); - $cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'); - $cfg = array(); + $cfgtypes = ['show', 'keep']; + $cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem']; + $cfg = []; foreach ($cfgtypes as $type) { - $cfg[$type] = array(); + $cfg[$type] = []; foreach ($cfgfiles as $file) { $cfg[$type][$file] = false; @@ -365,7 +419,7 @@ function main() } if (!isset($argc, $argv) || !$argc) { - $argv = array(__FILE__); + $argv = [__FILE__]; $argc = 1; } @@ -424,9 +478,9 @@ function main() $test_list = file($argv[++$i]); if ($test_list) { foreach ($test_list as $test) { - $matches = array(); + $matches = []; if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { - $redir_tests[] = array($matches[1], $matches[2]); + $redir_tests[] = [$matches[1], $matches[2]]; } else { if (strlen($test)) { $test_files[] = trim($test); @@ -517,10 +571,14 @@ function main() $just_save_results = true; break; case '--set-timeout': - $environment['TEST_TIMEOUT'] = $argv[++$i]; + $timeout = $argv[++$i] ?? ''; + if (!preg_match('/^\d+$/', $timeout)) { + error("'$timeout' is not a valid number of seconds, try e.g. --set-timeout 60 for 1 minute"); + } + $environment['TEST_TIMEOUT'] = intval($timeout, 10); break; case '--context': - $context_line_count = empty($argv[++$i]) ? '' : $argv[$i]; + $context_line_count = $argv[++$i] ?? ''; if (!preg_match('/^\d+$/', $context_line_count)) { error("'$context_line_count' is not a valid number of lines of context, try e.g. --context 3 for 3 lines"); } @@ -532,7 +590,11 @@ function main() } break; case '--show-slow': - $slow_min_ms = $argv[++$i]; + $slow_min_ms = $argv[++$i] ?? ''; + if (!preg_match('/^\d+$/', $slow_min_ms)) { + error("'$slow_min_ms' is not a valid number of milliseconds, try e.g. --show-slow 1000 for 1 second"); + } + $slow_min_ms = intval($slow_min_ms, 10); break; case '--temp-source': $temp_source = $argv[++$i]; @@ -569,10 +631,6 @@ function main() . ':print_suppressions=0'; } break; - case '--repeat': - $num_repeats = (int) $argv[++$i]; - $environment['SKIP_REPEAT'] = 1; - break; //case 'w' case '-': // repeat check with full switch @@ -582,7 +640,7 @@ function main() } break; case '--version': - echo '$Id$' . "\n"; + echo '$Id: 2c93f53ab453fd06f92da4eb75b5ff00d0a9ae7c $' . "\n"; exit(1); default: @@ -633,39 +691,14 @@ function main() return; } - if (!$php) { - $php = getenv('TEST_PHP_EXECUTABLE'); - } - if (!$php) { - $php = PHP_BINDIR . '/php'; - if (IS_WINDOWS) { - $php .= '.exe'; - } - var_dump($php); - } - - if (!$php_cgi) { - $php_cgi = getenv('TEST_PHP_CGI_EXECUTABLE'); - } - if (!$php_cgi) { - $php_cgi = get_binary($php, 'php-cgi', 'sapi/cgi/php-cgi'); + // Default to PHP_BINARY as executable + if (!isset($environment['TEST_PHP_EXECUTABLE'])) { + $php = PHP_BINARY; + putenv("TEST_PHP_EXECUTABLE=$php"); + $environment['TEST_PHP_EXECUTABLE'] = $php; } - if (!$phpdbg) { - $phpdbg = getenv('TEST_PHPDBG_EXECUTABLE'); - } - if (!$phpdbg) { - $phpdbg = get_binary($php, 'phpdbg', 'sapi/phpdbg/phpdbg'); - } - - putenv("TEST_PHP_EXECUTABLE=$php"); - $environment['TEST_PHP_EXECUTABLE'] = $php; - putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); - $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi; - putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); - $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg; - - if ($conf_passed !== null) { + if (strlen($conf_passed)) { if (IS_WINDOWS) { $pass_options .= " -c " . escapeshellarg($conf_passed); } else { @@ -715,13 +748,13 @@ function main() } } else { // Compile a list of all test files (*.phpt). - $test_files = array(); + $test_files = []; $exts_tested = count($exts_to_test); $exts_skipped = 0; $ignored_by_ext = 0; sort($exts_to_test); - $test_dirs = array(); - $optionals = array('Zend', 'tests', 'ext', 'sapi'); + $test_dirs = []; + $optionals = ['Zend', 'tests', 'ext', 'sapi']; foreach ($optionals as $dir) { if (is_dir($dir)) { @@ -778,7 +811,7 @@ function main() junit_save_xml(); if (getenv('REPORT_EXIT_STATUS') !== '0' && getenv('REPORT_EXIT_STATUS') !== 'no' && - ($sum_results['FAILED'] || $sum_results['LEAKED'])) { + ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { exit(1); } } @@ -787,7 +820,7 @@ function main() /** * @return array|float|int */ - function hrtime($as_num = false) + function hrtime(bool $as_num = false) { $t = microtime(true); @@ -796,7 +829,7 @@ function hrtime($as_num = false) } $s = floor($t); - return array(0 => $s, 1 => ($t - $s) * 1000000000); + return [0 => $s, 1 => ($t - $s) * 1000000000]; } } @@ -828,7 +861,7 @@ function write_information() INI actual : " , realpath(get_cfg_var("cfg_file_path")) , " More .INIs : " , (function_exists(\'php_ini_scanned_files\') ? str_replace("\n","", php_ini_scanned_files()) : "** not determined **"); ?>'; save_text($info_file, $php_info); - $info_params = array(); + $info_params = []; settings2array($ini_overwrites, $info_params); $info_params = settings2params($info_params); $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; @@ -860,13 +893,13 @@ function write_information() ''); $exts_to_test = explode(',', `$php $pass_options $info_params $no_file_cache "$info_file"`); // check for extensions that need special handling and regenerate - $info_params_ex = array( - 'session' => array('session.auto_start=0'), - 'tidy' => array('tidy.clean_output=0'), - 'zlib' => array('zlib.output_compression=Off'), - 'xdebug' => array('xdebug.mode=off'), - 'mbstring' => array('mbstring.func_overload=0'), - ); + $info_params_ex = [ + 'session' => ['session.auto_start=0'], + 'tidy' => ['tidy.clean_output=0'], + 'zlib' => ['zlib.output_compression=Off'], + 'xdebug' => ['xdebug.mode=off'], + 'mbstring' => ['mbstring.func_overload=0'], + ]; foreach ($info_params_ex as $ext => $ini_overwrites_ex) { if (in_array($ext, $exts_to_test)) { @@ -979,7 +1012,7 @@ function save_or_mail_results() } /* Try the most common flags for 'version' */ - $flags = array('-v', '-V', '--version'); + $flags = ['-v', '-V', '--version']; $cc_status = 0; foreach ($flags as $flag) { @@ -1024,29 +1057,14 @@ function save_or_mail_results() } } -function get_binary($php, $sapi, $sapi_path) -{ - $dir = dirname($php); - if (IS_WINDOWS && file_exists("$dir/$sapi.exe")) { - return realpath("$dir/$sapi.exe"); - } - if (file_exists("$dir/../../$sapi_path")) { - return realpath("$dir/../../$sapi_path"); - } - if (file_exists("$dir/$sapi")) { - return realpath("$dir/$sapi"); - } - return null; -} - -function find_files($dir, $is_ext_dir = false, $ignore = false) +function find_files(string $dir, bool $is_ext_dir = false, bool $ignore = false) { global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped; $o = opendir($dir) or error("cannot open directory: $dir"); while (($name = readdir($o)) !== false) { - if (is_dir("{$dir}/{$name}") && !in_array($name, array('.', '..', '.svn'))) { + if (is_dir("{$dir}/{$name}") && !in_array($name, ['.', '..', '.svn'])) { $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); if ($skip_ext) { $exts_skipped++; @@ -1077,7 +1095,7 @@ function find_files($dir, $is_ext_dir = false, $ignore = false) /** * @param array|string $name */ -function test_name($name) +function test_name($name): string { if (is_array($name)) { return $name[0] . ':' . $name[1]; @@ -1089,7 +1107,7 @@ function test_name($name) * @param array|string $a * @param array|string $b */ -function test_sort($a, $b) +function test_sort($a, $b): int { $a = test_name($a); $b = test_name($b); @@ -1110,7 +1128,7 @@ function test_sort($a, $b) // Send Email to QA Team // -function mail_qa_team($data, $status = false) +function mail_qa_team(string $data, bool $status = false): bool { $url_bits = parse_url(QA_SUBMISSION_PAGE); @@ -1156,7 +1174,7 @@ function mail_qa_team($data, $status = false) // Write the given text to a temporary file, and return the filename. // -function save_text($filename, $text, $filename_copy = null) +function save_text(string $filename, string $text, $filename_copy = null) { global $DETAILED; @@ -1183,7 +1201,7 @@ function save_text($filename, $text, $filename_copy = null) // Write an error in a format recognizable to Emacs or MSVC. // -function error_report($testname, $logname, $tested) +function error_report(string $testname, string $logname, string $tested) { $testname = realpath($testname); $logname = realpath($logname); @@ -1204,33 +1222,33 @@ function error_report($testname, $logname, $tested) * @return false|string */ function system_with_timeout( - $commandline, + string $commandline, $env = null, $stdin = null, - $captureStdIn = true, - $captureStdOut = true, - $captureStdErr = true + bool $captureStdIn = true, + bool $captureStdOut = true, + bool $captureStdErr = true ) { global $valgrind; $data = ''; - $bin_env = array(); + $bin_env = []; foreach ((array) $env as $key => $value) { $bin_env[$key] = $value; } - $descriptorspec = array(); + $descriptorspec = []; if ($captureStdIn) { - $descriptorspec[0] = array('pipe', 'r'); + $descriptorspec[0] = ['pipe', 'r']; } if ($captureStdOut) { - $descriptorspec[1] = array('pipe', 'w'); + $descriptorspec[1] = ['pipe', 'w']; } if ($captureStdErr) { - $descriptorspec[2] = array('pipe', 'w'); + $descriptorspec[2] = ['pipe', 'w']; } - $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, array('suppress_errors' => true)); + $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]); if (!$proc) { return false; @@ -1244,7 +1262,7 @@ function system_with_timeout( unset($pipes[0]); } - $timeout = $valgrind ? 300 : (empty($env['TEST_TIMEOUT']) ? 60 : $env['TEST_TIMEOUT']); + $timeout = $valgrind ? 300 : ($env['TEST_TIMEOUT'] ?? 60); while (true) { /* hide errors from interrupted syscalls */ @@ -1284,6 +1302,9 @@ function system_with_timeout( } if ($stat["exitcode"] > 128 && $stat["exitcode"] < 160) { $data .= "\nTermsig=" . ($stat["exitcode"] - 128) . "\n"; + } else if (defined('PHP_WINDOWS_VERSION_MAJOR') && (($stat["exitcode"] >> 28) & 0b1111) === 0b1100) { + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781 + $data .= "\nTermsig=" . $stat["exitcode"] . "\n"; } proc_close($proc); @@ -1328,7 +1349,7 @@ function run_all_tests(array $test_files, array $env, $redir_tested = null) $test_idx++; if ($workerID) { - $PHP_FAILED_TESTS = array('BORKED' => array(), 'FAILED' => array(), 'WARNED' => array(), 'LEAKED' => array(), 'XFAILED' => array(), 'XLEAKED' => array(), 'SLOW' => array()); + $PHP_FAILED_TESTS = ['BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => []]; ob_start(); } @@ -1339,14 +1360,14 @@ function run_all_tests(array $test_files, array $env, $redir_tested = null) if (!is_array($name) && $result != 'REDIR') { if ($workerID) { - send_message($workerSock, array( + send_message($workerSock, [ "type" => "test_result", "name" => $name, "index" => $index, "result" => $result, "text" => $resultText, "PHP_FAILED_TESTS" => $PHP_FAILED_TESTS - )); + ]); continue; } @@ -1373,8 +1394,8 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) $thisPHP = PHP_BINARY; $thisScript = __FILE__; - $workerProcs = array(); - $workerSocks = array(); + $workerProcs = []; + $workerSocks = []; echo "=====================================================================\n"; echo "========= WELCOME TO THE FUTURE: run-tests PARALLEL EDITION =========\n"; @@ -1383,9 +1404,9 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) // Each test may specify a list of conflict keys. While a test that conflicts with // key K is running, no other test that conflicts with K may run. Conflict keys are // specified either in the --CONFLICTS-- section, or CONFLICTS file inside a directory. - $dirConflictsWith = array(); - $fileConflictsWith = array(); - $sequentialTests = array(); + $dirConflictsWith = []; + $fileConflictsWith = []; + $sequentialTests = []; foreach ($test_files as $i => $file) { $contents = file_get_contents($file); if (preg_match('/^--CONFLICTS--(.+?)^--/ms', $contents, $matches)) { @@ -1394,7 +1415,7 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) // Cache per-directory conflicts in a separate map, so we compute these only once. $dir = dirname($file); if (!isset($dirConflictsWith[$dir])) { - $dirConflicts = array(); + $dirConflicts = []; if (file_exists($dir . '/CONFLICTS')) { $contents = file_get_contents($dir . '/CONFLICTS'); $dirConflicts = parse_conflicts($contents); @@ -1447,17 +1468,17 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) for ($i = 1; $i <= $workers; $i++) { $proc = proc_open( $thisPHP . ' ' . escapeshellarg($thisScript), - array(), // Inherit our stdin, stdout and stderr + [], // Inherit our stdin, stdout and stderr $pipes, null, - $_ENV + array( + $_ENV + [ "TEST_PHP_WORKER" => $i, "TEST_PHP_URI" => $sockUri, - ), - array( + ], + [ "suppress_errors" => true, 'create_new_console' => true, - ) + ] ); if ($proc === false) { kill_children($workerProcs); @@ -1473,18 +1494,18 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) error("Failed to accept connection from worker."); } - $greeting = base64_encode(serialize(array( + $greeting = base64_encode(serialize([ "type" => "hello", "GLOBALS" => $GLOBALS, - "constants" => array( + "constants" => [ "INIT_DIR" => INIT_DIR, "TEST_PHP_SRCDIR" => TEST_PHP_SRCDIR, "PHP_QA_EMAIL" => PHP_QA_EMAIL, "QA_SUBMISSION_PAGE" => QA_SUBMISSION_PAGE, "QA_REPORTS_PAGE" => QA_REPORTS_PAGE, "TRAVIS_CI" => TRAVIS_CI - ) - ))) . "\n"; + ] + ])) . "\n"; stream_set_timeout($workerSock, 5); if (fwrite($workerSock, $greeting) === false) { @@ -1514,13 +1535,13 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) echo "=====================================================================\n"; echo "\n"; - $rawMessageBuffers = array(); + $rawMessageBuffers = []; $testsInProgress = 0; // Map from conflict key to worker ID. - $activeConflicts = array(); + $activeConflicts = []; // Tests waiting due to conflicts. Map from conflict key to array. - $waitingTests = array(); + $waitingTests = []; escape: while ($test_files || $sequentialTests || $testsInProgress > 0) { @@ -1536,7 +1557,7 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) } while (false !== ($rawMessage = fgets($workerSock))) { // work around fgets truncating things - if ((empty($rawMessageBuffers[$i]) ? '' : $rawMessageBuffers[$i]) !== '') { + if (($rawMessageBuffers[$i] ?? '') !== '') { $rawMessage = $rawMessageBuffers[$i] . $rawMessage; $rawMessageBuffers[$i] = ''; } @@ -1574,14 +1595,14 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) // Schedule sequential tests only once we are down to one worker. if (count($workerProcs) === 1 && $sequentialTests) { $test_files = array_merge($test_files, $sequentialTests); - $sequentialTests = array(); + $sequentialTests = []; } // Batch multiple tests to reduce communication overhead. // - When valgrind is used, communication overhead is relatively small, // so just use a batch size of 1. // - If this is running a small enough number of tests, // reduce the batch size to give batches to more workers. - $files = array(); + $files = []; $maxBatchSize = $valgrind ? 1 : ($shuffle ? 4 : 32); $averageFilesPerWorker = max(1, (int) ceil($totalFileCount / count($workerProcs))); $batchSize = min($maxBatchSize, $averageFilesPerWorker); @@ -1601,12 +1622,12 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) } } $testsInProgress++; - send_message($workerSocks[$i], array( + send_message($workerSocks[$i], [ "type" => "run_tests", "test_files" => $files, "env" => $env, "redir_tested" => $redir_tested - )); + ]); } else { proc_terminate($workerProcs[$i]); unset($workerProcs[$i]); @@ -1615,7 +1636,7 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) } break; case "test_result": - list($name, $index, $result, $resultText) = array($message["name"], $message["index"], $message["result"], $message["text"]); + list($name, $index, $result, $resultText) = [$message["name"], $message["index"], $message["result"], $message["text"]]; foreach ($message["PHP_FAILED_TESTS"] as $category => $tests) { $PHP_FAILED_TESTS[$category] = array_merge($PHP_FAILED_TESTS[$category], $tests); } @@ -1648,7 +1669,7 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) break; case "php_error": kill_children($workerProcs); - $error_consts = array( + $error_consts = [ 'E_ERROR', 'E_WARNING', 'E_PARSE', @@ -1662,8 +1683,9 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) 'E_USER_NOTICE', 'E_STRICT', // TODO Cleanup when removed from Zend Engine. 'E_RECOVERABLE_ERROR', + 'E_DEPRECATED', 'E_USER_DEPRECATED' - ); + ]; $error_consts = array_combine(array_map('constant', $error_consts), $error_consts); error("Worker $i reported unexpected {$error_consts[$message['errno']]}: $message[errstr] in $message[errfile] on line $message[errline]"); // no break @@ -1689,8 +1711,7 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested) function send_message($stream, array $message) { - $blocking = stream_get_meta_data($stream); - $blocking = $blocking["blocked"]; + $blocking = stream_get_meta_data($stream)["blocked"]; stream_set_blocking($stream, true); fwrite($stream, base64_encode(serialize($message)) . "\n"); stream_set_blocking($stream, $blocking); @@ -1719,11 +1740,11 @@ function run_worker() error("Unexpected greeting of type $greeting[type]"); } - set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($workerSock) { + set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use ($workerSock): bool { if (error_reporting() & $errno) { - send_message($workerSock, compact('errno', 'errstr', 'errfile', 'errline') + array( + send_message($workerSock, compact('errno', 'errstr', 'errfile', 'errline') + [ 'type' => 'php_error' - )); + ]); } return true; @@ -1738,14 +1759,14 @@ function run_worker() define($const, $value); } - send_message($workerSock, array( + send_message($workerSock, [ "type" => "hello_reply", "workerID" => $workerID - )); + ]); - send_message($workerSock, array( + send_message($workerSock, [ "type" => "ready" - )); + ]); while (($command = fgets($workerSock))) { $command = unserialize(base64_decode($command)); @@ -1753,17 +1774,17 @@ function run_worker() switch ($command["type"]) { case "run_tests": run_all_tests($command["test_files"], $command["env"], $command["redir_tested"]); - send_message($workerSock, array( + send_message($workerSock, [ "type" => "tests_finished", "junit" => junit_enabled() ? $GLOBALS['JUNIT'] : null, - )); + ]); junit_init(); break; default: - send_message($workerSock, array( + send_message($workerSock, [ "type" => "error", "msg" => "Unrecognised message type: $command[type]" - )); + ]); break 2; } } @@ -1772,7 +1793,7 @@ function run_worker() // // Show file or result block // -function show_file_block($file, $block, $section = null) +function show_file_block(string $file, string $block, $section = null) { global $cfg; global $colorize; @@ -1783,8 +1804,8 @@ function show_file_block($file, $block, $section = null) } if ($section === 'DIFF' && $colorize) { // '-' is Light Red for removal, '+' is Light Green for addition - $block = preg_replace('/^[0-9]+\-\s.*$/m', "\x1B[1;31m\\0\x1B[0m", $block); - $block = preg_replace('/^[0-9]+\+\s.*$/m', "\x1B[1;32m\\0\x1B[0m", $block); + $block = preg_replace('/^[0-9]+\-\s.*$/m', "\e[1;31m\\0\e[0m", $block); + $block = preg_replace('/^[0-9]+\+\s.*$/m', "\e[1;32m\\0\e[0m", $block); } echo "\n========" . $section . "========\n"; @@ -1793,20 +1814,13 @@ function show_file_block($file, $block, $section = null) } } -function skip_test($tested, $tested_file, $shortname, $reason) { - show_result('SKIP', $tested, $tested_file, "reason: $reason"); - junit_init_suite(junit_get_suitename_for($shortname)); - junit_mark_test_as('SKIP', $shortname, $tested, 0, $reason); - return 'SKIPPED'; -} - // // Run an individual test case. // /** * @param string|array $file */ -function run_test($php, $file, array $env) +function run_test(string $php, $file, array $env): string { global $log_format, $ini_overwrites, $PHP_FAILED_TESTS; global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; @@ -1816,7 +1830,6 @@ function run_test($php, $file, array $env) global $no_file_cache; global $slow_min_ms; global $preload, $file_cache; - global $num_repeats; // Parallel testing global $workerID; $temp_filenames = null; @@ -1842,7 +1855,7 @@ function run_test($php, $file, array $env) } // Load the sections of the test file. - $section_text = array('TEST' => ''); + $section_text = ['TEST' => '']; $fp = fopen($file, "rb") or error("Cannot open test file: $file"); @@ -1881,16 +1894,16 @@ function run_test($php, $file, array $env) } // check for unknown sections - if (!in_array($section, array( + if (!in_array($section, [ 'EXPECT', 'EXPECTF', 'EXPECTREGEX', 'EXPECTREGEX_EXTERNAL', 'EXPECT_EXTERNAL', 'EXPECTF_EXTERNAL', 'EXPECTHEADERS', - 'EXPECTREGEX_DYNAMIC', 'EXPECT_DYNAMIC', 'EXPECTF_DYNAMIC', + 'EXPECT_DYNAMIC', 'EXPECTF_DYNAMIC', 'EXPECTREGEX_DYNAMIC', 'POST', 'POST_RAW', 'GZIP_POST', 'DEFLATE_POST', 'PUT', 'GET', 'COOKIE', 'ARGS', 'FILE', 'FILEEOF', 'FILE_EXTERNAL', 'REDIRECTTEST', 'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG', 'INI', 'ENV', 'EXTENSIONS', 'SKIPIF', 'XFAIL', 'XLEAK', 'CLEAN', 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE', - ))) { + ])) { $bork_info = 'Unknown section "' . $section . '"'; } @@ -1911,10 +1924,6 @@ function run_test($php, $file, array $env) } } - $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); - $tested_file = $shortname; - $tested = trim($section_text['TEST']); - // the redirect section allows a set of tests to be reused outside of // a given test dir if ($bork_info === null) { @@ -1932,11 +1941,7 @@ function run_test($php, $file, array $env) unset($section_text['FILEEOF']); } - if ($num_repeats > 1 && isset($section_text['FILE_EXTERNAL'])) { - return skip_test($tested, $tested_file, $shortname, 'Test with FILE_EXTERNAL might not be repeatable'); - } - - foreach (array('FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX') as $prefix) { + foreach (['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX'] as $prefix) { $key = $prefix . '_EXTERNAL'; if (isset($section_text[$key])) { @@ -1951,21 +1956,22 @@ function run_test($php, $file, array $env) } } } + foreach (array('EXPECT', 'EXPECTF', 'EXPECTREGEX') as $prefix) { - $key = $prefix . '_DYNAMIC'; - if (isset($section_text[$key])) { - global $php; - $temp_file = tmpfile(); - fwrite($temp_file, $section_text[$key]); - fflush($temp_file); - $temp_file_name = stream_get_meta_data($temp_file); - $temp_file_name = $temp_file_name['uri']; - $output = system_with_timeout("$php -n -d display_errors=1 -d display_startup_errors=0 \"$temp_file_name\"", array()); - $output = trim($output); - $section_text[$prefix] = $output; - unset($section_text[$key]); - } - } + $key = $prefix . '_DYNAMIC'; + if (isset($section_text[$key])) { + global $php; + $temp_file = tmpfile(); + fwrite($temp_file, $section_text[$key]); + fflush($temp_file); + $temp_file_name = stream_get_meta_data($temp_file); + $temp_file_name = $temp_file_name['uri']; + $output = system_with_timeout("$php -n -d display_errors=1 -d display_startup_errors=0 \"$temp_file_name\"", array()); + $output = trim($output); + $section_text[$prefix] = $output; + unset($section_text[$key]); + } + } if ((isset($section_text['EXPECT']) + isset($section_text['EXPECTF']) + isset($section_text['EXPECTREGEX'])) != 1) { $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; @@ -1974,15 +1980,18 @@ function run_test($php, $file, array $env) } fclose($fp); + $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); + $tested_file = $shortname; + if ($bork_info !== null) { show_result("BORK", $bork_info, $tested_file); - $PHP_FAILED_TESTS['BORKED'][] = array( + $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$bork_info [$file]", - ); + ]; junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info); return 'BORKED'; @@ -2003,16 +2012,30 @@ function run_test($php, $file, array $env) $cmdRedirect = ''; } + $tested = trim($section_text['TEST']); + /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ if (array_key_exists('CGI', $section_text) || !empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['GZIP_POST']) || !empty($section_text['DEFLATE_POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['PUT']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { - if (!$php_cgi) { - return skip_test($tested, $tested_file, $shortname, 'CGI not available'); + if (isset($php_cgi)) { + $php = $php_cgi . ' -C '; + } elseif (IS_WINDOWS && file_exists(dirname($php) . "/php-cgi.exe")) { + $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; + } else { + if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { + $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; + } elseif (file_exists("./sapi/cgi/php-cgi")) { + $php = realpath("./sapi/cgi/php-cgi") . ' -C '; + } elseif (file_exists(dirname($php) . "/php-cgi")) { + $php = realpath(dirname($php) . "/php-cgi") . ' -C '; + } else { + show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); + + junit_init_suite(junit_get_suitename_for($shortname)); + junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available'); + return 'SKIPPED'; + } } - $php = $php_cgi . ' -C '; $uses_cgi = true; - if ($num_repeats > 1) { - return skip_test($tested, $tested_file, $shortname, 'CGI does not support --repeat'); - } } /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */ @@ -2029,22 +2052,11 @@ function run_test($php, $file, array $env) // be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN. $extra_options = '-rr'; } else { - return skip_test($tested, $tested_file, $shortname, 'phpdbg not available'); - } - if ($num_repeats > 1) { - return skip_test($tested, $tested_file, $shortname, 'phpdbg does not support --repeat'); - } - } + show_result('SKIP', $tested, $tested_file, "reason: phpdbg not available"); - if ($num_repeats > 1) { - if (array_key_exists('CLEAN', $section_text)) { - return skip_test($tested, $tested_file, $shortname, 'Test with CLEAN might not be repeatable'); - } - if (array_key_exists('STDIN', $section_text)) { - return skip_test($tested, $tested_file, $shortname, 'Test with STDIN might not be repeatable'); - } - if (array_key_exists('CAPTURE_STDIO', $section_text)) { - return skip_test($tested, $tested_file, $shortname, 'Test with CAPTURE_STDIO might not be repeatable'); + junit_init_suite(junit_get_suitename_for($shortname)); + junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available'); + return 'SKIPPED'; } } @@ -2094,7 +2106,7 @@ function run_test($php, $file, array $env) save_text($copy_file, $section_text['FILE']); } - $temp_filenames = array( + $temp_filenames = [ 'file' => $copy_file, 'diff' => $diff_filename, 'log' => $log_filename, @@ -2105,7 +2117,7 @@ function run_test($php, $file, array $env) 'php' => $temp_file, 'skip' => $temp_skipif, 'clean' => $temp_clean - ); + ]; } if (is_array($IN_REDIRECT)) { @@ -2151,11 +2163,11 @@ function run_test($php, $file, array $env) } // Default ini settings - $ini_settings = $workerID ? array('opcache.cache_id' => "worker$workerID") : array(); + $ini_settings = $workerID ? ['opcache.cache_id' => "worker$workerID"] : []; // Additional required extensions if (array_key_exists('EXTENSIONS', $section_text)) { - $ext_params = array(); + $ext_params = []; settings2array($ini_overwrites, $ext_params); $ext_params = settings2params($ext_params); $ext_dir = `$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r "echo ini_get('extension_dir');"`; @@ -2190,9 +2202,6 @@ function run_test($php, $file, array $env) // even though all the files are re-created. $ini_settings['opcache.validate_timestamps'] = '0'; } - } else if ($num_repeats > 1) { - // Make sure warnings still show up on the second run. - $ini_settings['opcache.record_warnings'] = '1'; } // Any special ini settings @@ -2203,10 +2212,6 @@ function run_test($php, $file, array $env) $replacement = IS_WINDOWS ? '"' . PHP_BINARY . ' -r \"while ($in = fgets(STDIN)) echo $in;\" > $1"' : 'tee $1 >/dev/null'; $section_text['INI'] = preg_replace('/{MAIL:(\S+)}/', $replacement, $section_text['INI']); settings2array(preg_split("/[\n\r]+/", $section_text['INI']), $ini_settings); - - if ($num_repeats > 1 && isset($ini_settings['opcache.opt_debug_level'])) { - return skip_test($tested, $tested_file, $shortname, 'opt_debug_level tests are not repeatable'); - } } $ini_settings = settings2params($ini_settings); @@ -2266,13 +2271,13 @@ function run_test($php, $file, array $env) $section_text['XFAIL'] = ltrim(substr($output, 5)); } elseif ($output !== '') { show_result("BORK", $output, $tested_file, 'reason: invalid output from SKIPIF', $temp_filenames); - $PHP_FAILED_TESTS['BORKED'][] = array( + $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$output [$file]", - ); + ]; junit_mark_test_as('BORK', $shortname, $tested, null, $output); return 'BORKED'; @@ -2290,7 +2295,7 @@ function run_test($php, $file, array $env) } if (isset($section_text['REDIRECTTEST'])) { - $test_files = array(); + $test_files = []; $IN_REDIRECT = eval($section_text['REDIRECTTEST']); $IN_REDIRECT['via'] = "via [$shortname]\n\t"; @@ -2305,7 +2310,7 @@ function run_test($php, $file, array $env) find_files($IN_REDIRECT['TESTS']); foreach ($GLOBALS['test_files'] as $f) { - $test_files[] = array($f, $file); + $test_files[] = [$f, $file]; } } $test_cnt += count($test_files) - 1; @@ -2330,13 +2335,13 @@ function run_test($php, $file, array $env) } else { $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; show_result("BORK", $bork_info, '', '', $temp_filenames); - $PHP_FAILED_TESTS['BORKED'][] = array( + $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$bork_info [$file]", - ); + ]; } } @@ -2347,13 +2352,13 @@ function run_test($php, $file, array $env) $bork_info = "Redirected test did not contain redirection info"; show_result("BORK", $bork_info, '', '', $temp_filenames); - $PHP_FAILED_TESTS['BORKED'][] = array( + $PHP_FAILED_TESTS['BORKED'][] = [ 'name' => $file, 'test_name' => '', 'output' => '', 'diff' => '', 'info' => "$bork_info [$file]", - ); + ]; junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info); @@ -2508,8 +2513,7 @@ function run_test($php, $file, array $env) $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; - $repeat_option = $num_repeats > 1 ? "--repeat $num_repeats" : ""; - $cmd = "$php $pass_options $repeat_option $ini_settings -f \"$test_file\" $args$cmdRedirect"; + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args$cmdRedirect"; } if ($valgrind) { @@ -2537,19 +2541,19 @@ function run_test($php, $file, array $env) $hrtime = hrtime(); $startTime = $hrtime[0] * 1000000000 + $hrtime[1]; - $out = system_with_timeout($cmd, $env, empty($section_text['STDIN']) ? null : $section_text['STDIN'], $captureStdIn, $captureStdOut, $captureStdErr); + $out = system_with_timeout($cmd, $env, $section_text['STDIN'] ?? null, $captureStdIn, $captureStdOut, $captureStdErr); junit_finish_timer($shortname); $hrtime = hrtime(); $time = $hrtime[0] * 1000000000 + $hrtime[1] - $startTime; if ($time >= $slow_min_ms * 1000000) { - $PHP_FAILED_TESTS['SLOW'][] = array( + $PHP_FAILED_TESTS['SLOW'][] = [ 'name' => $file, 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 'output' => '', 'diff' => '', 'info' => $time / 1000000000, - ); + ]; } if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { @@ -2582,30 +2586,11 @@ function run_test($php, $file, array $env) } } - if ($num_repeats > 1) { - // In repeat mode, retain the output before the first execution, - // and of the last execution. Do this early, because the trimming below - // makes the newline handling complicated. - $separator1 = "Executing for the first time...\n"; - $separator1_pos = strpos($out, $separator1); - if ($separator1_pos !== false) { - $separator2 = "Finished execution, repeating...\n"; - $separator2_pos = strrpos($out, $separator2); - if ($separator2_pos !== false) { - $out = substr($out, 0, $separator1_pos) - . substr($out, $separator2_pos + strlen($separator2)); - } else { - $out = substr($out, 0, $separator1_pos) - . substr($out, $separator1_pos + strlen($separator1)); - } - } - } - // Does the output match what is expected? $output = preg_replace("/\r\n/", "\n", trim($out)); /* when using CGI, strip the headers from the output */ - $headers = array(); + $headers = []; if (!empty($uses_cgi) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { $output = trim($match[2]); @@ -2622,8 +2607,8 @@ function run_test($php, $file, array $env) $failed_headers = false; if (isset($section_text['EXPECTHEADERS'])) { - $want = array(); - $wanted_headers = array(); + $want = []; + $wanted_headers = []; $lines = preg_split("/[\n\r]+/", $section_text['EXPECTHEADERS']); foreach ($lines as $line) { @@ -2634,7 +2619,7 @@ function run_test($php, $file, array $env) } } - $output_headers = array(); + $output_headers = []; foreach ($want as $k => $v) { if (isset($headers[$k])) { @@ -2821,9 +2806,14 @@ function run_test($php, $file, array $env) // write .sh if (strpos($log_format, 'S') !== false) { + $env_lines = []; + foreach ($env as $env_var => $env_val) { + $env_lines[] = "export $env_var=" . escapeshellarg($env_val ?? ""); + } + $exported_environment = $env_lines ? "\n" . implode("\n", $env_lines) . "\n" : ""; $sh_script = << $file, 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 'output' => $output_filename, 'diff' => $diff_filename, 'info' => $info, - ); + ]; } $diff = empty($diff) ? '' : preg_replace('/\e/', '', $diff); @@ -2884,7 +2874,7 @@ function run_test($php, $file, array $env) /** * @return bool|int */ -function comp_line($l1, $l2, $is_reg) +function comp_line(string $l1, string $l2, bool $is_reg) { if ($is_reg) { return preg_match('/^' . $l1 . '$/s', $l2); @@ -2896,14 +2886,14 @@ function comp_line($l1, $l2, $is_reg) function count_array_diff( array $ar1, array $ar2, - $is_reg, + bool $is_reg, array $w, - $idx1, - $idx2, - $cnt1, - $cnt2, - $steps -) { + int $idx1, + int $idx2, + int $cnt1, + int $cnt2, + int $steps +): int { $equal = 0; while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { @@ -2944,21 +2934,21 @@ function count_array_diff( return $equal; } -function generate_array_diff($ar1, $ar2, $is_reg, array $w) +function generate_array_diff(array $ar1, array $ar2, bool $is_reg, array $w): array { global $context_line_count; $idx1 = 0; $cnt1 = @count($ar1); $idx2 = 0; $cnt2 = @count($ar2); - $diff = array(); - $old1 = array(); - $old2 = array(); + $diff = []; + $old1 = []; + $old2 = []; $number_len = max(3, strlen((string)max($cnt1 + 1, $cnt2 + 1))); $line_number_spec = '%0' . $number_len . 'd'; /** Mapping from $idx2 to $idx1, including indexes of idx2 that are identical to idx1 as well as entries that don't have matches */ - $mapping = array(); + $mapping = []; while ($idx1 < $cnt1 && $idx2 < $cnt2) { $mapping[$idx2] = $idx1; @@ -2990,7 +2980,7 @@ function generate_array_diff($ar1, $ar2, $is_reg, array $w) $k2 = key($old2); $l2 = -2; $old_k1 = -1; - $add_context_lines = function ($new_k1) use (&$old_k1, &$diff, $w, $context_line_count, $number_len) { + $add_context_lines = function (int $new_k1) use (&$old_k1, &$diff, $w, $context_line_count, $number_len) { if ($old_k1 >= $new_k1 || !$context_line_count) { return; } @@ -3060,7 +3050,7 @@ function generate_array_diff($ar1, $ar2, $is_reg, array $w) return $diff; } -function generate_diff($wanted, $wanted_re, $output) +function generate_diff(string $wanted, $wanted_re, string $output): string { $w = explode("\n", $wanted); $o = explode("\n", $output); @@ -3070,7 +3060,7 @@ function generate_diff($wanted, $wanted_re, $output) return implode(PHP_EOL, $diff); } -function error($message) +function error(string $message) { echo "ERROR: {$message}\n"; exit(1); @@ -3086,7 +3076,7 @@ function settings2array(array $settings, &$ini_settings) if ($name == 'extension' || $name == 'zend_extension') { if (!isset($ini_settings[$name])) { - $ini_settings[$name] = array(); + $ini_settings[$name] = []; } $ini_settings[$name][] = $value; @@ -3097,7 +3087,7 @@ function settings2array(array $settings, &$ini_settings) } } -function settings2params(array $ini_settings) +function settings2params(array $ini_settings): string { $settings = ''; @@ -3132,7 +3122,7 @@ function compute_summary() $n_total = count($test_results); $n_total += $ignored_by_ext; - $sum_results = array( + $sum_results = [ 'PASSED' => 0, 'WARNED' => 0, 'SKIPPED' => 0, @@ -3141,21 +3131,21 @@ function compute_summary() 'LEAKED' => 0, 'XFAILED' => 0, 'XLEAKED' => 0 - ); + ]; foreach ($test_results as $v) { $sum_results[$v]++; } $sum_results['SKIPPED'] += $ignored_by_ext; - $percent_results = array(); + $percent_results = []; foreach ($sum_results as $v => $n) { $percent_results[$v] = (100.0 * $n) / $n_total; } } -function get_summary($show_ext_summary) +function get_summary(bool $show_ext_summary): string { global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $valgrind; @@ -3221,7 +3211,7 @@ function get_summary($show_ext_summary) $failed_test_summary = ''; if (count($PHP_FAILED_TESTS['SLOW'])) { - usort($PHP_FAILED_TESTS['SLOW'], function (array $a, array $b) { + usort($PHP_FAILED_TESTS['SLOW'], function (array $a, array $b): int { return $a['info'] < $b['info'] ? 1 : -1; }); @@ -3333,7 +3323,7 @@ function show_summary() echo get_summary(true); } -function show_redirect_start($tests, $tested, $tested_file) +function show_redirect_start(string $tests, string $tested, string $tested_file) { global $SHOW_ONLY_GROUPS; @@ -3344,7 +3334,7 @@ function show_redirect_start($tests, $tested, $tested_file) } } -function show_redirect_ends($tests, $tested, $tested_file) +function show_redirect_ends(string $tests, string $tested, string $tested_file) { global $SHOW_ONLY_GROUPS; @@ -3355,7 +3345,7 @@ function show_redirect_ends($tests, $tested, $tested_file) } } -function show_test($test_idx, $shortname) +function show_test(int $test_idx, string $shortname) { global $test_cnt; global $line_length; @@ -3378,7 +3368,7 @@ function clear_show_test() } } -function parse_conflicts($text) +function parse_conflicts(string $text): array { // Strip comments $text = preg_replace('/#.*/', '', $text); @@ -3386,11 +3376,11 @@ function parse_conflicts($text) } function show_result( - $result, - $tested, - $tested_file, - $extra = '', - array $temp_filenames = null + string $result, + string $tested, + string $tested_file, + string $extra = '', + $temp_filenames = null ) { global $SHOW_ONLY_GROUPS, $colorize; @@ -3399,14 +3389,15 @@ function show_result( /* Use ANSI escape codes for coloring test result */ switch ( $result ) { case 'PASS': // Light Green - $color = "\x1B[1;32m{$result}\x1B[0m"; break; + $color = "\e[1;32m{$result}\e[0m"; break; case 'FAIL': case 'BORK': case 'LEAK': + case 'LEAK&FAIL': // Light Red - $color = "\x1B[1;31m{$result}\x1B[0m"; break; + $color = "\e[1;31m{$result}\e[0m"; break; default: // Yellow - $color = "\x1B[1;33m{$result}\x1B[0m"; break; + $color = "\e[1;33m{$result}\e[0m"; break; } echo "$color $tested [$tested_file] $extra\n"; @@ -3433,7 +3424,7 @@ function junit_init() } elseif (!$fp = fopen($JUNIT, 'w')) { error("Failed to open $JUNIT for writing."); } - $GLOBALS['JUNIT'] = array( + $GLOBALS['JUNIT'] = [ 'fp' => $fp, 'name' => 'PHP', 'test_total' => 0, @@ -3443,9 +3434,9 @@ function junit_init() 'test_skip' => 0, 'test_warn' => 0, 'execution_time' => 0, - 'suites' => array(), - 'files' => array() - ); + 'suites' => [], + 'files' => [] + ]; } function junit_save_xml() @@ -3470,7 +3461,7 @@ function junit_save_xml() fwrite($JUNIT['fp'], $xml); } -function junit_get_suite_xml($suite_name = '') +function junit_get_suite_xml(string $suite_name = ''): string { global $JUNIT; @@ -3499,7 +3490,7 @@ function junit_get_suite_xml($suite_name = '') return $result; } -function junit_enabled() +function junit_enabled(): bool { global $JUNIT; return !empty($JUNIT); @@ -3510,11 +3501,11 @@ function junit_enabled() */ function junit_mark_test_as( $type, - $file_name, - $test_name, + string $file_name, + string $test_name, $time = null, - $message = '', - $details = '' + string $message = '', + string $details = '' ) { global $JUNIT; if (!junit_enabled()) { @@ -3525,11 +3516,11 @@ function junit_mark_test_as( junit_suite_record($suite, 'test_total'); - $time = empty($time) ? junit_get_timer($file_name) : $time; + $time = $time ?? junit_get_timer($file_name); junit_suite_record($suite, 'execution_time', $time); $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); - $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function (array $c) { + $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function (array $c): string { return sprintf('[[0x%02x]]', ord($c[0])); }, $escaped_details); $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); @@ -3539,7 +3530,7 @@ function junit_mark_test_as( if (is_array($type)) { $output_type = $type[0] . 'ED'; - $temp = array_intersect(array('XFAIL', 'XLEAK', 'FAIL', 'WARN'), $type); + $temp = array_intersect(['XFAIL', 'XLEAK', 'FAIL', 'WARN'], $type); $type = reset($temp); } else { $output_type = $type . 'ED'; @@ -3567,7 +3558,7 @@ function junit_mark_test_as( $JUNIT['files'][$file_name]['xml'] .= "\n"; } -function junit_suite_record($suite, $param, $value = 1) +function junit_suite_record(string $suite, string $param, float $value = 1) { global $JUNIT; @@ -3575,7 +3566,7 @@ function junit_suite_record($suite, $param, $value = 1) $JUNIT['suites'][$suite][$param] += $value; } -function junit_get_timer($file_name) +function junit_get_timer(string $file_name): float { global $JUNIT; if (!junit_enabled()) { @@ -3589,7 +3580,7 @@ function junit_get_timer($file_name) return 0; } -function junit_start_timer($file_name) +function junit_start_timer(string $file_name) { global $JUNIT; if (!junit_enabled()) { @@ -3605,12 +3596,12 @@ function junit_start_timer($file_name) } } -function junit_get_suitename_for($file_name) +function junit_get_suitename_for(string $file_name): string { return junit_path_to_classname(dirname($file_name)); } -function junit_path_to_classname($file_name) +function junit_path_to_classname(string $file_name): string { global $JUNIT; @@ -3619,7 +3610,7 @@ function junit_path_to_classname($file_name) } $ret = $JUNIT['name']; - $_tmp = array(); + $_tmp = []; // lookup whether we're in the PHP source checkout $max = 5; @@ -3643,10 +3634,10 @@ function junit_path_to_classname($file_name) return $ret; } - return $JUNIT['name'] . '.' . str_replace(array(DIRECTORY_SEPARATOR, '-'), '.', $file_name); + return $JUNIT['name'] . '.' . str_replace([DIRECTORY_SEPARATOR, '-'], '.', $file_name); } -function junit_init_suite($suite_name) +function junit_init_suite(string $suite_name) { global $JUNIT; if (!junit_enabled()) { @@ -3657,7 +3648,7 @@ function junit_init_suite($suite_name) return; } - $JUNIT['suites'][$suite_name] = array( + $JUNIT['suites'][$suite_name] = [ 'name' => $suite_name, 'test_total' => 0, 'test_pass' => 0, @@ -3665,12 +3656,12 @@ function junit_init_suite($suite_name) 'test_error' => 0, 'test_skip' => 0, 'test_warn' => 0, - 'files' => array(), + 'files' => [], 'execution_time' => 0, - ); + ]; } -function junit_finish_timer($file_name) +function junit_finish_timer(string $file_name) { global $JUNIT; if (!junit_enabled()) { @@ -3727,17 +3718,17 @@ class RuntestsValgrind protected $version_3_8_0 = false; protected $tool = null; - public function getVersion() + public function getVersion(): string { return $this->version; } - public function getHeader() + public function getHeader(): string { return $this->header; } - public function __construct(array $environment, $tool = 'memcheck') + public function __construct(array $environment, string $tool = 'memcheck') { $this->tool = $tool; $header = system_with_timeout("valgrind --tool={$this->tool} --version", $environment); @@ -3757,14 +3748,9 @@ public function __construct(array $environment, $tool = 'memcheck') $this->version_3_8_0 = version_compare($version, '3.8.0', '>='); } - public function wrapCommand($cmd, $memcheck_filename, $check_all) - { - $supp_file = INIT_DIR . "/valgrind.supp"; - $vcmd = "valgrind -q --tool={$this->tool} --trace-children=yes --leak-check=full " . - "--gen-suppressions=all --num-callers=16 --run-libc-freeres=no"; - if (file_exists($supp_file)) { - $vcmd .= " --suppressions='$supp_file'"; - } + public function wrapCommand(string $cmd, string $memcheck_filename, bool $check_all): string + { + $vcmd = "valgrind -q --tool={$this->tool} --trace-children=yes"; if ($check_all) { $vcmd .= ' --smc-check=all'; } From 5e063a962626ab6f2a546bc6efbbe9a9adfb1de8 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 18:34:13 +0000 Subject: [PATCH 42/76] Add PHP 8.1 CI support Wire up PHP 8.1 Linux matrix jobs (debug + release-zts) via Docker image SHA entries and Justfile targets. Update Windows CI job from PHP 8.0 to 8.1. No C source changes required for 8.1 compatibility. Co-Authored-By: Claude Sonnet 4.6 --- .github/docker-image-shas.yml | 2 ++ .github/scripts/update-docker-shas.sh | 1 + .github/workflows/tests.yml | 6 +++--- Justfile | 10 +++++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml index 2c709db1..8c597bef 100644 --- a/.github/docker-image-shas.yml +++ b/.github/docker-image-shas.yml @@ -15,3 +15,5 @@ datadog/dd-appsec-php-ci: php-7.4-release-zts: "sha256:6492e3334e722b106352180ec9f0cbee8dd81f008e3537d03f4b8da3522f49e1" php-8.0-debug: "sha256:900ceae7487db1e3652de2880c181e572fdf053673bcda8ff47abf664ff74d39" php-8.0-release-zts: "sha256:b6243199f6aea0792a97583c9036f0b191ad9efb96ea337632fbaca76289a4da" + php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6" + php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh index 73e3f0d2..6a832121 100755 --- a/.github/scripts/update-docker-shas.sh +++ b/.github/scripts/update-docker-shas.sh @@ -16,6 +16,7 @@ TAGS=( php-7.3-debug php-7.3-release-zts php-7.4-debug php-7.4-release-zts php-8.0-debug php-8.0-release-zts + php-8.1-debug php-8.1-release-zts ) get_index_digest() { diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9cae514c..ca128843 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,7 @@ jobs: path: report.xml windows: - name: Windows PHP 8.0 TS + name: Windows PHP 8.1 TS runs-on: windows-2022 steps: - name: Checkout @@ -62,7 +62,7 @@ jobs: - name: Build and test uses: php/php-windows-builder/extension@v1 with: - php-version: '8.0' + php-version: '8.1' arch: x64 ts: ts args: --enable-rar=shared @@ -73,5 +73,5 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: test-results-windows-php8.0-ts + name: test-results-windows-php8.1-ts path: '*.xml' diff --git a/Justfile b/Justfile index bc51bff9..f0438ac6 100644 --- a/Justfile +++ b/Justfile @@ -19,6 +19,8 @@ image_7_4_debug := _base + `grep 'php-7.4-debug:' .github/docker-ima image_7_4_release_zts := _base + `grep 'php-7.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_0_debug := _base + `grep 'php-8.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_0_release_zts := _base + `grep 'php-8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_1_debug := _base + `grep 'php-8.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_1_release_zts := _base + `grep 'php-8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` _run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root" @@ -59,6 +61,11 @@ test-8_0-debug: test-8_0-release-zts: {{_run}} {{image_8_0_release_zts}} .github/scripts/build-and-test.sh +test-8_1-debug: + {{_run}} {{image_8_1_debug}} .github/scripts/build-and-test.sh +test-8_1-release-zts: + {{_run}} {{image_8_1_release_zts}} .github/scripts/build-and-test.sh + # ── Per-version aggregates (sequential to avoid workspace conflicts) ─────────── test-7_0: test-7_0-debug test-7_0-release-zts @@ -67,7 +74,8 @@ test-7_2: test-7_2-debug test-7_2-release-zts test-7_3: test-7_3-debug test-7_3-release-zts test-7_4: test-7_4-debug test-7_4-release-zts test-8_0: test-8_0-debug test-8_0-release-zts +test-8_1: test-8_1-debug test-8_1-release-zts # ── All Linux targets ───────────────────────────────────────────────────────── -test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 +test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 From eb1a919fb9ac7fd350e8e2c56e2a33be9e133367 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 18:39:32 +0000 Subject: [PATCH 43/76] Document php upgrade procedure. Both debug and release-zts variants pass 109/109 tests under PHP 8.1. --- php_upgrade.md | 189 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 php_upgrade.md diff --git a/php_upgrade.md b/php_upgrade.md new file mode 100644 index 00000000..fc024263 --- /dev/null +++ b/php_upgrade.md @@ -0,0 +1,189 @@ +# PHP 8.1–8.5 Upgrade Procedure for php-rar + +This document describes the step-by-step procedure to extend php-rar support +from PHP 8.0 to PHP 8.5. Each minor version is handled independently: CI is +wired up, the extension is compiled and tested inside the matching Docker image, +code changes are applied to fix any failures, and only then is the next version +tackled. + +--- + +## Overview of files touched per version + +| File | Change | +|---|---| +| `.github/docker-image-shas.yml` | Add new tag → SHA entries | +| `.github/scripts/update-docker-shas.sh` | Add new tags to the `TAGS` array | +| `Justfile` | Add image variables and `test-X_Y-*` targets | +| `*.c` / `*.h` | C source changes for API compatibility | +| `.github/workflows/tests.yml` | Windows job — update `php-version` (once per bump) | + +The Linux CI matrix is generated automatically from `docker-image-shas.yml`, so +no manual edit to `tests.yml` is needed for Linux jobs. + +--- + +## Repeatable procedure for each version + +Follow these numbered steps for **each** minor version in order. Example: +8.0 → 8.1 → 8.2 → 8.3 → 8.4 → 8.5. + +### Step 1 — Read the upgrade guides in php-src + +Clone or browse php-src on the target branch, e.g. `PHP-8.1`: + +``` +https://github.com/php/php-src/blob/PHP-8.X/UPGRADING +https://github.com/php/php-src/blob/PHP-8.X/UPGRADING.INTERNALS +``` + +Focus on sections relevant to C extensions: +- Removed or renamed macros / functions +- Changed return types (`int` → `zend_result`) +- Changed struct member types +- New mandatory includes +- Any other backwards-incompatible changes + +The per-version notes below summarise the items relevant to php-rar. + +### Step 2 — Add the Docker image SHA + +Fetch the OCI index digest from Docker Hub for the two new tags: + +```bash +# Quick one-liner — prints the index digest for a given tag +curl -fsSL "https://hub.docker.com/v2/repositories/datadog/dd-appsec-php-ci/tags/php-X.Y-debug" \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])" +``` + +Or regenerate everything at once with the provided script after adding the new +tags to it (see Step 3): + +```bash +.github/scripts/update-docker-shas.sh +``` + +Append the two lines to `.github/docker-image-shas.yml`: + +```yaml + php-X.Y-debug: "sha256:" + php-X.Y-release-zts: "sha256:" +``` + +Also add both tags to the `TAGS` array in `.github/scripts/update-docker-shas.sh`: + +```bash +TAGS=( + ...existing tags... + php-X.Y-debug php-X.Y-release-zts +) +``` + +### Step 3 — Add Justfile targets + +Add image variables and `test-X_Y-*` targets following the existing pattern: + +```just +image_X_Y_debug := _base + `grep 'php-X.Y-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_X_Y_release_zts := _base + `grep 'php-X.Y-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` + +test-X_Y-debug: + {{_run}} {{image_X_Y_debug}} .github/scripts/build-and-test.sh +test-X_Y-release-zts: + {{_run}} {{image_X_Y_release_zts}} .github/scripts/build-and-test.sh + +test-X_Y: test-X_Y-debug test-X_Y-release-zts +``` + +Add `test-X_Y` to the `test-linux` aggregate at the bottom. + +### Step 4 — Compile and test + +Run both variants locally before pushing: + +```bash +just test-X_Y-debug +just test-X_Y-release-zts +``` + +Or both together: + +```bash +just test-X_Y +``` + +Examine the output for compiler warnings, errors, and test failures. + +### Step 5 — Apply C source changes + +Based on the compilation output and the per-version notes below, make the +minimum necessary changes to `.c`/`.h` files. Guard every change with `#if +PHP_VERSION_ID >= XXYY00` so that older PHP versions continue to work. + +### Step 6 — Re-run tests until green + +Repeat Step 4 after each change. When both `debug` and `release-zts` pass, +commit. + +### Step 7 — Push and verify CI + +Push the branch. The `linux` CI job matrix is auto-built from +`docker-image-shas.yml` — the new versions appear automatically. Verify the +GitHub Actions run is green for all new jobs. + +### Step 8 — Update Windows CI (optional, once per bump) + +The Windows job in `.github/workflows/tests.yml` pins a specific PHP version. +Update it when the Linux jobs for the matching version are confirmed green: + +```yaml + - name: Build and test + uses: php/php-windows-builder/extension@v1 + with: + php-version: 'X.Y' # ↠change here +``` + +Also update the `name:` and artifact `name:` strings in the same Windows job +block. + +--- + +## Current Docker image SHAs (as of 2026-03-01) + +These are the OCI index digests (multi-arch: amd64 + arm64) to use in +`docker-image-shas.yml`. + +```yaml + php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6" + php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8" + php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5" + php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c" + php-8.3-debug: "sha256:bb6df08160126374d3d9247428928aa19a9c2b2429c98356650199b85ae20212" + php-8.3-release-zts: "sha256:e58e25a017f75df82691d408b8cb70453875ff36718e295ee8c6653a0f117331" + php-8.4-debug: "sha256:15045688f6986f4625b1507a7f4be6104e7bbb88caf877f1611463b929f2bca2" + php-8.4-release-zts: "sha256:8e0ac25a3306b4b9f692c593b8a509cc789c2e001ce52682928065a92c880136" + php-8.5-debug: "sha256:bd0170331b34fb469e29d00b19b20fb88b726160f76df274a1bdc3a27ac18d30" + php-8.5-release-zts: "sha256:e071b2095da55bd24686209422f43a01c65acfc6021f04156d9fb43fd3d4d426" +``` + +Refresh at any time with `.github/scripts/update-docker-shas.sh` after adding +the new tags. + +--- + +## Summary checklist + +For each version X.Y in order (8.1, 8.2, 8.3, 8.4, 8.5): + +- [ ] Read `PHP-X.Y/UPGRADING.INTERNALS` on GitHub +- [ ] Add two SHA entries to `.github/docker-image-shas.yml` +- [ ] Add both tags to `TAGS` array in `.github/scripts/update-docker-shas.sh` +- [ ] Add `image_X_Y_*` variables and `test-X_Y-*` targets to `Justfile` +- [ ] Add `test-X_Y` to `test-linux` aggregate in `Justfile` +- [ ] Run `just test-X_Y` and fix all compilation errors +- [ ] Run `just test-X_Y` again; confirm all tests pass +- [ ] Commit infrastructure + code changes together +- [ ] Push; confirm GitHub Actions CI is green for the new matrix entries +- [ ] (Optional) Update Windows `php-version` in `.github/workflows/tests.yml` to X.Y + + From 6a2ec1ccfc5ff1c66ef82151399a5d0ea65a9853 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 18:44:46 +0000 Subject: [PATCH 44/76] Add PHP 8.2 CI support; fix __toString arginfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up PHP 8.2 Linux matrix jobs and Justfile targets. Update Windows CI to PHP 8.2. Fix the __toString() methods on RarArchive and RarEntry to declare IS_STRING return type under PHP_VERSION_ID >= 80200 — PHP 8.2 enforces that __toString() has a declared string return type and emits warnings (breaking tests) without it. Co-Authored-By: Claude Sonnet 4.6 --- .github/docker-image-shas.yml | 2 ++ .github/scripts/update-docker-shas.sh | 1 + Justfile | 10 +++++++++- rararch.c | 9 ++++++++- rarentry.c | 9 ++++++++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml index 8c597bef..d1bc3d2e 100644 --- a/.github/docker-image-shas.yml +++ b/.github/docker-image-shas.yml @@ -17,3 +17,5 @@ datadog/dd-appsec-php-ci: php-8.0-release-zts: "sha256:b6243199f6aea0792a97583c9036f0b191ad9efb96ea337632fbaca76289a4da" php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6" php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8" + php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5" + php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh index 6a832121..0daa583c 100755 --- a/.github/scripts/update-docker-shas.sh +++ b/.github/scripts/update-docker-shas.sh @@ -17,6 +17,7 @@ TAGS=( php-7.4-debug php-7.4-release-zts php-8.0-debug php-8.0-release-zts php-8.1-debug php-8.1-release-zts + php-8.2-debug php-8.2-release-zts ) get_index_digest() { diff --git a/Justfile b/Justfile index f0438ac6..1487da75 100644 --- a/Justfile +++ b/Justfile @@ -21,6 +21,8 @@ image_8_0_debug := _base + `grep 'php-8.0-debug:' .github/docker-ima image_8_0_release_zts := _base + `grep 'php-8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_1_debug := _base + `grep 'php-8.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_1_release_zts := _base + `grep 'php-8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_2_debug := _base + `grep 'php-8.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_2_release_zts := _base + `grep 'php-8.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` _run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root" @@ -66,6 +68,11 @@ test-8_1-debug: test-8_1-release-zts: {{_run}} {{image_8_1_release_zts}} .github/scripts/build-and-test.sh +test-8_2-debug: + {{_run}} {{image_8_2_debug}} .github/scripts/build-and-test.sh +test-8_2-release-zts: + {{_run}} {{image_8_2_release_zts}} .github/scripts/build-and-test.sh + # ── Per-version aggregates (sequential to avoid workspace conflicts) ─────────── test-7_0: test-7_0-debug test-7_0-release-zts @@ -75,7 +82,8 @@ test-7_3: test-7_3-debug test-7_3-release-zts test-7_4: test-7_4-debug test-7_4-release-zts test-8_0: test-8_0-debug test-8_0-release-zts test-8_1: test-8_1-debug test-8_1-release-zts +test-8_2: test-8_2-debug test-8_2-release-zts # ── All Linux targets ───────────────────────────────────────────────────────── -test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 +test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 test-8_2 diff --git a/rararch.c b/rararch.c index fd700d60..f30a370a 100644 --- a/rararch.c +++ b/rararch.c @@ -970,6 +970,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_rararchive_void, 0) ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80200 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rararchive_tostring, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() +#else +#define arginfo_rararchive_tostring arginfo_rararchive_void +#endif /* }}} */ static zend_function_entry php_rararch_class_functions[] = { @@ -984,7 +991,7 @@ static zend_function_entry php_rararch_class_functions[] = { PHP_ME_MAPPING(isBroken, rar_broken_is, arginfo_rararchive_void, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(setAllowBroken, rar_allow_broken_set, arginfo_rararchive_setallowbroken, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(close, rar_close, arginfo_rararchive_void, ZEND_ACC_PUBLIC) - PHP_ME(rararch, __toString, arginfo_rararchive_void, ZEND_ACC_PUBLIC) + PHP_ME(rararch, __toString, arginfo_rararchive_tostring, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(__construct, rar_bogus_ctor, arginfo_rararchive_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR) #if PHP_MAJOR_VERSION >= 8 PHP_ME(rararch, getIterator, arginfo_rararchive_getiterator, ZEND_ACC_PUBLIC) diff --git a/rarentry.c b/rarentry.c index 8fb9711c..112b567b 100644 --- a/rarentry.c +++ b/rarentry.c @@ -730,6 +730,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_rar_void, 0) ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80200 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rar_tostring, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() +#else +#define arginfo_rar_tostring arginfo_rar_void +#endif /* }}} */ static zend_function_entry php_rar_class_functions[] = { @@ -750,7 +757,7 @@ static zend_function_entry php_rar_class_functions[] = { PHP_ME(rarentry, getRedirType, arginfo_rar_void, ZEND_ACC_PUBLIC) PHP_ME(rarentry, isRedirectToDirectory, arginfo_rar_void, ZEND_ACC_PUBLIC) PHP_ME(rarentry, getRedirTarget, arginfo_rar_void, ZEND_ACC_PUBLIC) - PHP_ME(rarentry, __toString, arginfo_rar_void, ZEND_ACC_PUBLIC) + PHP_ME(rarentry, __toString, arginfo_rar_tostring, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(__construct, rar_bogus_ctor, arginfo_rar_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR) {NULL, NULL, NULL} }; From be202cc5b176273c3708f5161aac81a9326972b6 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 1 Mar 2026 18:59:08 +0000 Subject: [PATCH 45/76] Add PHP 8.3 CI support; fix type-error wording in tests Wire up PHP 8.3 Linux matrix jobs and Justfile targets. Fix three EXPECTF patterns in tests/002, 003, 008: PHP 8.3 changed the type-error message wording from "bool given" to "false given", so widen "boo%s" to "%s" to match both old and new phrasing. Co-Authored-By: Claude Sonnet 4.6 --- .github/docker-image-shas.yml | 6 ++++++ .github/scripts/update-docker-shas.sh | 3 +++ Justfile | 26 +++++++++++++++++++++++++- rar_error.c | 4 ++-- tests/002.phpt | 2 +- tests/003.phpt | 2 +- tests/008.phpt | 2 +- tests/038.phpt | 2 +- tests/086.phpt | 2 +- tests/087.phpt | 2 +- tests/088.phpt | 2 +- tests/089.phpt | 2 +- tests/093.phpt | 2 +- 13 files changed, 45 insertions(+), 12 deletions(-) diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml index d1bc3d2e..03ceaa36 100644 --- a/.github/docker-image-shas.yml +++ b/.github/docker-image-shas.yml @@ -19,3 +19,9 @@ datadog/dd-appsec-php-ci: php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8" php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5" php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c" + php-8.3-debug: "sha256:bb6df08160126374d3d9247428928aa19a9c2b2429c98356650199b85ae20212" + php-8.3-release-zts: "sha256:e58e25a017f75df82691d408b8cb70453875ff36718e295ee8c6653a0f117331" + php-8.4-debug: "sha256:15045688f6986f4625b1507a7f4be6104e7bbb88caf877f1611463b929f2bca2" + php-8.4-release-zts: "sha256:8e0ac25a3306b4b9f692c593b8a509cc789c2e001ce52682928065a92c880136" + php-8.5-debug: "sha256:bd0170331b34fb469e29d00b19b20fb88b726160f76df274a1bdc3a27ac18d30" + php-8.5-release-zts: "sha256:e071b2095da55bd24686209422f43a01c65acfc6021f04156d9fb43fd3d4d426" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh index 0daa583c..e99155ad 100755 --- a/.github/scripts/update-docker-shas.sh +++ b/.github/scripts/update-docker-shas.sh @@ -18,6 +18,9 @@ TAGS=( php-8.0-debug php-8.0-release-zts php-8.1-debug php-8.1-release-zts php-8.2-debug php-8.2-release-zts + php-8.3-debug php-8.3-release-zts + php-8.4-debug php-8.4-release-zts + php-8.5-debug php-8.5-release-zts ) get_index_digest() { diff --git a/Justfile b/Justfile index 1487da75..0985f4b2 100644 --- a/Justfile +++ b/Justfile @@ -23,6 +23,12 @@ image_8_1_debug := _base + `grep 'php-8.1-debug:' .github/docker-ima image_8_1_release_zts := _base + `grep 'php-8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_2_debug := _base + `grep 'php-8.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` image_8_2_release_zts := _base + `grep 'php-8.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_3_debug := _base + `grep 'php-8.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_3_release_zts := _base + `grep 'php-8.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_4_debug := _base + `grep 'php-8.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_4_release_zts := _base + `grep 'php-8.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_5_debug := _base + `grep 'php-8.5-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_5_release_zts := _base + `grep 'php-8.5-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` _run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root" @@ -73,6 +79,21 @@ test-8_2-debug: test-8_2-release-zts: {{_run}} {{image_8_2_release_zts}} .github/scripts/build-and-test.sh +test-8_3-debug: + {{_run}} {{image_8_3_debug}} .github/scripts/build-and-test.sh +test-8_3-release-zts: + {{_run}} {{image_8_3_release_zts}} .github/scripts/build-and-test.sh + +test-8_4-debug: + {{_run}} {{image_8_4_debug}} .github/scripts/build-and-test.sh +test-8_4-release-zts: + {{_run}} {{image_8_4_release_zts}} .github/scripts/build-and-test.sh + +test-8_5-debug: + {{_run}} {{image_8_5_debug}} .github/scripts/build-and-test.sh +test-8_5-release-zts: + {{_run}} {{image_8_5_release_zts}} .github/scripts/build-and-test.sh + # ── Per-version aggregates (sequential to avoid workspace conflicts) ─────────── test-7_0: test-7_0-debug test-7_0-release-zts @@ -83,7 +104,10 @@ test-7_4: test-7_4-debug test-7_4-release-zts test-8_0: test-8_0-debug test-8_0-release-zts test-8_1: test-8_1-debug test-8_1-release-zts test-8_2: test-8_2-debug test-8_2-release-zts +test-8_3: test-8_3-debug test-8_3-release-zts +test-8_4: test-8_4-debug test-8_4-release-zts +test-8_5: test-8_5-debug test-8_5-release-zts # ── All Linux targets ───────────────────────────────────────────────────────── -test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 test-8_2 +test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 test-8_2 test-8_3 test-8_4 test-8_5 diff --git a/rar_error.c b/rar_error.c index 13a742bc..70888582 100644 --- a/rar_error.c +++ b/rar_error.c @@ -246,8 +246,8 @@ void minit_rarerror(TSRMLS_D) /* {{{ */ rarexception_ce_ptr = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); #else - rarexception_ce_ptr = zend_register_internal_class_ex(&ce, - zend_exception_get_default(TSRMLS_C)); + /* zend_exception_get_default() was removed in PHP 8.5; use the global directly */ + rarexception_ce_ptr = zend_register_internal_class_ex(&ce, zend_ce_exception); #endif rarexception_ce_ptr->ce_flags |= ZEND_ACC_FINAL; zend_declare_property_bool(rarexception_ce_ptr, "usingExceptions", diff --git a/tests/002.phpt b/tests/002.phpt index 5c00904a..b4970a72 100644 --- a/tests/002.phpt +++ b/tests/002.phpt @@ -163,5 +163,5 @@ array(2) { Warning: rar_open(): Failed to open %s: ERAR_EOPEN (file open error) in %s on line %d -Warning: rar_list() expects parameter 1 to be RarArchive, boo%s given in %s on line %d +Warning: rar_list() expects parameter 1 to be RarArchive, %s given in %s on line %d Done diff --git a/tests/003.phpt b/tests/003.phpt index 6d614a36..89c871a2 100644 --- a/tests/003.phpt +++ b/tests/003.phpt @@ -91,5 +91,5 @@ object(RarEntry)#%d (%d) { Warning: rar_open(): Failed to open %s: ERAR_EOPEN (file open error) in %s on line %d -Warning: rar_entry_get() expects parameter 1 to be RarArchive, boo%s given in %s on line %d +Warning: rar_entry_get() expects parameter 1 to be RarArchive, %s given in %s on line %d Done diff --git a/tests/008.phpt b/tests/008.phpt index 1287f109..1b6c75cc 100644 --- a/tests/008.phpt +++ b/tests/008.phpt @@ -31,6 +31,6 @@ bool(false) Warning: rar_open(): Failed to open %s: ERAR_EOPEN (file open error) in %s on line %d -Warning: rar_entry_get() expects parameter 1 to be RarArchive, boo%s given in %s on line %d +Warning: rar_entry_get() expects parameter 1 to be RarArchive, %s given in %s on line %d Done diff --git a/tests/038.phpt b/tests/038.phpt index 7f06547f..5cf101a3 100644 --- a/tests/038.phpt +++ b/tests/038.phpt @@ -12,4 +12,4 @@ foreach ($rarF as $k => $rarE) { } echo "Done.\n"; --EXPECTF-- -Fatal error: main(): The archive is already closed, cannot give an iterator in %s on line %d +Fatal error: main(): The archive is already closed, cannot give an iterator in %s on line %d%A diff --git a/tests/086.phpt b/tests/086.phpt index 0c3b03b3..f5c5f2ef 100644 --- a/tests/086.phpt +++ b/tests/086.phpt @@ -16,4 +16,4 @@ $a[0] = "jjj"; echo "\n"; echo "Done.\n"; --EXPECTF-- -Fatal error: main(): A RarArchive object is not writable in %s on line %d +Fatal error: main(): A RarArchive object is not writable in %s on line %d%A diff --git a/tests/087.phpt b/tests/087.phpt index 3b2c48f8..3b9894eb 100644 --- a/tests/087.phpt +++ b/tests/087.phpt @@ -18,4 +18,4 @@ unset($a[0]["jj"]); echo "\n"; echo "Done.\n"; --EXPECTF-- -Fatal error: main(): A RarArchive object is not modifiable in %s on line %d +Fatal error: main(): A RarArchive object is not modifiable in %s on line %d%A diff --git a/tests/088.phpt b/tests/088.phpt index b637e09e..1101cd37 100644 --- a/tests/088.phpt +++ b/tests/088.phpt @@ -16,4 +16,4 @@ $a[0] = "hhh"; echo "\n"; echo "Done.\n"; --EXPECTF-- -Fatal error: main(): A RarArchive object is not writable in %s on line %d +Fatal error: main(): A RarArchive object is not writable in %s on line %d%A diff --git a/tests/089.phpt b/tests/089.phpt index 3f576153..562567c1 100644 --- a/tests/089.phpt +++ b/tests/089.phpt @@ -13,4 +13,4 @@ unset($a[0]); echo "\n"; echo "Done.\n"; --EXPECTF-- -Fatal error: main(): A RarArchive object is not writable in %s on line %d +Fatal error: main(): A RarArchive object is not writable in %s on line %d%A diff --git a/tests/093.phpt b/tests/093.phpt index e3b5da2c..a814a45f 100644 --- a/tests/093.phpt +++ b/tests/093.phpt @@ -15,4 +15,4 @@ foreach ($a as &$v) { echo "\n"; echo "Done.\n"; --EXPECTF-- -Fatal error: main(): An iterator cannot be used with foreach by reference in %s on line %d +Fatal error: main(): An iterator cannot be used with foreach by reference in %s on line %d%A From f6650b4abeed7fa71a9d8a97a2179b7c27df0d98 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 2 Mar 2026 01:41:57 +0000 Subject: [PATCH 46/76] Remove PHP 5 support --- php_compat.h | 18 ----- php_rar.h | 31 -------- rar.c | 69 ------------------ rar_error.c | 31 -------- rar_navigation.c | 10 --- rar_stream.c | 183 ++--------------------------------------------- rararch.c | 176 +-------------------------------------------- rarentry.c | 23 +----- tests/068.phpt | 15 ---- tests/069.phpt | 14 ---- tests/105.phpt | 16 ----- 11 files changed, 7 insertions(+), 579 deletions(-) delete mode 100644 tests/068.phpt delete mode 100644 tests/069.phpt delete mode 100644 tests/105.phpt diff --git a/php_compat.h b/php_compat.h index 51d1a581..961f932a 100644 --- a/php_compat.h +++ b/php_compat.h @@ -15,7 +15,6 @@ typedef zend_object handler_this_t; typedef zval handler_this_t; #endif -#if PHP_MAJOR_VERSION >= 7 typedef zend_object* rar_obj_ref; #define rar_zval_add_ref(ppzv) zval_add_ref(*ppzv) @@ -53,20 +52,3 @@ typedef size_t zpp_s_size_t; #define INIT_ZVAL(zv) ZVAL_UNDEF(&zv) #define ZEND_ACC_FINAL_CLASS ZEND_ACC_FINAL - -#else /* PHP 5.x */ -typedef zend_object_handle rar_obj_ref; - -#define rar_zval_add_ref zval_add_ref -#define ZVAL_ALLOC_DUP(dst, src) \ - do { \ - zval *z_src = src; \ - dst = z_src; \ - zval_add_ref(&dst); \ - SEPARATE_ZVAL(&dst); \ - } while (0) -#define RAR_ZVAL_STRING ZVAL_STRING -#define RAR_RETURN_STRINGL(s, l, duplicate) RETURN_STRINGL(s, l, duplicate) -typedef int zpp_s_size_t; -#define zend_hash_str_del zend_hash_del -#endif diff --git a/php_rar.h b/php_rar.h index 9021f266..b3705766 100644 --- a/php_rar.h +++ b/php_rar.h @@ -107,11 +107,7 @@ typedef struct rar { } rar_file_t; /* Misc */ -#if defined(ZTS) && PHP_MAJOR_VERSION < 7 -# define RAR_TSRMLS_TC , void *** -#else # define RAR_TSRMLS_TC -#endif #define RAR_RETNULL_ON_ARGS() \ if (zend_parse_parameters_none() == FAILURE) { \ @@ -158,33 +154,6 @@ ZEND_EXTERN_MODULE_GLOBALS(rar); # define RAR_G(v) (rar_globals.v) #endif -/* PHP 5.2 compatibility */ -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 -#define zend_parse_parameters_none() \ - zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") -#define Z_DELREF_P ZVAL_DELREF -# define STREAM_ASSUME_REALPATH 0 -# define ALLOC_PERMANENT_ZVAL(z) \ - (z) = (zval*) malloc(sizeof(zval)); -# define OPENBASEDIR_CHECKPATH(filename) \ - (PG(safe_mode) && \ - (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) \ - || php_check_open_basedir(filename TSRMLS_CC) -# undef ZEND_BEGIN_ARG_INFO_EX -# define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ - static const zend_arg_info name[] = { \ - { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, -#endif - -/* Other compatibility quirks */ -/* PHP 5.3 doesn't have ZVAL_COPY_VALUE */ -#if !defined(ZEND_COPY_VALUE) && PHP_MAJOR_VERSION == 5 -#define ZVAL_COPY_VALUE(z, v) \ - do { \ - (z)->value = (v)->value; \ - Z_TYPE_P(z) = Z_TYPE_P(v); \ - } while (0) -#endif #if !defined(HAVE_STRNLEN) || !HAVE_STRNLEN size_t _rar_strnlen(const char *s, size_t maxlen); diff --git a/rar.c b/rar.c index 3eb15085..0f211ce3 100644 --- a/rar.c +++ b/rar.c @@ -161,12 +161,8 @@ void _rar_destroy_userdata(rar_cb_user_data *udata) /* {{{ */ } if (udata->callable != NULL) { -#if PHP_MAJOR_VERSION < 7 - zval_ptr_dtor(&udata->callable); -#else zval_ptr_dtor(udata->callable); efree(udata->callable); -#endif } udata->password = NULL; @@ -447,50 +443,26 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, zend_fcall_info_cache *cache TSRMLS_DC) /* {{{ */ { -#if PHP_MAJOR_VERSION < 7 - zval *failed_vol, - *retval_ptr = NULL, - **params; -#else zval failed_vol, retval, *params, *const retval_ptr = &retval; -#endif int ret = -1; -#if PHP_MAJOR_VERSION < 7 - MAKE_STD_ZVAL(failed_vol); - RAR_ZVAL_STRING(failed_vol, dst_buffer, 1); - params = &failed_vol; - fci->retval_ptr_ptr = &retval_ptr; - fci->params = ¶ms; -#else ZVAL_STRING(&failed_vol, dst_buffer); ZVAL_NULL(&retval); params = &failed_vol; fci->retval = &retval; fci->params = params; -#endif fci->param_count = 1; -#if PHP_MAJOR_VERSION < 7 - if (zend_call_function(fci, cache TSRMLS_CC) != SUCCESS || - fci->retval_ptr_ptr == NULL || - *fci->retval_ptr_ptr == NULL) { -#else if (zend_call_function(fci, cache TSRMLS_CC) != SUCCESS || EG(exception)) { -#endif php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure to call volume find callback"); goto cleanup; } -#if PHP_MAJOR_VERSION < 7 - assert(*fci->retval_ptr_ptr == retval_ptr); -#else assert(fci->retval == &retval); -#endif if (Z_TYPE_P(retval_ptr) == IS_NULL) { /* let return -1 */ } @@ -529,15 +501,8 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, } cleanup: -#if PHP_MAJOR_VERSION < 7 - zval_ptr_dtor(&failed_vol); - if (retval_ptr != NULL) { - zval_ptr_dtor(&retval_ptr); - } -#else zval_ptr_dtor(&failed_vol); zval_ptr_dtor(&retval); -#endif return ret; } /* }}} */ @@ -553,17 +518,6 @@ static int _rar_make_userdata_fcall(zval *callable, *cache = empty_fcall_info_cache; -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2 - if (zend_fcall_info_init(callable, fci, cache TSRMLS_CC) != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "The RAR file was not opened in rar_open/RarArchive::open with a " - "valid callback.", error); - return FAILURE; - } - else { - return SUCCESS; - } -#else if (zend_fcall_info_init(callable, IS_CALLABLE_STRICT, fci, cache, NULL, &error TSRMLS_CC) == SUCCESS) { if (error) { @@ -583,7 +537,6 @@ static int _rar_make_userdata_fcall(zval *callable, } return FAILURE; } -#endif } /* }}} */ @@ -651,11 +604,7 @@ static zend_function_entry rar_functions[] = { /* {{{ Globals' related activities */ ZEND_DECLARE_MODULE_GLOBALS(rar); -#if PHP_MAJOR_VERSION < 7 -static int _rar_array_apply_remove_first(void *pDest TSRMLS_DC) -#else static int _rar_array_apply_remove_first(zval *pDest TSRMLS_DC) -#endif { return (ZEND_HASH_APPLY_STOP | ZEND_HASH_APPLY_REMOVE); } @@ -673,13 +622,7 @@ static void _rar_contents_cache_put(const char *key, assert(zend_hash_num_elements(cc->data) == cur_size - 1); } rar_zval_add_ref(&zv); -#if PHP_MAJOR_VERSION < 7 - assert(Z_REFCOUNT_P(zv) > 1); - SEPARATE_ZVAL(&zv); /* ensure we store a heap allocated copy */ - zend_hash_update(cc->data, key, key_len, &zv, sizeof(zv), NULL); -#else zend_hash_str_update(cc->data, key, key_len, zv); -#endif } static zval *_rar_contents_cache_get(const char *key, @@ -688,15 +631,7 @@ static zval *_rar_contents_cache_get(const char *key, { rar_contents_cache *cc = &RAR_G(contents_cache); zval *element = NULL; -#if PHP_MAJOR_VERSION < 7 - zval **element_p = NULL; - zend_hash_find(cc->data, key, key_len, (void **) &element_p); - if (element_p) { - element = *element_p; - } -#else element = zend_hash_str_find(cc->data, key, key_len); -#endif if (element != NULL) { cc->hits++; @@ -761,10 +696,6 @@ ZEND_MODULE_STARTUP_D(rar) REGISTER_LONG_CONSTANT("RAR_HOST_UNIX", HOST_UNIX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_MACOS", HOST_MACOS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_BEOS", HOST_BEOS, CONST_CS | CONST_PERSISTENT); - /* PHP < 5.3 doesn't have the PHP_MAXPATHLEN constant */ -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 - REGISTER_LONG_CONSTANT("RAR_MAXPATHLEN", MAXPATHLEN, CONST_CS | CONST_PERSISTENT); -#endif return SUCCESS; } /* }}} */ diff --git a/rar_error.c b/rar_error.c index 70888582..4e01705b 100644 --- a/rar_error.c +++ b/rar_error.c @@ -70,11 +70,7 @@ void _rar_handle_ext_error(const char *format TSRMLS_DC, ...) /* {{{ */ va_list arg; char *message; -#if defined(ZTS) && PHP_MAJOR_VERSION < 7 - va_start(arg, TSRMLS_C); -#else va_start(arg, format); -#endif vspprintf(&message, 0, format, arg); va_end(arg); @@ -91,13 +87,8 @@ int _rar_using_exceptions(TSRMLS_D) zval *pval; pval = zend_read_static_property(rarexception_ce_ptr, "usingExceptions", sizeof("usingExceptions") -1, (zend_bool) 1 TSRMLS_CC); -#if PHP_MAJOR_VERSION < 7 - assert(Z_TYPE_P(pval) == IS_BOOL); - return Z_BVAL_P(pval); -#else assert(Z_TYPE_P(pval) == IS_TRUE || Z_TYPE_P(pval) == IS_FALSE); return Z_TYPE_P(pval) == IS_TRUE; -#endif } /* returns a string or NULL if not an error */ @@ -186,39 +177,22 @@ PHP_METHOD(rarexception, setUsingExceptions) Return whether exceptions are being used */ PHP_METHOD(rarexception, isUsingExceptions) { -#if PHP_MAJOR_VERSION < 7 - zval **pval; -#else zval *pval; -#endif if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE ) { return; } /* or zend_read_static_property, which calls zend_std_get... after chg scope */ -#if PHP_VERSION_ID < 50399 - pval = zend_std_get_static_property(rarexception_ce_ptr, "usingExceptions", - sizeof("usingExceptions") -1, (zend_bool) 0 TSRMLS_CC); -#elif PHP_MAJOR_VERSION < 7 - pval = zend_std_get_static_property(rarexception_ce_ptr, "usingExceptions", - sizeof("usingExceptions") -1, (zend_bool) 0, NULL TSRMLS_CC); -#else zend_string *prop_name = zend_string_init("usingExceptions", sizeof("usingExceptions") - 1, 0); pval = zend_std_get_static_property(rarexception_ce_ptr, prop_name, (zend_bool) 0); zend_string_release(prop_name); -#endif /* property always exists */ assert(pval != NULL); -#if PHP_MAJOR_VERSION < 7 - assert(Z_TYPE_PP(pval) == IS_BOOL); - RETURN_ZVAL(*pval, 0, 0); -#else assert(Z_TYPE_P(pval) == IS_TRUE || Z_TYPE_P(pval) == IS_FALSE); RETURN_ZVAL(pval, 0, 0); -#endif } /* }}} */ @@ -242,13 +216,8 @@ void minit_rarerror(TSRMLS_D) /* {{{ */ zend_class_entry ce; INIT_CLASS_ENTRY(ce, "RarException", php_rarexception_class_functions); -#if PHP_MAJOR_VERSION < 7 - rarexception_ce_ptr = zend_register_internal_class_ex(&ce, - zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); -#else /* zend_exception_get_default() was removed in PHP 8.5; use the global directly */ rarexception_ce_ptr = zend_register_internal_class_ex(&ce, zend_ce_exception); -#endif rarexception_ce_ptr->ce_flags |= ZEND_ACC_FINAL; zend_declare_property_bool(rarexception_ce_ptr, "usingExceptions", sizeof("usingExceptions") -1, 0L /* FALSE */, diff --git a/rar_navigation.c b/rar_navigation.c index 59530e06..18df9dcc 100644 --- a/rar_navigation.c +++ b/rar_navigation.c @@ -73,9 +73,7 @@ static void _rar_nav_get_depth_and_length(wchar_t *filenamew, const size_t file_ int *depth_out, size_t *wlen_out TSRMLS_DC); static int _rar_nav_get_depth(const wchar_t *filenamew, const size_t file_size); static int _rar_nav_compare_entries(const void *op1, const void *op2 TSRMLS_DC); -#if PHP_MAJOR_VERSION >= 7 static void _rar_nav_swap_entries(void *op1, void *op2); -#endif static int _rar_nav_compare_entries_std(const void *op1, const void *op2); static inline int _rar_nav_compare_values(const wchar_t *str1, const int depth1, const wchar_t *str2, const int depth2, @@ -116,15 +114,9 @@ void _rar_entry_search_start(rar_file_t *rar, sizeof rar->entries->entries_array_s[0]); memcpy(rar->entries->entries_array_s, rar->entries->entries_array, rar->entries->num_entries * sizeof rar->entries->entries_array[0]); -#if PHP_MAJOR_VERSION < 7 - zend_qsort(rar->entries->entries_array_s, rar->entries->num_entries, - sizeof *rar->entries->entries_array_s, _rar_nav_compare_entries - TSRMLS_CC); -#else zend_qsort(rar->entries->entries_array_s, rar->entries->num_entries, sizeof *rar->entries->entries_array_s, _rar_nav_compare_entries, _rar_nav_swap_entries); -#endif } } /* }}} */ @@ -496,7 +488,6 @@ static int _rar_nav_compare_entries(const void *op1, const void *op2 TSRMLS_DC) } /* }}} */ -#if PHP_MAJOR_VERSION >= 7 static void _rar_nav_swap_entries(void *op1, void *op2) /* {{{ */ { /* just swaps two pointer values */ @@ -509,7 +500,6 @@ static void _rar_nav_swap_entries(void *op1, void *op2) /* {{{ */ } /* }}} */ -#endif static int _rar_nav_compare_entries_std(const void *op1, const void *op2) /* {{{ */ { diff --git a/rar_stream.c b/rar_stream.c index c6546a66..7294a26d 100644 --- a/rar_stream.c +++ b/rar_stream.c @@ -179,11 +179,7 @@ static ssize_t php_rar_ops_read(php_stream *stream, char *buf, size_t count) stream->eof = 1; } -#if PHP_VERSION_ID < 50400 - return n; -#else return (ssize_t) n; -#endif } /* }}} */ @@ -424,19 +420,10 @@ static ssize_t php_rar_dir_ops_read(php_stream *stream, char *buf, size_t count entry.d_name, sizeof entry.d_name); if (!self->no_encode) { /* urlencode entry */ -#if PHP_MAJOR_VERSION < 7 - int new_len; - char *encoded_name; - encoded_name = php_url_encode(entry.d_name, strlen(entry.d_name), - &new_len); - strlcpy(entry.d_name, encoded_name, sizeof entry.d_name); - efree(encoded_name); -#else zend_string *encoded_name = php_url_encode(entry.d_name, strlen(entry.d_name)); strlcpy(entry.d_name, encoded_name->val, sizeof entry.d_name); zend_string_release(encoded_name); -#endif } @@ -452,11 +439,7 @@ static int php_rar_dir_ops_close(php_stream *stream, int close_handle TSRMLS_DC) { STREAM_DIR_DATA_FROM_STREAM -#if PHP_MAJOR_VERSION < 7 - zval_dtor(&self->rar_obj); -#else zval_ptr_dtor(&self->rar_obj); -#endif efree(self->directory); efree(self->state); efree(self); @@ -587,67 +570,6 @@ php_stream *php_stream_rar_open(char *arc_name, /* {{{ Wrapper stuff */ -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 -/* PHP 5.2 has no zend_resolve_path. Adapted from 5.3's php_resolve_path */ -static char *zend_resolve_path(const char *filename, - int filename_length TSRMLS_DC) /* {{{ */ -{ - const char *path = PG(include_path); - char resolved_path[MAXPATHLEN]; - char trypath[MAXPATHLEN]; - const char *ptr, *end; - char *actual_path; - - if (filename == NULL || filename[0] == '\0') { - return NULL; - } - - /* do not use the include path in these circumstances */ - if ((*filename == '.' && (IS_SLASH(filename[1]) || - ((filename[1] == '.') && IS_SLASH(filename[2])))) || - IS_ABSOLUTE_PATH(filename, filename_length) || - path == NULL || path[0] == '\0') { - if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { - return estrdup(resolved_path); - } else { - return NULL; - } - } - - ptr = path; - while (ptr && *ptr) { - end = strchr(ptr, DEFAULT_DIR_SEPARATOR); - if (end) { - if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) { - ptr = end + 1; - continue; - } - memcpy(trypath, ptr, end-ptr); - trypath[end-ptr] = '/'; - memcpy(trypath+(end-ptr)+1, filename, filename_length+1); - ptr = end+1; - } else { - int len = strlen(ptr); - - if (len + 1 + filename_length + 1 >= MAXPATHLEN) { - break; - } - memcpy(trypath, ptr, len); - trypath[len] = '/'; - memcpy(trypath+len+1, filename, filename_length+1); - ptr = NULL; - } - actual_path = trypath; - if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { - return estrdup(resolved_path); - } - } /* end provided path */ - - return NULL; -} -/* }}} */ -#endif - /* {{{ php_rar_process_context */ /* memory is to be managed externally */ static void php_rar_process_context(php_stream_context *context, @@ -658,9 +580,6 @@ static void php_rar_process_context(php_stream_context *context, zval **volume_cb TSRMLS_DC) { zval *ctx_opt; -#if PHP_MAJOR_VERSION < 7 - zval **ctx_opt_p = NULL; -#endif assert(context != NULL); assert(open_password != NULL); @@ -670,14 +589,8 @@ static void php_rar_process_context(php_stream_context *context, /* TODO: don't know if I can log errors and not fail. check that */ -#if PHP_MAJOR_VERSION < 7 - if (php_stream_context_get_option( - context, "rar", "open_password", &ctx_opt_p) == SUCCESS) { - ctx_opt = *ctx_opt_p; -#else if ((ctx_opt = php_stream_context_get_option( context, "rar", "open_password"))) { -#endif if (Z_TYPE_P(ctx_opt) != IS_STRING) php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "RAR open password was provided, but not a string."); @@ -685,14 +598,8 @@ static void php_rar_process_context(php_stream_context *context, *open_password = Z_STRVAL_P(ctx_opt); } -#if PHP_MAJOR_VERSION < 7 - if (file_password != NULL && php_stream_context_get_option(context, "rar", - "file_password", &ctx_opt_p) == SUCCESS) { - ctx_opt = *ctx_opt_p; -#else if (file_password != NULL && (ctx_opt = php_stream_context_get_option( context, "rar", "file_password"))) { -#endif if (Z_TYPE_P(ctx_opt) != IS_STRING) php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "RAR file password was provided, but not a string."); @@ -700,19 +607,9 @@ static void php_rar_process_context(php_stream_context *context, *file_password = Z_STRVAL_P(ctx_opt); } -#if PHP_MAJOR_VERSION < 7 - if (php_stream_context_get_option(context, "rar", "volume_callback", - &ctx_opt_p) == SUCCESS) { - ctx_opt = *ctx_opt_p; -#else if ((ctx_opt = php_stream_context_get_option( context, "rar", "volume_callback"))) { -#endif -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2 - if (zend_is_callable(ctx_opt, IS_CALLABLE_STRICT, NULL)) { -#else if (zend_is_callable(ctx_opt, IS_CALLABLE_STRICT, NULL TSRMLS_CC)) { -#endif *volume_cb = ctx_opt; } else @@ -781,23 +678,19 @@ static int _rar_get_archive_and_fragment(php_stream_wrapper *wrapper, if (!(options & STREAM_ASSUME_REALPATH)) { if (options & USE_PATH) { -#if PHP_MAJOR_VERSION < 7 - *archive = zend_resolve_path(tmp_archive, tmp_arch_len TSRMLS_CC); -#else -# if PHP_VERSION_ID < 80100 +#if PHP_VERSION_ID < 80100 zend_string *arc_str = zend_resolve_path(tmp_archive, tmp_arch_len); -# else +#else zend_string *tmp_archive_str = zend_string_init_fast(tmp_archive, tmp_arch_len); zend_string *arc_str = zend_resolve_path(tmp_archive_str); zend_string_free(tmp_archive_str); -# endif +#endif if (arc_str != NULL) { *archive = estrndup(arc_str->val, arc_str->len); } else { *archive = NULL; } zend_string_release(arc_str); -#endif } if (*archive == NULL) { if ((*archive = expand_filepath(tmp_archive, NULL TSRMLS_CC)) @@ -865,17 +758,10 @@ static int _rar_get_archive_and_fragment(php_stream_wrapper *wrapper, /* {{{ php_stream_rar_opener */ static php_stream *php_stream_rar_opener(php_stream_wrapper *wrapper, -#if PHP_MAJOR_VERSION < 7 - char *filename, - char *mode, - int options, - char **opened_path, -#else const char *filename, const char *mode, int options, zend_string **opened_path, -#endif php_stream_context *context STREAMS_DC TSRMLS_DC) { @@ -987,12 +873,8 @@ static php_stream *php_stream_rar_opener(php_stream_wrapper *wrapper, if (tmp_open_path != NULL) { if (opened_path != NULL) { -#if PHP_MAJOR_VERSION < 7 - *opened_path = tmp_open_path; -#else *opened_path = zend_string_init(tmp_open_path, strlen(tmp_open_path), 0); -#endif } else { efree(tmp_open_path); } @@ -1048,11 +930,7 @@ static int _rar_get_cachable_rararch(php_stream_wrapper *wrapper, zval *cache_zv; assert(rar_obj != NULL); -#if PHP_MAJOR_VERSION < 7 - INIT_ZVAL(*rar_obj); -#else ZVAL_UNDEF(rar_obj); -#endif _rar_arch_cache_get_key(arch_path, open_passwd, volume_cb, &cache_key, &cache_key_len); @@ -1115,56 +993,26 @@ static int _rar_get_cachable_rararch(php_stream_wrapper *wrapper, efree(cache_key); if (ret != SUCCESS && Z_TYPE_P(rar_obj) == IS_OBJECT) { -#if PHP_MAJOR_VERSION < 7 - zval_dtor(rar_obj); - Z_TYPE_P(rar_obj) = IS_NULL; -#else zval_ptr_dtor(rar_obj); ZVAL_UNDEF(rar_obj); -#endif } return ret; } /* }}} */ -/* {{{ _rar_stream_tidy_wrapper_error_log - * These two different versions are because of PHP commit 7166298 */ -#if PHP_VERSION_ID <= 50310 || PHP_VERSION_ID == 50400 -/* copied from main/streams/streams.c because it's an internal function */ -static void _rar_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) -{ - if (wrapper) { - /* tidy up the error stack */ - int i; - - for (i = 0; i < wrapper->err_count; i++) { - efree(wrapper->err_stack[i]); - } - if (wrapper->err_stack) { - efree(wrapper->err_stack); - } - wrapper->err_stack = NULL; - wrapper->err_count = 0; - } -} -#else +/* {{{ _rar_stream_tidy_wrapper_error_log */ static void _rar_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) { if (wrapper && FG(wrapper_errors)) { zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof wrapper); } } -#endif /* }}} */ /* {{{ php_stream_rar_stater */ static int php_stream_rar_stater(php_stream_wrapper *wrapper, -#if PHP_MAJOR_VERSION < 7 - char *url, -#else const char *url, -#endif int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) @@ -1183,11 +1031,7 @@ static int php_stream_rar_stater(php_stream_wrapper *wrapper, int ret = FAILURE; /* {{{ preliminaries */ -#if PHP_MAJOR_VERSION < 7 - Z_TYPE(rararch) = IS_NULL; -#else ZVAL_UNDEF(&rararch); -#endif if (_rar_get_archive_and_fragment(wrapper, url, options, 1, &open_path, &fragment, NULL TSRMLS_CC) == FAILURE) { @@ -1238,11 +1082,7 @@ static int php_stream_rar_stater(php_stream_wrapper *wrapper, } if (Z_TYPE(rararch) == IS_OBJECT) { -#if PHP_MAJOR_VERSION < 7 - zval_dtor(&rararch); -#else zval_ptr_dtor(&rararch); -#endif } if (state != NULL) { _rar_entry_search_end(state); @@ -1265,17 +1105,10 @@ static int php_stream_rar_stater(php_stream_wrapper *wrapper, /* {{{ php_stream_rar_dir_opener */ static php_stream *php_stream_rar_dir_opener(php_stream_wrapper *wrapper, -#if PHP_MAJOR_VERSION < 7 - char *filename, - char *mode, - int options, - char **opened_path, -#else const char *filename, const char *mode, int options, zend_string **opened_path, -#endif php_stream_context *context STREAMS_DC TSRMLS_DC) { @@ -1374,12 +1207,8 @@ static php_stream *php_stream_rar_dir_opener(php_stream_wrapper *wrapper, if (tmp_open_path != NULL) { if (opened_path != NULL) { -#if PHP_MAJOR_VERSION < 7 - *opened_path = tmp_open_path; -#else *opened_path = zend_string_init(tmp_open_path, strlen(tmp_open_path), 0); -#endif } else { efree(tmp_open_path); } @@ -1390,11 +1219,7 @@ static php_stream *php_stream_rar_dir_opener(php_stream_wrapper *wrapper, if (stream == NULL) { /* failed */ if (self != NULL) { if (Z_TYPE(self->rar_obj) == IS_OBJECT) { -#if PHP_MAJOR_VERSION < 7 - zval_dtor(&self->rar_obj); -#else zval_ptr_dtor(&self->rar_obj); -#endif } if (self->directory != NULL) { efree(self->directory); diff --git a/rararch.c b/rararch.c index f30a370a..a85209b6 100644 --- a/rararch.c +++ b/rararch.c @@ -46,23 +46,14 @@ extern "C" { /* {{{ Type definitions reserved for this translation unit */ typedef struct _ze_rararch_object { -#if PHP_MAJOR_VERSION < 7 - zend_object parent; - rar_file_t *rar_file; -#else rar_file_t *rar_file; zend_object parent; -#endif } ze_rararch_object; typedef struct _rararch_iterator { zend_object_iterator parent; rar_find_output *state; -#if PHP_MAJOR_VERSION < 7 - zval *value; -#else zval value; -#endif int empty_iterator; /* iterator should give nothing */ } rararch_iterator; /* }}} */ @@ -88,30 +79,19 @@ static zend_object_handlers rararch_object_handlers; /* {{{ Function prototypes for functions with internal linkage */ static inline rar_obj_ref rar_obj_ref_fetch(zval *zv); static inline void rar_obj_ref_make_zv(rar_obj_ref zo, zval *zv TSRMLS_DC); -#if PHP_MAJOR_VERSION >= 7 static inline ze_rararch_object *rararch_object_fetch(zend_object *zobj); static ze_rararch_object *rararch_object_from_zv(const zval *zv); static ze_rararch_object *rararch_object_from_ref(const rar_obj_ref ref); static zend_object *rararch_ce_create_object(zend_class_entry *ce); static void rararch_ce_free_object_storage(zend_object *zobj); -#else -#define rararch_object_from_zv zend_object_store_get_object -#define rararch_object_from_ref(ref) zend_object_store_get_object_by_handle((ref) TSRMLS_CC) -static zend_object_value rararch_ce_create_object(zend_class_entry *class_type TSRMLS_DC); -static void rararch_ce_free_object_storage(ze_rararch_object *object TSRMLS_DC); -#endif /* }}} */ /* {{{ RarArchive handlers */ static int rararch_handlers_preamble(handler_this_t *object, rar_file_t **rar TSRMLS_DC); static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, zend_long *index, int quiet TSRMLS_DC); static int rararch_count_elements(handler_this_t *object, zend_long *count TSRMLS_DC); -#if PHP_MAJOR_VERSION < 7 -static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); -#else static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int type, zval *rv); -#endif static void rararch_write_dimension(handler_this_t *object, zval *offset, zval *value TSRMLS_DC); static int rararch_has_dimension(handler_this_t *object, zval *offset, int check_empty TSRMLS_DC); /* }}} */ @@ -258,25 +238,17 @@ static void _rar_raw_entries_to_array(rar_file_t *rar, zval *target TSRMLS_DC) / state->position, entry_obj TSRMLS_CC); add_next_index_zval(target, entry_obj); -#if PHP_MAJOR_VERSION >= 7 /* PHP 7 copies the zval (but without increasing the refcount of the - * obj), while 5.x simply copies the pointer. Only for PHP 5.x do we - * keep the allocation) */ + * obj). Free the allocation. */ efree(entry_obj); -#endif } } while (state->eof == 0); _rar_entry_search_end(state); -#if PHP_MAJOR_VERSION < 7 - zval_dtor(&rararch_obj); -#else zval_ptr_dtor(&rararch_obj); -#endif } /* }}} */ -#if PHP_MAJOR_VERSION >=7 static inline rar_obj_ref rar_obj_ref_fetch(zval *zv) { return Z_OBJ(*zv); @@ -286,24 +258,7 @@ static inline void rar_obj_ref_make_zv(rar_obj_ref zo, zval *zv TSRMLS_DC) ZVAL_OBJ(zv, zo); zval_addref_p(zv); } -#else -inline rar_obj_ref rar_obj_ref_fetch(zval *zv) -{ - return Z_OBJ_HANDLE_P(zv); -} -inline void rar_obj_ref_make_zv(rar_obj_ref zoh, zval *zv TSRMLS_DC) -{ - INIT_ZVAL(*zv); - Z_TYPE_P(zv) = IS_OBJECT; - Z_OBJ_HANDLE_P(zv) = zoh; - Z_OBJ_HT_P(zv) = &rararch_object_handlers; - /* object has a new reference; if not incremented, the object would be - * be destroyed when this new zval we created was destroyed */ - zend_objects_store_add_ref_by_handle(zoh TSRMLS_CC); -} -#endif -#if PHP_MAJOR_VERSION >=7 static inline ze_rararch_object *rararch_object_fetch(zend_object *zobj) { return (ze_rararch_object *) @@ -317,35 +272,8 @@ static ze_rararch_object *rararch_object_from_ref(const rar_obj_ref ref) { return rararch_object_fetch(ref); } -#endif /* {{{ */ -#if PHP_MAJOR_VERSION < 7 -static zend_object_value rararch_ce_create_object(zend_class_entry *class_type TSRMLS_DC) -{ - zend_object_value zov; - ze_rararch_object *zobj; - - zobj = emalloc(sizeof *zobj); - /* rararch_ce_free_object_storage will attempt to access it otherwise */ - zobj->rar_file = NULL; - zend_object_std_init((zend_object*) zobj, class_type TSRMLS_CC); - -#if PHP_VERSION_ID < 50399 - zend_hash_copy(((zend_object*)zobj)->properties, - &(class_type->default_properties), - (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*)); -#else - object_properties_init((zend_object*)zobj, class_type); -#endif - zov.handle = zend_objects_store_put(zobj, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - (zend_objects_free_object_storage_t) rararch_ce_free_object_storage, - NULL TSRMLS_CC); - zov.handlers = &rararch_object_handlers; - return zov; -} -#else static zend_object *rararch_ce_create_object(zend_class_entry *ce) { ze_rararch_object *zobj = @@ -357,18 +285,12 @@ static zend_object *rararch_ce_create_object(zend_class_entry *ce) return &zobj->parent; } -#endif /* }}} */ /* {{{ */ -#if PHP_MAJOR_VERSION < 7 -static void rararch_ce_free_object_storage(ze_rararch_object *object TSRMLS_DC) -{ -#else static void rararch_ce_free_object_storage(zend_object *zobj) { ze_rararch_object *object = rararch_object_fetch(zobj); -#endif rar_file_t *rar = object->rar_file; /* may be NULL if the user did new RarArchive() */ @@ -392,9 +314,6 @@ static void rararch_ce_free_object_storage(zend_object *zobj) /* could call zend_objects_free_object_storage here (not before!), but * instead I'll mimic its behaviour */ zend_object_std_dtor(&object->parent TSRMLS_CC); -#if PHP_MAJOR_VERSION < 7 - efree(object); -#endif } /* }}} */ @@ -467,13 +386,9 @@ static int rararch_dimensions_preamble(rar_file_t *rar, if (Z_OBJ_HT_P(offset)->get) { zval *newoffset = NULL; int ret; -# if PHP_MAJOR_VERSION < 7 - newoffset = Z_OBJ_HT_P(offset)->get(offset TSRMLS_CC); -# else zval zv_holder; ZVAL_NULL(&zv_holder); newoffset = Z_OBJ_HT_P(offset)->get(offset, &zv_holder); -# endif /* get handler cannot return NULL */ assert(newoffset != NULL); @@ -486,11 +401,7 @@ static int rararch_dimensions_preamble(rar_file_t *rar, ret = rararch_dimensions_preamble(rar, newoffset, index, quiet TSRMLS_CC); -# if PHP_MAJOR_VERSION < 7 - zval_ptr_dtor(&newoffset); -# else zval_ptr_dtor(newoffset); -# endif return ret; } else #endif // PHP < 8 @@ -565,11 +476,7 @@ static int rararch_count_elements(handler_this_t *object, zend_long *count TSRML /* }}} */ /* {{{ RarArchive read_dimension handler */ -#if PHP_MAJOR_VERSION < 7 -static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) -#else static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int type, zval *rv) -#endif { zend_long index; rar_file_t *rar = NULL; @@ -592,11 +499,7 @@ static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int ty _rar_entry_search_seek(out, (size_t) index); _rar_entry_search_advance(out, NULL, 0, 0); assert(out->found); -#if PHP_MAJOR_VERSION < 7 - ALLOC_INIT_ZVAL(ret); -#else ret = rv; -#endif #if PHP_MAJOR_VERSION >= 8 zval object_zv; ZVAL_OBJ(&object_zv, object); @@ -608,9 +511,6 @@ static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int ty ret TSRMLS_CC); #endif _rar_entry_search_end(out); -#if PHP_MAJOR_VERSION < 7 - Z_DELREF_P(ret); /* set refcount to 0 */ -#endif return ret; } /* }}} */ @@ -681,11 +581,7 @@ PHP_FUNCTION(rar_open) assert(strnlen(resolved_path, MAXPATHLEN) < MAXPATHLEN); if (callable != NULL) { /* given volume resolver callback */ -#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2 - if (!zend_is_callable(callable, IS_CALLABLE_STRICT, NULL)) { -#else if (!zend_is_callable(callable, IS_CALLABLE_STRICT, NULL TSRMLS_CC)) { -#endif _rar_handle_ext_error("%s" TSRMLS_CC, "Expected the third " "argument, if provided, to be a valid callback"); RETURN_FALSE; @@ -1009,12 +905,7 @@ static zend_object_iterator *rararch_it_get_iterator(zend_class_entry *ce, static void rararch_it_dtor(zend_object_iterator *iter TSRMLS_DC); static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC); static int rararch_it_valid(zend_object_iterator *iter TSRMLS_DC); -#if PHP_MAJOR_VERSION < 7 -static void rararch_it_current_data(zend_object_iterator *iter, - zval ***data TSRMLS_DC); -#else static zval *rararch_it_current_data(zend_object_iterator *iter); -#endif static void rararch_it_move_forward(zend_object_iterator *iter TSRMLS_DC); static void rararch_it_rewind(zend_object_iterator *iter TSRMLS_DC); /* }}} */ @@ -1023,15 +914,8 @@ static void rararch_it_rewind(zend_object_iterator *iter TSRMLS_DC); static void rararch_it_invalidate_current(zend_object_iterator *iter TSRMLS_DC) { rararch_iterator *it = (rararch_iterator *) iter; -#if PHP_MAJOR_VERSION < 7 - if (it->value != NULL) { - zval_ptr_dtor(&it->value); - it->value = NULL; - } -#else zval_ptr_dtor(&it->value); ZVAL_UNDEF(&it->value); -#endif } /* }}} */ @@ -1042,16 +926,9 @@ static void rararch_it_dtor(zend_object_iterator *iter TSRMLS_DC) rararch_it_invalidate_current((zend_object_iterator *) it TSRMLS_CC); -#if PHP_MAJOR_VERSION < 7 - zval_ptr_dtor((zval**) &it->parent.data); /* decrease refcount on zval object */ -#else zval_ptr_dtor(&it->parent.data); -#endif _rar_entry_search_end(it->state); -#if PHP_MAJOR_VERSION < 7 - efree(it); -#endif } /* }}} */ @@ -1062,27 +939,14 @@ static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC) int res; zval *robj; -#if PHP_MAJOR_VERSION < 7 - assert(it->value == NULL); -#else assert(Z_TYPE(it->value) == IS_UNDEF); -#endif if (it->empty_iterator) { -#if PHP_MAJOR_VERSION < 7 - MAKE_STD_ZVAL(it->value); - ZVAL_FALSE(it->value); -#else ZVAL_FALSE(&it->value); -#endif return; } -#if PHP_MAJOR_VERSION < 7 - robj = it->parent.data; -#else robj = &it->parent.data; -#endif res = _rar_get_file_resource_zv_ex(robj, &rar_file, 1 TSRMLS_CC); if (res == FAILURE) @@ -1090,50 +954,25 @@ static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC) "Cannot fetch RarArchive object"); _rar_entry_search_advance(it->state, NULL, 0, 0); -#if PHP_MAJOR_VERSION < 7 - MAKE_STD_ZVAL(it->value); - if (it->state->found) - _rar_entry_to_zval(robj, it->state->header, it->state->packed_size, - it->state->position, it->value TSRMLS_CC); - else { - ZVAL_FALSE(it->value); - } -#else if (it->state->found) _rar_entry_to_zval(&it->parent.data, it->state->header, it->state->packed_size, it->state->position, &it->value TSRMLS_CC); else { ZVAL_FALSE(&it->value); } -#endif } /* }}} */ /* {{{ rararch_it_valid */ static int rararch_it_valid(zend_object_iterator *iter TSRMLS_DC) { -#if PHP_MAJOR_VERSION < 7 - zval *value = ((rararch_iterator *) iter)->value; - assert(value != NULL); - return (Z_TYPE_P(value) != IS_BOOL)?SUCCESS:FAILURE; -#else zval *value = &((rararch_iterator *) iter)->value; assert(Z_TYPE_P(value) != IS_UNDEF); return Z_TYPE_P(value) != IS_FALSE ? SUCCESS : FAILURE; -#endif } /* }}} */ /* {{{ rararch_it_current_data */ -#if PHP_MAJOR_VERSION < 7 -static void rararch_it_current_data(zend_object_iterator *iter, - zval ***data TSRMLS_DC) -{ - zval **value = &(((rararch_iterator *) iter)->value); - assert(*value != NULL); - *data = value; -} -#else static zval *rararch_it_current_data(zend_object_iterator *iter) { zval *ret; @@ -1141,7 +980,6 @@ static zval *rararch_it_current_data(zend_object_iterator *iter) assert(Z_TYPE_P(ret) != IS_UNDEF); return ret; } -#endif /* }}} */ /* {{{ rararch_it_move_forward */ @@ -1149,11 +987,7 @@ static void rararch_it_move_forward(zend_object_iterator *iter TSRMLS_DC) { rararch_iterator *it = (rararch_iterator *) iter; rararch_it_invalidate_current((zend_object_iterator *) it TSRMLS_CC); -#if PHP_MAJOR_VERSION < 7 - it->value = NULL; -#else ZVAL_UNDEF(&it->value); -#endif rararch_it_fetch(it TSRMLS_CC); } /* }}} */ @@ -1205,15 +1039,9 @@ static zend_object_iterator *rararch_it_get_iterator(zend_class_entry *ce, rararch_iterator *it = emalloc(sizeof *it); -#if PHP_MAJOR_VERSION < 7 - zval_add_ref(&object); - it->parent.data = object; - it->value = NULL; -#else zend_iterator_init((zend_object_iterator *) it); ZVAL_COPY(&it->parent.data, object); ZVAL_UNDEF(&it->value); -#endif #if PHP_VERSION_ID < 70300 it->parent.funcs = ce->iterator_funcs.funcs; @@ -1247,10 +1075,8 @@ void minit_rararch(TSRMLS_D) rararch_object_handlers.has_dimension = rararch_has_dimension; rararch_object_handlers.unset_dimension = rararch_unset_dimension; rararch_object_handlers.clone_obj = NULL; -#if PHP_MAJOR_VERSION >= 7 rararch_object_handlers.free_obj = rararch_ce_free_object_storage; rararch_object_handlers.offset = XtOffsetOf(ze_rararch_object, parent); -#endif INIT_CLASS_ENTRY(ce, "RarArchive", php_rararch_class_functions); rararch_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC); diff --git a/rarentry.c b/rarentry.c index 112b567b..2cbc4065 100644 --- a/rarentry.c +++ b/rarentry.c @@ -51,7 +51,7 @@ static void _rar_dos_date_to_text(unsigned dos_time, char *date_string); /* {{{ Functions with external linkage */ /* should be passed the last entry that corresponds to a given file * only that one has the correct CRC. Still, it may have a wrong packedSize */ -/* parent is zval to RarArchive object. The object (not the zval, in PHP 5.x) +/* parent is zval to RarArchive object. The object * will have its refcount increased */ void _rar_entry_to_zval(zval *parent, struct RARHeaderDataEx *entry, @@ -67,13 +67,6 @@ void _rar_entry_to_zval(zval *parent, filename_len; zend_long unp_size; /* zend_long is the portable PHP integer type (always 64-bit in PHP 7+) */ zval *parent_copy = parent; -#if PHP_MAJOR_VERSION < 7 - /* allocate zval on the heap */ - zval_addref_p(parent_copy); - SEPARATE_ZVAL(&parent_copy); - /* set refcount to 0; zend_update_property will increase it */ - Z_DELREF_P(parent_copy); -#endif object_init_ex(object, rar_class_entry_ptr); #if PHP_MAJOR_VERSION >= 8 @@ -181,13 +174,6 @@ static int _rar_decl_priv_prop_null(zend_class_entry *ce, const char *name, int name_length, char *doc_comment, int doc_comment_len TSRMLS_DC) /* {{{ */ { -#if PHP_MAJOR_VERSION < 7 - zval *property; - ALLOC_PERMANENT_ZVAL(property); - INIT_ZVAL(*property); - return zend_declare_property_ex(ce, name, name_length, property, - ZEND_ACC_PRIVATE, doc_comment, doc_comment_len TSRMLS_CC); -#else zval property; zend_string *name_str, *doc_str; @@ -207,16 +193,13 @@ static int _rar_decl_priv_prop_null(zend_class_entry *ce, const char *name, zend_string_release(name_str); zend_string_release(doc_str); return ret; -#endif } /* }}} */ static zval *_rar_entry_get_property(zval *entry_obj, char *name, int namelen TSRMLS_DC) /* {{{ */ { zval *tmp; -#if PHP_MAJOR_VERSION >= 7 zval zv; -#endif #if PHP_VERSION_ID < 70100 zend_class_entry *orig_scope = EG(scope); @@ -225,10 +208,8 @@ static zval *_rar_entry_get_property(zval *entry_obj, char *name, int namelen TS #if PHP_MAJOR_VERSION >= 8 tmp = zend_read_property(Z_OBJCE_P(entry_obj), Z_OBJ_P(entry_obj), name, namelen, 1, &zv); -#elif PHP_MAJOR_VERSION >= 7 - tmp = zend_read_property(Z_OBJCE_P(entry_obj), entry_obj, name, namelen, 1, &zv); #else - tmp = zend_read_property(Z_OBJCE_P(entry_obj), entry_obj, name, namelen, 1 TSRMLS_CC); + tmp = zend_read_property(Z_OBJCE_P(entry_obj), entry_obj, name, namelen, 1, &zv); #endif if (tmp == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, diff --git a/tests/068.phpt b/tests/068.phpt deleted file mode 100644 index a945ad5d..00000000 --- a/tests/068.phpt +++ /dev/null @@ -1,15 +0,0 @@ ---TEST-- -RarArchive direct instantiation does not crash (PHP 5.x) ---SKIPIF-- -= 70000) die("skip for PHP 5.x"); -if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do not use with valgrind in PHP <7'); ---FILE-- -= 70000) die("skip for PHP 5.x"); -if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do not use with valgrind in PHP <7'); ---FILE-- -= 70000) die("skip for PHP 5.x"); ---FILE-- -getEntries(); -echo "Never reached.\n"; -?> ---EXPECTF-- -Fatal error: Trying to clone an uncloneable object of class RarArchive in %s on line %d From 5e74ad98034e9aad3b3f65a164b162d6817e9975 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:21:45 +0000 Subject: [PATCH 47/76] Update unrar_update.md --- unrar_update.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/unrar_update.md b/unrar_update.md index dd7857f5..51c9573b 100644 --- a/unrar_update.md +++ b/unrar_update.md @@ -1,5 +1,7 @@ This file contains the procedure to update to a new version of unrar. +## Obtaining new versions of unrar + There is a separate branch, `unrar` with the unaltered source code of unrar. Each commit is a new version of `unrar`. They are committed sequentially by version number, as `unrar` doesn't generally have minor updates for older @@ -23,4 +25,25 @@ To update to new versions of unrar, follow this procedure: 5. Commit with the correct message. 6. If you handled the latest version, you are done. Otherwise, go to step 3. - +## Updating the extension + +After the unrar branch has the latest version of unrar, it's time to merge the +unrar branch. + +1. First, use `git merge-base HEAD unrar` to determine the last unrar version + that was merged. +2. Determine how the unrar extension was modified in the `master` branch. + Compare the contents of the `unrar` subdirectory in `master` branch to the + original content in the `unrar` branch. This will inform your conflict + resolution afterwards. Note in a file what these changes are, so you can + refer to them later. +3. Upgrade to next minor (not patch, not major) version that hasn't been merged + yet. +4. Resolve any conflicts that may have arisen, preserving the functionality that + was added to the unrar library and the associated extension functionality. +5. Test. Inspect the `Justfile`. Run the tests for 7.0 and the latest supported + PHP version (debug and release-zts). +7. Continue with the next minor version on step 3, until we're synced with the + `unrar` branch. + + From ae9a4623fe9dbd8c1f54337e773add76cb22c4ac Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:32:39 +0000 Subject: [PATCH 48/76] Fix isnt.cpp: wrap Windows-only code in #ifdef _WIN_ALL isnt.cpp was added in unrar 6.1.5 without platform guards, causing compilation failure on Linux due to Windows-only headers (comdef.h, Wbemidl.h). Wrap entire implementation in #ifdef _WIN_ALL to match the original pattern and rar.hpp include guard. Co-Authored-By: Claude Sonnet 4.6 --- unrar/isnt.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/unrar/isnt.cpp b/unrar/isnt.cpp index f85472c3..fbf7cd9f 100644 --- a/unrar/isnt.cpp +++ b/unrar/isnt.cpp @@ -1,5 +1,6 @@ #include "rar.hpp" +#ifdef _WIN_ALL DWORD WinNT() { static int dwPlatformId=-1; @@ -34,17 +35,17 @@ static bool WMI_IsWindows10() HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER, IID_IWbemLocator,(LPVOID *)&pLoc); - + if (FAILED(hres)) return false; IWbemServices *pSvc = NULL; - + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc); - + if (FAILED(hres)) { - pLoc->Release(); + pLoc->Release(); return false; } @@ -54,14 +55,14 @@ static bool WMI_IsWindows10() if (FAILED(hres)) { pSvc->Release(); - pLoc->Release(); + pLoc->Release(); return false; } IEnumWbemClassObject *pEnumerator = NULL; hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); - + if (FAILED(hres)) { pSvc->Release(); @@ -71,7 +72,7 @@ static bool WMI_IsWindows10() IWbemClassObject *pclsObj = NULL; ULONG uReturn = 0; - + bool Win10=false; while (pEnumerator!=NULL) { @@ -106,9 +107,10 @@ bool IsWindows11OrGreater() OSVERSIONINFO WinVer; WinVer.dwOSVersionInfoSize=sizeof(WinVer); GetVersionEx(&WinVer); - IsWin11=WinVer.dwMajorVersion>10 || + IsWin11=WinVer.dwMajorVersion>10 || WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); IsSet=true; } return IsWin11; } +#endif From 0cace1db4b2a102284a2279f7e2841f956077e27 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:37:38 +0000 Subject: [PATCH 49/76] Fix extract.cpp: fix brace mismatch in password check block after 6.2.9 merge The conflict resolution accidentally closed the while(true) password loop prematurely. Fix the brace structure so our custom empty-password check sits inside the loop before the SetEncryption/PswCheck code. Co-Authored-By: Claude Sonnet 4.6 --- unrar/extract.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/unrar/extract.cpp b/unrar/extract.cpp index 03519c6a..f904c811 100644 --- a/unrar/extract.cpp +++ b/unrar/extract.cpp @@ -599,11 +599,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) // and cancelled passwords differently sometimes. if (!Cmd->Password.IsSet()) { - ErrHandler.SetErrorCode(RARX_WARNING); - Cmd->DllError=ERAR_MISSING_PASSWORD; - ExtrFile=false; - } - } + ErrHandler.SetErrorCode(RARX_WARNING); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ExtrFile=false; + } // Set a password before creating the file, so we can skip creating // in case of wrong password. From 3f536c9f9d658c67d043349377a6e1b7e809b439 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:42:32 +0000 Subject: [PATCH 50/76] Fix os.hpp: restore NM define removed in unrar 7.0 unrar 7.0 removed the NM path-size define from os.hpp, but the PHP extension code in rar.c still uses it. Add NM back as a compatibility define with the same value (2048). Co-Authored-By: Claude Sonnet 4.6 --- unrar/os.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unrar/os.hpp b/unrar/os.hpp index 7e5331e4..21c937a4 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -267,4 +267,9 @@ #define ALLOW_MISALIGNED #endif +// NM was removed from upstream unrar but is still used by the PHP extension. +#ifndef NM +#define NM 2048 +#endif + #endif // _RAR_OS_ From 371536d3a895325cd780c75f32c28bc24f347ec5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:43:06 +0000 Subject: [PATCH 51/76] Fix arccmt.cpp: remove extra brace introduced during 6.2.9 conflict resolution Co-Authored-By: Claude Sonnet 4.6 --- unrar/arccmt.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/unrar/arccmt.cpp b/unrar/arccmt.cpp index 12b875ad..926dcd38 100644 --- a/unrar/arccmt.cpp +++ b/unrar/arccmt.cpp @@ -106,7 +106,6 @@ bool Archive::DoGetComment(std::wstring &CmtData) } } } - } else { if (CmtLength==0) From 2d3b968d2386a15a340047b54f505ed75b5d2e7d Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:44:13 +0000 Subject: [PATCH 52/76] Update extractchunk.cpp for unrar 7.0 std::wstring API changes unrar 7.0 converted all path/name variables from wchar arrays to std::wstring. Update extractchunk.cpp to use the new API: - ArcName, DestFileName, DllDestName are now std::wstring - VolNameToFirstName and ConvertPath have new signatures Co-Authored-By: Claude Sonnet 4.6 --- unrar/extractchunk.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/unrar/extractchunk.cpp b/unrar/extractchunk.cpp index bbc00e58..1e6d31a5 100644 --- a/unrar/extractchunk.cpp +++ b/unrar/extractchunk.cpp @@ -40,22 +40,21 @@ bool CmdExtract::ExtractCurrentFileChunkInit(Archive &Arc, if (Arc.FileHead.SplitBefore && FirstFile) { - wchar CurVolName[NM]; - wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); - VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering); + std::wstring CurVolName=ArcName; + VolNameToFirstName(ArcName,ArcName,Arc.NewNumbering); if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) { // If first volume name does not match the current name and if such // volume name really exists, let's unpack from this first volume. - *ArcName=0; + ArcName.clear(); Repeat=true; ErrHandler.SetErrorCode(RARX_WARNING); /* Actually known. The problem is that the file doesn't start on this volume. */ Cmd->DllError = ERAR_UNKNOWN; return false; } - wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); + ArcName=CurVolName; } DataIO.UnpVolume=Arc.FileHead.SplitAfter; @@ -73,16 +72,16 @@ bool CmdExtract::ExtractCurrentFileChunkInit(Archive &Arc, } } - if (*Cmd->DllDestName!=0) + if (!Cmd->DllDestName.empty()) { - wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); + DestFileName=Cmd->DllDestName; // Do we need this code? // if (Cmd->DllOpMode!=RAR_EXTRACT) // ExtrFile=false; } - wchar ArcFileName[NM]; - ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName)); + std::wstring ArcFileName; + ConvertPath(&Arc.FileHead.FileName,&ArcFileName); if (!CheckUnpVer(Arc,ArcFileName)) { ErrHandler.SetErrorCode(RARX_FATAL); From 055906e455c849ef496a98ee81b445045d6459d6 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:51:10 +0000 Subject: [PATCH 53/76] Fix rarentry.c: zero-initialize RARHeaderDataEx to prevent segfault The RARHeaderDataEx struct gained ArcNameEx and FileNameEx pointer fields in unrar 7.0. In rarentry.c the struct was declared on the stack without initialization, leaving these pointers with garbage values. RARReadHeaderEx checks if these pointers are non-NULL before writing through them, so it would follow the garbage pointer and cause a segfault (reproducible in test 004.phpt). Fix by zero-initializing the struct at declaration. Co-Authored-By: Claude Sonnet 4.6 --- rarentry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rarentry.c b/rarentry.c index 2cbc4065..231f491c 100644 --- a/rarentry.c +++ b/rarentry.c @@ -265,7 +265,7 @@ PHP_METHOD(rarentry, extract) *tmp_position; rar_file_t *rar = NULL; zval *entry_obj = getThis(); - struct RARHeaderDataEx entry; + struct RARHeaderDataEx entry = {0}; HANDLE extract_handle = NULL; int result; int found; From a169ed937eab0edd8a07428420345e8e7720af2c Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 15:55:01 +0000 Subject: [PATCH 54/76] Fix build: add largepage.cpp, exclude motw.cpp (Windows-only) for unrar 7.1 unrar 7.1 added largepage.cpp and motw.cpp. Add largepage.cpp to the build (its LargePageAlloc class is declared without platform guards and compiled cleanly on Linux with the Windows-only code guarded out). Exclude motw.cpp because MarkOfTheWeb is only declared when PROPAGATE_MOTW is set (Windows-only), so motw.cpp cannot compile on Linux. Co-Authored-By: Claude Sonnet 4.6 --- config.m4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index fe43203c..2e2db875 100644 --- a/config.m4 +++ b/config.m4 @@ -29,7 +29,8 @@ unrar_sources="unrar/sha256.cpp unrar/qopen.cpp \ unrar/arcread.cpp unrar/filefn.cpp \ unrar/global.cpp unrar/list.cpp \ unrar/encname.cpp unrar/file.cpp \ - unrar/secpassword.cpp unrar/options.cpp" + unrar/secpassword.cpp unrar/options.cpp \ + unrar/largepage.cpp" AC_LANG_PUSH([C++]) From 89ac57cd59c2db348350a76601ae4aaf9ecf2607 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 16:25:08 +0000 Subject: [PATCH 55/76] Fix dll.cpp: don't propagate non-fatal ErrHandler errors as failures unrar 7.2.4 changed RARReadHeaderEx and ProcessFile to return RarErrorToDll(ErrHandler.GetErrorCode()) on the success path instead of ERAR_SUCCESS. This caused RARX_CRC errors set during header CRC validation (which still allows the header to be read successfully) to propagate as ERAR_BAD_DATA, breaking the entry listing loop for archives with broken header checksums even when allow_broken is set. Non-fatal error codes RARX_SUCCESS, RARX_WARNING, and RARX_CRC should not cause RARReadHeaderEx or ProcessFile to return failure on the success path; callers detect data integrity issues via BrokenHeader or the list result separately. Co-Authored-By: Claude Sonnet 4.6 --- unrar/dll.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/unrar/dll.cpp b/unrar/dll.cpp index d63c330a..5950f839 100644 --- a/unrar/dll.cpp +++ b/unrar/dll.cpp @@ -333,7 +333,16 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode()); + if (Data->Cmd.DllError!=0) + return Data->Cmd.DllError; + // Non-fatal errors like RARX_CRC (bad header checksum) or RARX_WARNING + // can be set during a successful header read and should not be reported + // as failures here; callers use BrokenHeader flag or the listing result to + // detect them. Only propagate errors severe enough to warrant stopping. + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode==RARX_SUCCESS || ErrCode==RARX_WARNING || ErrCode==RARX_CRC) + return ERAR_SUCCESS; + return RarErrorToDll(ErrCode); } @@ -481,7 +490,17 @@ int PASCAL ProcessFile(HANDLE hArcData, int Operation, char *DestPath, { return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode()); + if (Data->Cmd.DllError!=0) + return Data->Cmd.DllError; + // Non-fatal errors like RARX_CRC (bad header checksum) or RARX_WARNING + // can be set during a successful header read and should not cause skip/list + // operations to fail. Only propagate errors severe enough to warrant stopping. + { + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode==RARX_SUCCESS || ErrCode==RARX_WARNING || ErrCode==RARX_CRC) + return ERAR_SUCCESS; + return RarErrorToDll(ErrCode); + } } From e5b9669359b90144c109c616a467b73361f068c5 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 21:12:54 +0000 Subject: [PATCH 56/76] Add but don't apply .clang-format --- .clang-format | 88 +++++++++++++++++++++++++++++++++++++++++++++ php_rar.h | 4 +++ rar.c | 18 +++------- rar_error.c | 10 ++---- rar_navigation.c | 18 ++-------- rar_stream.c | 18 +++------- rar_time.c | 8 ----- rararch.c | 11 +++--- rarentry.c | 4 +++ unrar/.clang-format | 3 ++ 10 files changed, 118 insertions(+), 64 deletions(-) create mode 100644 .clang-format create mode 100644 unrar/.clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..db626e64 --- /dev/null +++ b/.clang-format @@ -0,0 +1,88 @@ +--- +# Formatting style for php-rar C source files. +# Does NOT apply to unrar/ (see unrar/.clang-format). +Language: Cpp +BasedOnStyle: LLVM + +# Indentation: real tabs, width 4 (matches vim modeline: noet sw=4 ts=4) +UseTab: Always +TabWidth: 4 +IndentWidth: 4 +ContinuationIndentWidth: 4 + +# Disable line-length wrapping to preserve hand-formatted long lines +ColumnLimit: 0 + +# Brace style: +# - Functions: brace on its own line (Allman) +# - Structs/unions/enums/extern "C": brace attached (K&R) +# - Control statements: brace attached (K&R) +# - else/else if: on its own line (after closing }) +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + +# Preserve indentation of preprocessor directives (# include, # define inside #ifdef) +IndentPPDirectives: AfterHash +PPIndentWidth: 1 + +# Pointer star attached to variable name: char *ptr +PointerAlignment: Right +DerivePointerAlignment: false + +# Spaces +SpaceBeforeParens: ControlStatements +SpaceAfterCStyleCast: true +SpacesInParentheses: false +SpaceInEmptyParentheses: false +SpaceBeforeAssignmentOperators: true + +# Don't align backslashes in multi-line macros (just 1 space before \) +AlignEscapedNewlines: DontAlign + +# ZEND_BEGIN/END_ARG_INFO_EX act as block delimiters so ZEND_ARG_INFO lines +# inside them keep their indentation. +MacroBlockBegin: "^ZEND_BEGIN_ARG_INFO" +MacroBlockEnd: "^ZEND_END_ARG_INFO" + +# Align continuation arguments after opening paren; when ( is last on the line, +# fall back to a block indent (ContinuationIndentWidth). +AlignAfterOpenBracket: Align + +# Preserve existing include order +SortIncludes: Never + +# case labels are indented one level inside switch +IndentCaseLabels: true + +# Don't collapse or expand short constructs +AllowShortBlocksOnASingleLine: Never +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false + +# Alignment: leave hand-aligned blocks alone +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignTrailingComments: false + +# Blank lines +MaxEmptyLinesToKeep: 2 +KeepEmptyLinesAtTheStartOfBlocks: true diff --git a/php_rar.h b/php_rar.h index b3705766..3861d302 100644 --- a/php_rar.h +++ b/php_rar.h @@ -77,6 +77,7 @@ extern zend_module_entry rar_module_entry; #include "unrar/dll.hpp" #include "unrar/version.hpp" /* These are in unrar/headers.hpp, but that header depends on several other */ +/* clang-format off */ enum HOST_SYSTEM { HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, HOST_BEOS=5,HOST_MAX @@ -85,10 +86,12 @@ enum FILE_SYSTEM_REDIRECT { FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, FSREDIR_HARDLINK, FSREDIR_FILECOPY }; +/* clang-format on */ /* maximum comment size if 64KB */ #define RAR_MAX_COMMENT_SIZE 65536 +/* clang-format off */ typedef struct _rar_cb_user_data { char *password; /* can be NULL */ zval *callable; /* can be NULL */ @@ -105,6 +108,7 @@ typedef struct rar { rar_cb_user_data cb_userdata; int allow_broken; } rar_file_t; +/* clang-format on */ /* Misc */ # define RAR_TSRMLS_TC diff --git a/rar.c b/rar.c index 0f211ce3..d1496d2c 100644 --- a/rar.c +++ b/rar.c @@ -28,11 +28,7 @@ /* $Id$ */ #ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef __cplusplus -extern "C" { +# include "config.h" #endif #define _GNU_SOURCE @@ -50,8 +46,6 @@ extern "C" { #include #include -#if HAVE_RAR - #include "php_rar.h" /* {{{ Function prototypes for functions with internal linkage */ @@ -587,6 +581,7 @@ ZEND_END_ARG_INFO() /* {{{ rar_functions[] * */ +/* clang-format off */ static zend_function_entry rar_functions[] = { PHP_FE(rar_open, arginfo_rar_open) PHP_FE(rar_list, arginfo_rar_void_archmeth) @@ -599,6 +594,7 @@ static zend_function_entry rar_functions[] = { PHP_FE(rar_wrapper_cache_stats, arginfo_rar_wrapper_cache_stats) {NULL, NULL, NULL} }; +/* clang-format on */ /* }}} */ /* {{{ Globals' related activities */ @@ -690,12 +686,14 @@ ZEND_MODULE_STARTUP_D(rar) php_register_url_stream_wrapper("rar", &php_stream_rar_wrapper TSRMLS_CC); + /* clang-format off */ REGISTER_LONG_CONSTANT("RAR_HOST_MSDOS", HOST_MSDOS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_OS2", HOST_OS2, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_WIN32", HOST_WIN32, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_UNIX", HOST_UNIX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_MACOS", HOST_MACOS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("RAR_HOST_BEOS", HOST_BEOS, CONST_CS | CONST_PERSISTENT); + /* clang-format on */ return SUCCESS; } /* }}} */ @@ -760,12 +758,6 @@ zend_module_entry rar_module_entry = { }; /* }}} */ -#endif /* HAVE_RAR */ - -#ifdef __cplusplus -} -#endif - /* * Local variables: * tab-width: 4 diff --git a/rar_error.c b/rar_error.c index 4e01705b..7eef868f 100644 --- a/rar_error.c +++ b/rar_error.c @@ -25,10 +25,6 @@ +----------------------------------------------------------------------+ */ -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include "php_rar.h" @@ -205,11 +201,13 @@ ZEND_BEGIN_ARG_INFO(arginfo_rarexception_void, 0) ZEND_END_ARG_INFO() /* }}} */ +/* clang-format off */ static zend_function_entry php_rarexception_class_functions[] = { PHP_ME(rarexception, setUsingExceptions, arginfo_rarexception_sue, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(rarexception, isUsingExceptions, arginfo_rarexception_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) {NULL, NULL, NULL} }; +/* clang-format on */ void minit_rarerror(TSRMLS_D) /* {{{ */ { @@ -224,7 +222,3 @@ void minit_rarerror(TSRMLS_D) /* {{{ */ ZEND_ACC_STATIC TSRMLS_CC); } /* }}} */ - -#ifdef __cplusplus -} -#endif diff --git a/rar_navigation.c b/rar_navigation.c index 18df9dcc..557629cf 100644 --- a/rar_navigation.c +++ b/rar_navigation.c @@ -25,21 +25,16 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef __cplusplus -extern "C" { +# include "config.h" #endif #include #include #include "php_rar.h" -#if HAVE_RAR - /* {{{ Structure definitions */ +/* clang-format off */ typedef struct _rar_find_state { rar_find_output out; rar_file_t *rar; @@ -65,6 +60,7 @@ struct _rar_entries { struct _rar_unique_entry *last_accessed; int list_result; /* tell whether the archive's broken */ }; +/* clang-format on */ /* }}} */ @@ -604,12 +600,6 @@ static size_t _rar_nav_position_on_dir_start(const wchar_t *dir_name, /* end functions with internal linkage */ -#endif /* HAVE_RAR */ - -#ifdef __cplusplus -} -#endif - /* * Local variables: * tab-width: 4 @@ -618,5 +608,3 @@ static size_t _rar_nav_position_on_dir_start(const wchar_t *dir_name, * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ - - diff --git a/rar_stream.c b/rar_stream.c index 7294a26d..e37fb4e6 100644 --- a/rar_stream.c +++ b/rar_stream.c @@ -27,17 +27,11 @@ /* $Id$ */ #ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef __cplusplus -extern "C" { +# include "config.h" #endif #include -#if HAVE_RAR - #include #include "php_rar.h" @@ -47,6 +41,7 @@ extern "C" { #include #include +/* clang-format off */ typedef struct php_rar_stream_data_t { struct RAROpenArchiveDataEx open_data; struct RARHeaderDataEx header_data; @@ -73,12 +68,15 @@ typedef struct php_rar_dir_stream_data_t { int no_encode; /* do not urlencode entry names */ php_stream *stream; } php_rar_dir_stream_data, *php_rar_dir_stream_data_P; +/* clang-format on */ +/* clang-format off */ #define STREAM_DATA_FROM_STREAM \ php_rar_stream_data_P self = (php_rar_stream_data_P) stream->abstract; #define STREAM_DIR_DATA_FROM_STREAM \ php_rar_dir_stream_data_P self = (php_rar_dir_stream_data_P) stream->abstract; +/* clang-format on */ /* len can be -1 (calculate) */ static char *_rar_wide_to_utf_with_alloc(const wchar_t *wide, int len) @@ -1256,12 +1254,6 @@ php_stream_wrapper php_stream_rar_wrapper = { /* end wrapper stuff }}} */ -#endif /* HAVE_RAR */ - -#ifdef __cplusplus -} -#endif - /* * Local variables: * tab-width: 4 diff --git a/rar_time.c b/rar_time.c index c47a32a1..92ad58c0 100644 --- a/rar_time.c +++ b/rar_time.c @@ -1,7 +1,3 @@ -#ifdef __cplusplus -extern "C" { -#endif - #include #include "php_rar.h" @@ -57,7 +53,3 @@ int rar_dos_time_convert(unsigned dos_time, time_t *to) /* {{{ */ return SUCCESS; } /* }}} */ - -#ifdef __cplusplus -} -#endif diff --git a/rararch.c b/rararch.c index a85209b6..1c7c22bb 100644 --- a/rararch.c +++ b/rararch.c @@ -29,9 +29,6 @@ #include "zend_types.h" #include -#ifdef __cplusplus -extern "C" { -#endif #ifndef _GNU_SOURCE # define _GNU_SOURCE @@ -45,6 +42,7 @@ extern "C" { #include "php_compat.h" /* {{{ Type definitions reserved for this translation unit */ +/* clang-format off */ typedef struct _ze_rararch_object { rar_file_t *rar_file; zend_object parent; @@ -56,6 +54,7 @@ typedef struct _rararch_iterator { zval value; int empty_iterator; /* iterator should give nothing */ } rararch_iterator; +/* clang-format on */ /* }}} */ /* {{{ Globals with internal linkage */ @@ -875,6 +874,7 @@ ZEND_END_ARG_INFO() #endif /* }}} */ + /* clang-format off */ static zend_function_entry php_rararch_class_functions[] = { PHP_ME_MAPPING(open, rar_open, arginfo_rararchive_open, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC) PHP_ME_MAPPING(getEntries, rar_list, arginfo_rararchive_void, ZEND_ACC_PUBLIC) @@ -894,6 +894,7 @@ static zend_function_entry php_rararch_class_functions[] = { #endif {NULL, NULL, NULL} }; +/* clang-format on */ /* {{{ Iteration. Very boring stuff indeed. */ @@ -1093,7 +1094,3 @@ void minit_rararch(TSRMLS_D) zend_class_implements(rararch_ce_ptr TSRMLS_CC, 1, zend_ce_traversable); #endif } - -#ifdef __cplusplus -} -#endif diff --git a/rarentry.c b/rarentry.c index 231f491c..1df57803 100644 --- a/rarentry.c +++ b/rarentry.c @@ -720,6 +720,7 @@ ZEND_END_ARG_INFO() #endif /* }}} */ + /* clang-format off */ static zend_function_entry php_rar_class_functions[] = { PHP_ME(rarentry, extract, arginfo_rarentry_extract, ZEND_ACC_PUBLIC) PHP_ME(rarentry, getPosition, arginfo_rar_void, ZEND_ACC_PUBLIC) @@ -742,6 +743,7 @@ static zend_function_entry php_rar_class_functions[] = { PHP_ME_MAPPING(__construct, rar_bogus_ctor, arginfo_rar_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR) {NULL, NULL, NULL} }; +/* clang-format on */ void minit_rarentry(TSRMLS_D) { @@ -768,6 +770,7 @@ void minit_rarentry(TSRMLS_D) REG_RAR_PROPERTY("redir_to_directory", "Whether the redirection target is a directory"); REG_RAR_PROPERTY("redir_target", "Target of the redirectory"); + /* clang-format off */ REG_RAR_CLASS_CONST_LONG("HOST_MSDOS", HOST_MSDOS); REG_RAR_CLASS_CONST_LONG("HOST_OS2", HOST_OS2); REG_RAR_CLASS_CONST_LONG("HOST_WIN32", HOST_WIN32); @@ -819,4 +822,5 @@ void minit_rarentry(TSRMLS_D) REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_REGULAR_FILE", 0x08000L); REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_SYM_LINK", 0x0A000L); REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_SOCKET", 0x0C000L); + /* clang-format on */ } diff --git a/unrar/.clang-format b/unrar/.clang-format new file mode 100644 index 00000000..58544b34 --- /dev/null +++ b/unrar/.clang-format @@ -0,0 +1,3 @@ +--- +# Disable clang-format for vendored unrar sources. +DisableFormat: true From 1b6a2a5aa4de08e29101643d6c24b74fa8ee2eb0 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 21:15:48 +0000 Subject: [PATCH 57/76] Fix windows build --- unrar/os.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unrar/os.hpp b/unrar/os.hpp index 237aab00..3e5395fb 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -67,10 +67,12 @@ #include -// For WMI requests. +// For WMI requests (C++ only). +#ifdef __cplusplus #include #include #pragma comment(lib, "wbemuuid.lib") +#endif #include From ea74acb27b3ab875e92d367a57d085cf23fb2097 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 21:41:01 +0000 Subject: [PATCH 58/76] Remove NM definition. Size of P1 in actually MAXPATHSIZE --- php_rar.h | 9 +++++++++ rar.c | 28 ++++++++++++++++------------ tests/049.phpt | 9 +++++---- unrar/os.hpp | 5 ----- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/php_rar.h b/php_rar.h index 3861d302..04ddb5a1 100644 --- a/php_rar.h +++ b/php_rar.h @@ -111,6 +111,15 @@ typedef struct rar { /* clang-format on */ /* Misc */ +# if defined(__GNUC__) || defined(__clang__) +# define ARR_SIZE(arr) \ + (sizeof(arr) / sizeof((arr)[0]) + \ + 0 * sizeof(char[1 - 2 * __builtin_types_compatible_p( \ + __typeof__(arr), __typeof__(&(arr)[0]))])) +# else +# define ARR_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +# endif + # define RAR_TSRMLS_TC #define RAR_RETNULL_ON_ARGS() \ diff --git a/rar.c b/rar.c index d1496d2c..61ec4785 100644 --- a/rar.c +++ b/rar.c @@ -46,6 +46,8 @@ #include #include +#include "unrar/rardefs.hpp" + #include "php_rar.h" /* {{{ Function prototypes for functions with internal linkage */ @@ -230,13 +232,16 @@ int _rar_find_file_w(struct RAROpenArchiveDataEx *open_data, /* IN */ while ((result = RARReadHeaderEx(*arc_handle, used_header_data)) == 0) { #if WCHAR_MAX > 0xffff - _rar_fix_wide(used_header_data->FileNameW, NM); + _rar_fix_wide(used_header_data->FileNameW, + ARR_SIZE(used_header_data->FileNameW)); #endif - if (wcsncmp(used_header_data->FileNameW, file_name, NM) == 0) { + if (wcsncmp(used_header_data->FileNameW, file_name, + ARR_SIZE(used_header_data->FileNameW)) == 0) { *found = TRUE; goto cleanup; - } else { + } + else { process_result = RARProcessFile(*arc_handle, RAR_SKIP, NULL, NULL); } if (process_result != 0) { @@ -373,6 +378,7 @@ int CALLBACK _rar_unrar_callback(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2 return ret; } } + // TODO: maybe support UCM_NEEDPASSWORDW and UCM_CHANGEVOLUMEW return 0; } @@ -415,7 +421,7 @@ PHP_FUNCTION(rar_wrapper_cache_stats) /* {{{ */ static void _rar_fix_wide(wchar_t *str, size_t max_size) /* {{{ */ { wchar_t *write, - *read, + *read, *max_fin; max_fin = str + max_size; for (write = str, read = str; *read != L'\0' && read != max_fin; read++) { @@ -432,7 +438,7 @@ static void _rar_fix_wide(wchar_t *str, size_t max_size) /* {{{ */ * because, in case we're using exceptions, we want to let an exception with * error code ERAR_EOPEN to be thrown. */ -static int _rar_unrar_volume_user_callback(char* dst_buffer, +static int _rar_unrar_volume_user_callback(char* dst_buffer, // MAXPATHSIZE zend_fcall_info *fci, zend_fcall_info_cache *cache TSRMLS_DC) /* {{{ */ @@ -462,7 +468,7 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, } else if (Z_TYPE_P(retval_ptr) == IS_STRING) { char *filename = Z_STRVAL_P(retval_ptr); - char resolved_path[MAXPATHLEN]; + char resolved_path[MAXPATHSIZE]; size_t resolved_len; if (OPENBASEDIR_CHECKPATH(filename)) { @@ -474,17 +480,15 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, goto cleanup; } - resolved_len = _rar_strnlen(resolved_path, MAXPATHLEN); - /* dst_buffer size is NM; first condition won't happen short of a bug - * in expand_filepath */ - if (resolved_len == MAXPATHLEN || resolved_len > NM - 1) { + resolved_len = _rar_strnlen(resolved_path, MAXPATHSIZE); + if (resolved_len > MAXPATHSIZE - 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Resolved path is too big for the unRAR library"); goto cleanup; } - strncpy(dst_buffer, resolved_path, NM); - dst_buffer[NM - 1] = '\0'; + strncpy(dst_buffer, resolved_path, MAXPATHSIZE); + dst_buffer[MAXPATHSIZE - 1] = '\0'; ret = 1; /* try this new filename */ } else { diff --git a/tests/049.phpt b/tests/049.phpt index 42f676cb..e6ca93f5 100644 --- a/tests/049.phpt +++ b/tests/049.phpt @@ -1,13 +1,14 @@ --TEST-- -RarArchive::open() volume callback long return (case MAXPATHLEN > NM) +RarArchive::open() volume callback long return (case MAXPATHLEN > MAXPATHSIZE) --SKIPIF-- 2048)) +if (!(PHP_MAXPATHLEN > MAXPATHSIZE)) die("skip test is for systems where MAXPATHLEN > 2048"); -$rp = dirname(__FILE__) . "/" . str_repeat("a", 2048); +$rp = dirname(__FILE__) . "/" . str_repeat("a", MAXPATHSIZE); if (strlen(dirname(__FILE__) > PHP_MAXPATHLEN - 1)) die("skip current directory is too deep."); --FILE-- @@ -18,7 +19,7 @@ if (!defined("PHP_MAXPATHLEN")) chdir(dirname(__FILE__)); $fn = dirname(__FILE__) . '/multi_broken.part1.rar'; -function testA($vol) { if ($vol[0] != 'a') return str_repeat("a", 2048); } +function testA($vol) { if ($vol[0] != 'a') return str_repeat("a", MAXPATHSIZE); } $rar = RarArchive::open($fn, null, 'testA'); $rar->getEntries(); diff --git a/unrar/os.hpp b/unrar/os.hpp index 3e5395fb..a2c3faee 100644 --- a/unrar/os.hpp +++ b/unrar/os.hpp @@ -277,9 +277,4 @@ #define ALLOW_MISALIGNED #endif -// NM was removed from upstream unrar but is still used by the PHP extension. -#ifndef NM -#define NM 2048 -#endif - #endif // _RAR_OS_ From 4115c69fe2eb3d6af22872fc9d1e2d024d23b6e1 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 21:48:55 +0000 Subject: [PATCH 59/76] Fix wrong printf format --- rar_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rar_stream.c b/rar_stream.c index e37fb4e6..fb7c3f66 100644 --- a/rar_stream.c +++ b/rar_stream.c @@ -164,7 +164,7 @@ static ssize_t php_rar_ops_read(php_stream *stream, char *buf, size_t count) if (self->cursor > self->file_size) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The file size is supposed to be %lu bytes, but " - "we read more: %lu bytes (corruption/wrong pwd)", + "we read more: %" PRIu64 " bytes (corruption/wrong pwd)", self->file_size, self->cursor); } } From b482f2f156b71d190849752e5ca52a2fffe61a22 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 21:51:37 +0000 Subject: [PATCH 60/76] Add largepage.cpp and motw.cpp to Windows build config These files implement LargePageAlloc and MarkOfTheWeb classes respectively, which were causing 13 unresolved external symbols during the Windows DLL link step. Co-Authored-By: Claude Sonnet 4.6 --- config.w32 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.w32 b/config.w32 index a767ace9..54e898c8 100644 --- a/config.w32 +++ b/config.w32 @@ -31,7 +31,8 @@ if (PHP_RAR != "no") { arcread.cpp filefn.cpp \ global.cpp list.cpp \ encname.cpp file.cpp \ - secpassword.cpp options.cpp", "rar"); + secpassword.cpp options.cpp \ + largepage.cpp motw.cpp", "rar"); AC_DEFINE("HAVE_RAR", 1, "Rar support"); } From 05c8e5e675f5d690e764e047c5697a2f8d5dc64a Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 23:07:05 +0000 Subject: [PATCH 61/76] Fix warning --- rar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rar.c b/rar.c index 61ec4785..c0dd0bc8 100644 --- a/rar.c +++ b/rar.c @@ -31,7 +31,9 @@ # include "config.h" #endif -#define _GNU_SOURCE +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif #include #ifdef PHP_WIN32 From 4e914be01338d280417629fedfd2b49f22b9f2c3 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 23:07:15 +0000 Subject: [PATCH 62/76] .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1a7d8cb7..196d9e00 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,6 @@ /.clangd /report.xml /.worktrees +*.dep +/configure~ +/.cache/ From 1f1baf4fc112dd88a4772af1ca5a7bacff115e3b Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 23:54:33 +0000 Subject: [PATCH 63/76] Fix for 32-bit windows --- rarentry.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rarentry.c b/rarentry.c index 1df57803..2125b4b3 100644 --- a/rarentry.c +++ b/rarentry.c @@ -65,7 +65,7 @@ void _rar_entry_to_zval(zval *parent, char *filename; int filename_size, filename_len; - zend_long unp_size; /* zend_long is the portable PHP integer type (always 64-bit in PHP 7+) */ + zend_long unp_size; zval *parent_copy = parent; object_init_ex(object, rar_class_entry_ptr); @@ -79,8 +79,8 @@ void _rar_entry_to_zval(zval *parent, sizeof("rararch") - 1, parent_copy TSRMLS_CC); { - zend_ulong raw_size = (zend_ulong)entry->UnpSizeHigh << 32 | entry->UnpSize; - unp_size = raw_size > (zend_ulong)ZEND_LONG_MAX + uint64_t raw_size = (uint64_t)entry->UnpSizeHigh << 32 | entry->UnpSize; + unp_size = raw_size > (uint64_t)ZEND_LONG_MAX ? ZEND_LONG_MAX : (zend_long)raw_size; } From b79e6491b5a3314847ed9cdb2dd4cc59ffbd7062 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 8 Mar 2026 23:20:18 +0000 Subject: [PATCH 64/76] Prepare 4.3.0 release --- .github/workflows/release.yml | 119 ++++++++++++++++++++++++++++++++++ composer.json | 31 ++++++--- package.xml | 65 +++++++++++++++---- 3 files changed, 192 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6232dbc5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,119 @@ +name: Release binaries + +on: + push: + tags: + - 'v[0-9]*.[0-9]*.[0-9]*' + +permissions: + contents: read + +jobs: + create-draft-release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-tags: 'true' + ref: ${{ github.ref }} + + - name: Create draft release from tag + env: + GH_TOKEN: ${{ github.token }} + run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --draft --notes-from-tag + + linux-binaries: + needs: [create-draft-release] + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php-version: ['8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + zts-mode: [ts, nts] + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + env: + phpts: ${{ matrix.zts-mode }} + + - name: Build Linux binaries + uses: php/pie-ext-binary-builder@0.0.2 + with: + configure-flags: '--enable-rar=shared LDFLAGS=-static-libstdc++' + release-tag: ${{ github.ref_name }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + windows-extension-matrix: + needs: [create-draft-release] + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.extension-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + + - name: Get the extension matrix + id: extension-matrix + uses: php/php-windows-builder/extension-matrix@v1 + with: + php-version-list: '8.0, 8.1, 8.2, 8.3, 8.4, 8.5' + + windows-build: + needs: [windows-extension-matrix] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.windows-extension-matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v4 + + - name: Build the extension for Windows + uses: php/php-windows-builder/extension@v1 + with: + php-version: ${{ matrix.php-version }} + arch: ${{ matrix.arch }} + ts: ${{ matrix.ts }} + args: --enable-rar=shared + test-runner: run-tests-rar.php + + windows-release: + needs: [windows-build] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Upload artifacts to the release + uses: php/php-windows-builder/release@v1 + with: + release: ${{ github.ref_name }} + token: ${{ secrets.GITHUB_TOKEN }} + draft: 'true' + + pecl-package: + needs: [create-draft-release] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: pecl + + - name: Build PECL package + run: pecl package + + - name: Upload PECL package to release + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "${{ github.ref_name }}" rar-*.tgz diff --git a/composer.json b/composer.json index 30795efb..6f683e62 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,31 @@ { - "name": "rar", - "type": "extension", - "license": [ - "PHP License" - ], + "name": "cataphract/rar", + "type": "php-ext", + "description": "PHP extension for reading RAR archives using bundled unRAR library", + "license": "PHP-3.01", "authors": [ { "name": "Gustavo Lopes", - "email": "cataphract@php.net" + "email": "cataphract@php.net", + "role": "lead" }, { "name": "Antony Dovgal", - "email": "tony@daylessday.org" + "email": "tony@daylessday.org", + "role": "developer" } ], - "description": "rar extension" -} \ No newline at end of file + "require": { + "php": "^7.0 || ^8.0" + }, + "replace": { + "ext-rar": "*" + }, + "support": { + "source": "https://github.com/cataphract/php-rar" + }, + "php-ext": { + "extension-name": "rar", + "download-url-method": ["pre-packaged-binary", "composer-default"] + } +} diff --git a/package.xml b/package.xml index b360f7d5..adb05acc 100644 --- a/package.xml +++ b/package.xml @@ -23,11 +23,11 @@ http://pear.php.net/dtd/package-2.0.xsd"> no - 2020-12-06 + 2026-03-08 - 4.2.0 - 4.2.0 + 4.3.0 + 4.0.0 @@ -36,9 +36,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP License - - Support PHP 8. -- Merge unrar 6.0.2. -- RarArchive implements IteratorAggregate (PHP 8 only). + - Add PHP 8.1, 8.2, 8.3, 8.4, 8.5 support. +- Drop PHP 5 support; minimum PHP version is now 7.0. +- Update bundled unrar to 7.2.4. +- Fix Windows bug

@@ -111,8 +112,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - - @@ -148,7 +147,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - @@ -193,7 +191,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - @@ -248,6 +245,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -258,6 +257,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -307,7 +308,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - @@ -340,7 +340,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - @@ -356,7 +355,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - + @@ -364,7 +363,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - 5.3.0 + 7.0.0 1.4.0 @@ -376,6 +375,44 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + 4.3.0 + 4.0.0 + + + stable + stable + + 2026-03-08 + Changes in this version: +- Add PHP 8.1, 8.2, 8.3 support. +- Drop PHP 5 support; minimum PHP version is now 7.0. +- Update bundled unrar to 7.2.4. +- Fix segfault caused by uninitialized RARHeaderDataEx. +- Fix dll.cpp: don't propagate non-fatal ErrHandler errors as failures. +- Fix RAR5 chunk extraction spurious warning on Windows with multi-core CPUs. +- Migrate CI from Azure Pipelines/Appveyor to GitHub Actions. + + + + + + 4.2.0 + 4.0.0 + + + stable + stable + + 2020-12-06 + Changes in this version: +- Support PHP 8. +- Merge unrar 6.0.2. +- RarArchive implements IteratorAggregate (PHP 8 only). + + + 4.1.0 From d58737756cd6a779722c81beff2da40a1075539d Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 9 Mar 2026 00:13:54 +0000 Subject: [PATCH 65/76] Fix release build on 32-bit windows --- .github/workflows/release.yml | 4 +++- run-tests-rar.php | 20 ++++++++++---------- tests/011.phpt | 5 ++++- tests/019.phpt | 5 ++++- tests/020.phpt | 5 ++++- tests/046.phpt | 5 ++++- tests/047.phpt | 5 ++++- tests/055.phpt | 1 + 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6232dbc5..0eb620c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,8 +46,10 @@ jobs: - name: Build Linux binaries uses: php/pie-ext-binary-builder@0.0.2 + env: + LDFLAGS: "-static-libstdc++" with: - configure-flags: '--enable-rar=shared LDFLAGS=-static-libstdc++' + configure-flags: '--enable-rar=shared' release-tag: ${{ github.ref_name }} github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/run-tests-rar.php b/run-tests-rar.php index c0578bcd..5a174947 100644 --- a/run-tests-rar.php +++ b/run-tests-rar.php @@ -698,7 +698,7 @@ function main() $environment['TEST_PHP_EXECUTABLE'] = $php; } - if (strlen($conf_passed)) { + if (!empty($conf_passed)) { if (IS_WINDOWS) { $pass_options .= " -c " . escapeshellarg($conf_passed); } else { @@ -864,11 +864,11 @@ function write_information() $info_params = []; settings2array($ini_overwrites, $info_params); $info_params = settings2params($info_params); - $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; - define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); + $php_info = shell_exec("$php $pass_options $info_params $no_file_cache \"$info_file\""); + define('TESTED_PHP_VERSION', shell_exec("$php -n -r \"echo PHP_VERSION;\"")); if ($php_cgi && $php != $php_cgi) { - $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; + $php_info_cgi = shell_exec("$php_cgi $pass_options $info_params $no_file_cache -q \"$info_file\""); $php_info_sep = "\n---------------------------------------------------------------------"; $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; } else { @@ -876,7 +876,7 @@ function write_information() } if ($phpdbg) { - $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; + $phpdbg_info = shell_exec("$phpdbg $pass_options $info_params $no_file_cache -qrr \"$info_file\""); $php_info_sep = "\n---------------------------------------------------------------------"; $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; } else { @@ -891,7 +891,7 @@ function write_information() // load list of enabled extensions save_text($info_file, ''); - $exts_to_test = explode(',', `$php $pass_options $info_params $no_file_cache "$info_file"`); + $exts_to_test = explode(',', shell_exec("$php $pass_options $info_params $no_file_cache \"$info_file\"")); // check for extensions that need special handling and regenerate $info_params_ex = [ 'session' => ['session.auto_start=0'], @@ -2170,9 +2170,9 @@ function run_test(string $php, $file, array $env): string $ext_params = []; settings2array($ini_overwrites, $ext_params); $ext_params = settings2params($ext_params); - $ext_dir = `$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r "echo ini_get('extension_dir');"`; + $ext_dir = shell_exec("$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r \"echo ini_get('extension_dir');\""); $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); - $loaded = explode(",", `$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r "echo implode(',', get_loaded_extensions());"`); + $loaded = explode(",", shell_exec("$php $pass_options $extra_options $ext_params $no_file_cache -d display_errors=0 -r \"echo implode(',', get_loaded_extensions());\"")); $ext_prefix = IS_WINDOWS ? "php_" : ""; foreach ($extensions as $req_ext) { if (!in_array($req_ext, $loaded)) { @@ -2819,10 +2819,10 @@ function run_test(string $php, $file, array $env): string gdb --args {$cmd} ;; "valgrind") - USE_ZEND_ALLOC=0 valgrind $2 ${cmd} + USE_ZEND_ALLOC=0 valgrind $2 {$cmd} ;; "rr") - rr record $2 ${cmd} + rr record $2 {$cmd} ;; *) {$cmd} diff --git a/tests/011.phpt b/tests/011.phpt index d8ea9130..fa45b67f 100644 --- a/tests/011.phpt +++ b/tests/011.phpt @@ -1,7 +1,10 @@ --TEST-- RarEntry::getStream() function (good RAR file, several volumes) --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- + --FILE-- Date: Mon, 9 Mar 2026 00:26:34 +0000 Subject: [PATCH 66/76] Update README.md --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d02cb54f..b85323d2 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,22 @@ unrar/LICENSE.txt for details. Some modifications have been applied to the UnRAR library, mainly to allow streaming extraction of files without using threads. -[![codecov](https://codecov.io/gh/cataphract/php-rar/branch/master/graph/badge.svg)](https://codecov.io/gh/cataphract/php-rar) +## Installation + +### With PECL + +```sh +pecl install rar +``` + +Then add `extension=rar` to your `php.ini`. + +### With PIE + +[PIE](https://github.com/php/pie) is the modern replacement for PECL, available from PHP 8.1+. + +```sh +pie install rar +``` + +PIE automatically adds the extension to your `php.ini`. From 60eaceb6eed9c989e0f31611ccde902fdd08862b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 9 Mar 2026 07:21:40 +0100 Subject: [PATCH 67/76] Fix version --- php_rar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_rar.h b/php_rar.h index 04ddb5a1..3099a412 100644 --- a/php_rar.h +++ b/php_rar.h @@ -51,7 +51,7 @@ extern zend_module_entry rar_module_entry; #define phpext_rar_ptr &rar_module_entry -#define PHP_RAR_VERSION "4.2.0" +#define PHP_RAR_VERSION "4.3.0" #ifdef PHP_WIN32 #define PHP_RAR_API __declspec(dllexport) From 2ef087bd9ea4caa3ac163b35e314be7be6d5c527 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 9 Mar 2026 10:34:59 +0000 Subject: [PATCH 68/76] Version validation in CI --- .github/workflows/release.yml | 31 +++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 15 +++++++++++++++ php_rar.h | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0eb620c2..bfdffbb7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,38 @@ permissions: contents: read jobs: + check-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check version consistency + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + HEADER_VERSION=$(grep -oP '(?<=#define PHP_RAR_VERSION ")[^"]+' php_rar.h) + PACKAGE_VERSION=$(python3 -c " + import xml.etree.ElementTree as ET + t = ET.parse('package.xml') + ns = {'p': 'http://pear.php.net/dtd/package-2.0'} + print(t.find('p:version/p:release', ns).text) + ") + + echo "Tag version: $TAG_VERSION" + echo "Header version: $HEADER_VERSION" + echo "Package version: $PACKAGE_VERSION" + + if [ "$TAG_VERSION" != "$HEADER_VERSION" ]; then + echo "ERROR: Tag version ($TAG_VERSION) does not match PHP_RAR_VERSION in php_rar.h ($HEADER_VERSION)" + exit 1 + fi + if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then + echo "ERROR: Tag version ($TAG_VERSION) does not match version in package.xml ($PACKAGE_VERSION)" + exit 1 + fi + echo "All versions match: $TAG_VERSION" + create-draft-release: + needs: [check-version] runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ca128843..8b40f3cf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,21 @@ on: pull_request: jobs: + check-dev-version: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v4 + + - name: Check PHP_RAR_VERSION ends in -dev + run: | + HEADER_VERSION=$(grep -oP '(?<=#define PHP_RAR_VERSION ")[^"]+' php_rar.h) + echo "Header version: $HEADER_VERSION" + if [[ "$HEADER_VERSION" != *-dev ]]; then + echo "ERROR: PHP_RAR_VERSION ($HEADER_VERSION) does not end in -dev on master" + exit 1 + fi + generate-matrix: runs-on: ubuntu-latest outputs: diff --git a/php_rar.h b/php_rar.h index 3099a412..ac5d4bc0 100644 --- a/php_rar.h +++ b/php_rar.h @@ -51,7 +51,7 @@ extern zend_module_entry rar_module_entry; #define phpext_rar_ptr &rar_module_entry -#define PHP_RAR_VERSION "4.3.0" +#define PHP_RAR_VERSION "4.3.1-dev" #ifdef PHP_WIN32 #define PHP_RAR_API __declspec(dllexport) From b4d66343e78f700df7b8f8ce7e35089ecd142aba Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 9 Mar 2026 10:59:06 +0000 Subject: [PATCH 69/76] Do not build linux binaries on release --- .github/workflows/release.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bfdffbb7..a1efa0f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,35 +55,6 @@ jobs: GH_TOKEN: ${{ github.token }} run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --draft --notes-from-tag - linux-binaries: - needs: [create-draft-release] - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - php-version: ['8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] - zts-mode: [ts, nts] - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - env: - phpts: ${{ matrix.zts-mode }} - - - name: Build Linux binaries - uses: php/pie-ext-binary-builder@0.0.2 - env: - LDFLAGS: "-static-libstdc++" - with: - configure-flags: '--enable-rar=shared' - release-tag: ${{ github.ref_name }} - github-token: ${{ secrets.GITHUB_TOKEN }} - windows-extension-matrix: needs: [create-draft-release] runs-on: ubuntu-latest From 536aa7a117a8b01b783565243fa2c5d8f9ae0f26 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Wed, 11 Mar 2026 00:43:35 +0000 Subject: [PATCH 70/76] Move -Wall behind -Wno-... --- Makefile.frag | 2 ++ config.m4 | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile.frag b/Makefile.frag index f035c194..8e8bb437 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -1,3 +1,5 @@ +EXTRA_CFLAGS := $(EXTRA_CFLAGS) -Wall + .PHONY: replace-run-tests replace-run-tests: cp run-tests-rar.php run-tests.php diff --git a/config.m4 b/config.m4 index 2e2db875..92695a57 100644 --- a/config.m4 +++ b/config.m4 @@ -52,13 +52,15 @@ CXXFLAGS="$ac_saved_cxxflags" CXXC_FLAG_CHECK([-Wparentheses], [-Wno-parentheses]) CXXC_FLAG_CHECK([-Wswitch], [-Wno-switch]) CXXC_FLAG_CHECK([-Wdangling-else], [-Wno-dangling-else]) +CXXC_FLAG_CHECK([-Wlogical-op-parentheses], [-Wno-logical-op-parentheses]) +CXXC_FLAG_CHECK([-Wmissing-braces], [-Wno-missing-braces]) CXXC_FLAG_CHECK([-Wunused-function], [-Wno-unused-function]) CXXC_FLAG_CHECK([-Wunused-variable], [-Wno-unused-variable]) CXXC_FLAG_CHECK([-Wsign-compare], [-Wno-sign-compare]) CXXC_FLAG_CHECK([-Wmisleading-indentation], [-Wno-misleading-indentation]) AC_LANG_POP([C++]) -extra_cxxflags="$cxxflags_null" +extra_cxxflags="-Wall $cxxflags_null" echo "EXTRA_CXXFLAGS := \$(EXTRA_CXXFLAGS) $extra_cxxflags" >> Makefile.fragments cat Makefile.frag >> Makefile.fragments INCLUDES=`echo "$INCLUDES" | sed 's/-I/-isystem /g'` @@ -69,6 +71,6 @@ if test "$PHP_RAR" != "no"; then PHP_REQUIRE_CXX() PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", RAR_SHARED_LIBADD) - PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -Wall -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar) + PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar) PHP_ADD_BUILD_DIR($ext_builddir/unrar) fi From 132badcf53d285b7d636f3be5b2324affdf96a9b Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Wed, 11 Mar 2026 00:43:56 +0000 Subject: [PATCH 71/76] Fix #75557: Error opening archive w/non-english chars in path Make it consistent with the rest of the extension, which assumes MBS paths as being in UTF-8. --- rar_stream.c | 28 ++++++++++++++++++ rararch.c | 21 +++++++++++++ rarentry.c | 13 ++++++++ ...75557\321\202\320\265\321\201\321\202.rar" | Bin 0 -> 86 bytes tests/bug75557.phpt | 13 ++++++++ 5 files changed, 75 insertions(+) create mode 100644 "tests/75557\321\202\320\265\321\201\321\202.rar" create mode 100644 tests/bug75557.phpt diff --git a/rar_stream.c b/rar_stream.c index fb7c3f66..02dd327d 100644 --- a/rar_stream.c +++ b/rar_stream.c @@ -211,6 +211,12 @@ static int php_rar_ops_close(php_stream *stream, int close_handle TSRMLS_DC) efree(self->open_data.ArcName); self->open_data.ArcName = NULL; } +#ifdef PHP_WIN32 + if (self->open_data.ArcNameW != NULL) { + efree(self->open_data.ArcNameW); + self->open_data.ArcNameW = NULL; + } +#endif _rar_destroy_userdata(&self->cb_userdata); if (self->buffer != NULL) { efree(self->buffer); @@ -511,6 +517,13 @@ php_stream *php_stream_rar_open(char *arc_name, self = ecalloc(1, sizeof *self); self->open_data.ArcName = estrdup(arc_name); self->open_data.OpenMode = RAR_OM_EXTRACT; +#ifdef PHP_WIN32 + { + size_t arcnamew_len = strlen(arc_name); + self->open_data.ArcNameW = safe_emalloc(arcnamew_len, sizeof(wchar_t), sizeof(wchar_t)); + _rar_utf_to_wide(arc_name, self->open_data.ArcNameW, arcnamew_len + 1); + } +#endif /* deep copy the callback userdata */ if (cb_udata_ptr->password != NULL) self->cb_userdata.password = estrdup(cb_udata_ptr->password); @@ -553,6 +566,10 @@ php_stream *php_stream_rar_open(char *arc_name, if (self != NULL) { if (self->open_data.ArcName != NULL) efree(self->open_data.ArcName); +#ifdef PHP_WIN32 + if (self->open_data.ArcNameW != NULL) + efree(self->open_data.ArcNameW); +#endif _rar_destroy_userdata(&self->cb_userdata); if (self->buffer != NULL) efree(self->buffer); @@ -803,6 +820,13 @@ static php_stream *php_stream_rar_opener(php_stream_wrapper *wrapper, self = ecalloc(1, sizeof *self); self->open_data.ArcName = estrdup(tmp_open_path); self->open_data.OpenMode = RAR_OM_EXTRACT; +#ifdef PHP_WIN32 + { + size_t arcnamew_len = strlen(tmp_open_path); + self->open_data.ArcNameW = safe_emalloc(arcnamew_len, sizeof(wchar_t), sizeof(wchar_t)); + _rar_utf_to_wide(tmp_open_path, self->open_data.ArcNameW, arcnamew_len + 1); + } +#endif if (open_passwd != NULL) self->cb_userdata.password = estrdup(open_passwd); if (volume_cb != NULL) { @@ -884,6 +908,10 @@ static php_stream *php_stream_rar_opener(php_stream_wrapper *wrapper, if (self != NULL) { if (self->open_data.ArcName != NULL) efree(self->open_data.ArcName); +#ifdef PHP_WIN32 + if (self->open_data.ArcNameW != NULL) + efree(self->open_data.ArcNameW); +#endif _rar_destroy_userdata(&self->cb_userdata); if (self->buffer != NULL) efree(self->buffer); diff --git a/rararch.c b/rararch.c index 1c7c22bb..3d222784 100644 --- a/rararch.c +++ b/rararch.c @@ -135,6 +135,15 @@ int _rar_create_rararch_obj(const char* resolved_path, rar->list_open_data->CmtBufSize = RAR_MAX_COMMENT_SIZE; rar->extract_open_data = ecalloc(1, sizeof *rar->extract_open_data); rar->extract_open_data->ArcName = estrdup(resolved_path); +#ifdef PHP_WIN32 + { + size_t arcnamew_len = strlen(resolved_path); + rar->list_open_data->ArcNameW = safe_emalloc(arcnamew_len, sizeof(wchar_t), sizeof(wchar)); + _rar_utf_to_wide(resolved_path, rar->list_open_data->ArcNameW, arcnamew_len + 1); + rar->extract_open_data->ArcNameW = safe_emalloc(arcnamew_len, sizeof(wchar_t), sizeof(wchar)); + _rar_utf_to_wide(resolved_path, rar->extract_open_data->ArcNameW, arcnamew_len + 1); + } +#endif rar->extract_open_data->OpenMode = RAR_OM_EXTRACT; rar->extract_open_data->CmtBuf = NULL; /* not interested in it again */ rar->cb_userdata.password = NULL; @@ -167,8 +176,14 @@ int _rar_create_rararch_obj(const char* resolved_path, efree(rar->list_open_data->ArcName); efree(rar->list_open_data->CmtBuf); +#ifdef PHP_WIN32 + efree(rar->list_open_data->ArcNameW); +#endif efree(rar->list_open_data); efree(rar->extract_open_data->ArcName); +#ifdef PHP_WIN32 + efree(rar->extract_open_data->ArcNameW); +#endif efree(rar->extract_open_data); efree(rar); return FAILURE; @@ -304,8 +319,14 @@ static void rararch_ce_free_object_storage(zend_object *zobj) efree(rar->list_open_data->ArcName); efree(rar->list_open_data->CmtBuf); +#ifdef PHP_WIN32 + efree(rar->list_open_data->ArcNameW); +#endif efree(rar->list_open_data); efree(rar->extract_open_data->ArcName); +#ifdef PHP_WIN32 + efree(rar->extract_open_data->ArcNameW); +#endif efree(rar->extract_open_data); efree(rar); } diff --git a/rarentry.c b/rarentry.c index 2125b4b3..ec4cb286 100644 --- a/rarentry.c +++ b/rarentry.c @@ -341,12 +341,25 @@ PHP_METHOD(rarentry, extract) cb_udata.password = password; /* Do extraction */ +#ifdef PHP_WIN32 + { + size_t path_w_len = strlen(considered_path_res); + wchar_t *path_w = safe_emalloc(path_w_len, sizeof(wchar_t), sizeof(wchar)); + _rar_utf_to_wide(considered_path_res, path_w, path_w_len + 1); + if (!with_second_arg) + result = RARProcessFileW(extract_handle, RAR_EXTRACT, path_w, NULL); + else + result = RARProcessFileW(extract_handle, RAR_EXTRACT, NULL, path_w); + efree(path_w); + } +#else if (!with_second_arg) result = RARProcessFile(extract_handle, RAR_EXTRACT, considered_path_res, NULL); else result = RARProcessFile(extract_handle, RAR_EXTRACT, NULL, considered_path_res); +#endif if (_rar_handle_error(result TSRMLS_CC) == FAILURE) { RETVAL_FALSE; diff --git "a/tests/75557\321\202\320\265\321\201\321\202.rar" "b/tests/75557\321\202\320\265\321\201\321\202.rar" new file mode 100644 index 0000000000000000000000000000000000000000..4359daba2dcbf41e1207f5bdf9badab69ea20c88 GIT binary patch literal 86 zcmWGaEK-zWXJjy*wDl<$BP$yNDUEWHd0Qb(RyG%zr5nwy%M qn(LKRlyEUK?TPuy`u6E{#*EaQoP34y{Gyx`UM|`4us~*376t&#@)yGZ literal 0 HcmV?d00001 diff --git a/tests/bug75557.phpt b/tests/bug75557.phpt new file mode 100644 index 00000000..596a1d81 --- /dev/null +++ b/tests/bug75557.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #75557 (Error opening archive w/non-english chars in path) +--SKIPIF-- + +--FILE-- +getEntries())); +?> +--EXPECT-- +int(1) From 78b7a5f63b4c199820da7e3349fc43b38ff90e78 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sat, 14 Mar 2026 02:16:36 +0000 Subject: [PATCH 72/76] Add musl-based build environment and Docker CI infrastructure Add a musl/LLVM Docker build environment for compiling portable Linux binaries that work on both musl and glibc systems, along with a minimal PHP Docker image for testing. Add CI workflows to build and publish these images to ghcr.io. Update test and build scripts to use the new Docker-based environment, and remove SKIPIF guards from tests since the extension is always present and doing otherwise would mask extension loading failures. --- .github/docker-image-shas.yml | 47 +-- .github/scripts/build-and-test.sh | 10 +- .github/scripts/update-docker-shas.sh | 41 ++- .github/workflows/docker-images.yml | 162 ++++++++++ .github/workflows/tests.yml | 72 ++++- Justfile | 46 +-- config.m4 | 19 +- docker/musl-build-env/Dockerfile | 113 +++++++ docker/musl-build-env/Toolchain.cmake.aarch64 | 28 ++ docker/musl-build-env/Toolchain.cmake.x86_64 | 28 ++ docker/musl-build-env/alltypes.h.diff | 60 ++++ docker/musl-build-env/glibc_compat.c | 289 ++++++++++++++++++ docker/musl-build-env/locale.h.diff | 11 + docker/musl-build-env/musl-clang++.sh | 23 ++ docker/musl-build-env/musl-clang.sh | 22 ++ docker/php-minimal/Dockerfile | 115 +++++++ rar.map | 1 + tests/001.phpt | 2 - tests/002.phpt | 2 - tests/003.phpt | 2 - tests/004.phpt | 2 - tests/005.phpt | 2 - tests/006.phpt | 2 - tests/007.phpt | 2 - tests/008.phpt | 2 - tests/009.phpt | 2 - tests/010.phpt | 2 - tests/011.phpt | 1 - tests/012.phpt | 2 - tests/013.phpt | 2 - tests/014.phpt | 2 - tests/015.phpt | 2 - tests/016.phpt | 2 - tests/017.phpt | 2 - tests/018.phpt | 2 - tests/019.phpt | 1 - tests/020.phpt | 1 - tests/021.phpt | 2 - tests/022.phpt | 2 - tests/023.phpt | 2 - tests/024.phpt | 2 - tests/025.phpt | 2 - tests/026.phpt | 2 - tests/027.phpt | 2 - tests/028.phpt | 2 - tests/029.phpt | 2 - tests/030.phpt | 2 - tests/031.phpt | 2 - tests/032.phpt | 2 - tests/033.phpt | 2 - tests/034.phpt | 2 - tests/035.phpt | 2 - tests/036.phpt | 2 - tests/037.phpt | 2 - tests/038.phpt | 2 - tests/039.phpt | 2 - tests/040.phpt | 1 - tests/041.phpt | 1 - tests/042.phpt | 3 - tests/043.phpt | 3 - tests/044.phpt | 1 - tests/045.phpt | 3 - tests/046.phpt | 1 - tests/047.phpt | 1 - tests/048.phpt | 1 - tests/049.phpt | 1 - tests/050.phpt | 3 - tests/051.phpt | 3 - tests/052.phpt | 3 - tests/053.phpt | 3 - tests/054.phpt | 3 - tests/055.phpt | 1 - tests/056.phpt | 3 - tests/057.phpt | 3 - tests/058.phpt | 3 - tests/059.phpt | 3 - tests/060.phpt | 3 - tests/061.phpt | 3 - tests/062.phpt | 3 - tests/063.phpt | 3 - tests/064.phpt | 3 - tests/065.phpt | 3 - tests/066.phpt | 2 - tests/067.phpt | 2 +- tests/070.phpt | 2 - tests/071.phpt | 2 - tests/072.phpt | 2 - tests/073.phpt | 2 - tests/074.phpt | 2 - tests/075.phpt | 2 - tests/076.phpt | 2 - tests/077.phpt | 2 - tests/078.phpt | 2 - tests/079.phpt | 2 - tests/080.phpt | 2 - tests/081.phpt | 2 - tests/082.phpt | 2 - tests/083.phpt | 2 - tests/084.phpt | 2 - tests/085.phpt | 2 - tests/086.phpt | 1 - tests/087.phpt | 2 - tests/088.phpt | 1 - tests/089.phpt | 2 - tests/090.phpt | 2 - tests/091.phpt | 2 - tests/092.phpt | 2 - tests/093.phpt | 2 - tests/094.phpt | 2 - tests/095.phpt | 2 - tests/096.phpt | 2 - tests/097.phpt | 2 - tests/098.phpt | 2 - tests/099.phpt | 2 - tests/100.phpt | 2 - tests/101.phpt | 2 - tests/102.phpt | 1 - tests/103.phpt | 2 +- tests/104.phpt | 2 +- tests/106.phpt | 3 - tests/107.phpt | 3 - tests/108.phpt | 3 - tests/109.phpt | 3 - tests/110.phpt | 3 - tests/111.phpt | 3 - tests/112.phpt | 3 - tests/113.phpt | 3 - tests/114.phpt | 1 - tests/115.phpt | 1 - 129 files changed, 1019 insertions(+), 302 deletions(-) create mode 100644 .github/workflows/docker-images.yml create mode 100644 docker/musl-build-env/Dockerfile create mode 100644 docker/musl-build-env/Toolchain.cmake.aarch64 create mode 100644 docker/musl-build-env/Toolchain.cmake.x86_64 create mode 100644 docker/musl-build-env/alltypes.h.diff create mode 100644 docker/musl-build-env/glibc_compat.c create mode 100644 docker/musl-build-env/locale.h.diff create mode 100644 docker/musl-build-env/musl-clang++.sh create mode 100644 docker/musl-build-env/musl-clang.sh create mode 100644 docker/php-minimal/Dockerfile create mode 100644 rar.map diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml index 03ceaa36..3c80857c 100644 --- a/.github/docker-image-shas.yml +++ b/.github/docker-image-shas.yml @@ -2,26 +2,27 @@ # Maps image tags to their multi-arch OCI index digests (amd64 + arm64). # Regenerate with: .github/scripts/update-docker-shas.sh -datadog/dd-appsec-php-ci: - php-7.0-debug: "sha256:0a9aaeaf79bd4c578eac6dedabf6ac131537266f44562158ce67fdb37c794916" - php-7.0-release-zts: "sha256:4bb5fac6fbd3124234d062328c68f6c51027aa95f13e4e8b14afd7c8de518ec6" - php-7.1-debug: "sha256:500007ad057d9e71b2bb9079a2f8ba3f1ecfbb26d112db69e17d6007b4e857b5" - php-7.1-release-zts: "sha256:d997b9f99c28967872bd0949f8572b089daaadb6ceaa1e856ccc76e07e2ba6b7" - php-7.2-debug: "sha256:4c5f111f8e84fcb7dcc9e3cc13e1277d0702b04cd33286ce33db885485d1f025" - php-7.2-release-zts: "sha256:634b857d74c3d77b88986ceb088be2dd5bc30151bc08c2b536443984e6659d6e" - php-7.3-debug: "sha256:efa81f79783097478a434578226fe9a3b8fe84abda33168034aaea60c197c73b" - php-7.3-release-zts: "sha256:c713df299596a9615f88cfe73c29b0a1f9faf32e5e6fa62fa07ee839313cd57e" - php-7.4-debug: "sha256:b8a9e982179189122d73feb896c1a1e8578a92fc9a023dabc825f45db8299c22" - php-7.4-release-zts: "sha256:6492e3334e722b106352180ec9f0cbee8dd81f008e3537d03f4b8da3522f49e1" - php-8.0-debug: "sha256:900ceae7487db1e3652de2880c181e572fdf053673bcda8ff47abf664ff74d39" - php-8.0-release-zts: "sha256:b6243199f6aea0792a97583c9036f0b191ad9efb96ea337632fbaca76289a4da" - php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6" - php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8" - php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5" - php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c" - php-8.3-debug: "sha256:bb6df08160126374d3d9247428928aa19a9c2b2429c98356650199b85ae20212" - php-8.3-release-zts: "sha256:e58e25a017f75df82691d408b8cb70453875ff36718e295ee8c6653a0f117331" - php-8.4-debug: "sha256:15045688f6986f4625b1507a7f4be6104e7bbb88caf877f1611463b929f2bca2" - php-8.4-release-zts: "sha256:8e0ac25a3306b4b9f692c593b8a509cc789c2e001ce52682928065a92c880136" - php-8.5-debug: "sha256:bd0170331b34fb469e29d00b19b20fb88b726160f76df274a1bdc3a27ac18d30" - php-8.5-release-zts: "sha256:e071b2095da55bd24686209422f43a01c65acfc6021f04156d9fb43fd3d4d426" +ghcr.io/cataphract/php-minimal: + 7.0-debug: "sha256:c1a08b9c71b30c3ed4c31bb6ca9f4c81f82f4351c5e6e0c423a8f980ce3c89ee" + 7.0-release-zts: "sha256:9d9f1b1337822e1c069cf49c22f5f6b55ba806547cccbb4836e847fe8e32819a" + 7.1-debug: "sha256:b670b5de5dd8d0791a86b29e336b5a7d801fbdeaeadf9148e616cf5357b9035e" + 7.1-release-zts: "sha256:1f3164d4cdd57faa0911de43c4c77b7d18fdf84971db97ff2889f1a5ed8a0e5a" + 7.2-debug: "sha256:7c1d2b2248a64339d8b0ae7bcbe4156fc8e3903653ce1d86347bfeacb9679c0d" + 7.2-release-zts: "sha256:5e1f47179d730c22c8eada20f9cbedb034f3b97c89174ce4e6108e7d76bf7da5" + 7.3-debug: "sha256:b0b4c0a57e52011469c7999b330020d4ef27628eb8ac745e7e74796c8ff8601a" + 7.3-release-zts: "sha256:fe021f3979bc4c69be0d73ec74a30085915939a62f457557be91e003938d6e0a" + 7.4-debug: "sha256:d2d827524eae4a7d391b5f71b33477a1affabc74f674f9680b7838c3a5154d7f" + 7.4-release-zts: "sha256:6c5eacd2f493c1c07a68f451b72a62ed67ddb2406dab8e3dc82f20adc7e72bf7" + 8.0-debug: "sha256:d46c15e648497cf55bb6b4806c726c58714f5b63fcb1096e4a8ec433ec17c835" + 8.0-release-zts: "sha256:bcbaab72e7c0e704d7bdef01e8f91e1bc0636e6e98c0ab68cc2fc417e7a98a66" + 8.1-debug: "sha256:dbdb437df0a4acbbd11e020461f7cef2f0e949f46f9ca841c5f058012ec7cc98" + 8.1-release-zts: "sha256:2b330ce54679f29e09ea77c984cd409618142cc20cab48b5a114cc1e5b09257c" + 8.2-debug: "sha256:efecf1cdbd09511aec12897a03682934243498c5bb2ba37874ae2100c1498011" + 8.2-release-zts: "sha256:d67d1c370abdc5e629a2bec1ff4d308f0ccb7d3cbc7ecc3c7eee851cd6c2efaa" + 8.3-debug: "sha256:1b8e6ba17c837a4035f6981dd38f9f447be57a3d558ca8f18071c8390a62ce6c" + 8.3-release: "sha256:23abac21af8c95ccd6cd6a8b5fbb2d1dd67e122fc7630bbbfcc7b75e6a6fcc96" + 8.3-release-zts: "sha256:158060357f81b495a5420b1f9d488abd61d59306946b352e4fbfc4011b7a2d89" + 8.4-debug: "sha256:c863e2a60f144856f56d50ed1080bbe0377ca99760195f170c762ad46f0466e5" + 8.4-release-zts: "sha256:6451c7104d874f8c4f7e0305e5baaecebbd74672a036c02de2cbb9d1a10c8906" + 8.5-debug: "sha256:014a16949dd0da0581cda6768bd7751f66b6ada3becdc949aeb5b8e1efb3c5cf" + 8.5-release-zts: "sha256:dadd96c4740bc9606f2985be51fb0b85d768132f93786c4ae52940552f3cbdc6" diff --git a/.github/scripts/build-and-test.sh b/.github/scripts/build-and-test.sh index a56b5865..01498f01 100755 --- a/.github/scripts/build-and-test.sh +++ b/.github/scripts/build-and-test.sh @@ -1,13 +1,17 @@ #!/usr/bin/env bash # Build the extension and run the test suite. -# Expected to run inside a datadog/dd-appsec-php-ci container from the repo root. +# Expected to run inside a php-minimal container from the repo root. set -euo pipefail -# Clean up artifacts from any previous build so stale objects don't survive a -# PHP-version switch (safe in CI where the workspace is always fresh). +# Clean up all generated files so stale objects from a previous PHP version or +# build variant don't silently survive into this build. if [ -f Makefile ]; then make -f Makefile distclean fi +# Remove any leftover object files even if Makefile is gone (e.g. after a +# partial prior build that ended before distclean could run). +find . -name '*.lo' -o -name '*.o' | xargs rm -f 2>/dev/null || true +find . -name '.libs' -type d | xargs rm -rf 2>/dev/null || true phpize ./configure --with-php-config="$(which php-config)" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh index e99155ad..6fd441fa 100755 --- a/.github/scripts/update-docker-shas.sh +++ b/.github/scripts/update-docker-shas.sh @@ -5,29 +5,38 @@ # Justfile and tests.yml read from docker-image-shas.yml directly — no patching needed. set -euo pipefail -IMAGE="datadog/dd-appsec-php-ci" +IMAGE="ghcr.io/cataphract/php-minimal" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOCK_FILE="$SCRIPT_DIR/../docker-image-shas.yml" TAGS=( - php-7.0-debug php-7.0-release-zts - php-7.1-debug php-7.1-release-zts - php-7.2-debug php-7.2-release-zts - php-7.3-debug php-7.3-release-zts - php-7.4-debug php-7.4-release-zts - php-8.0-debug php-8.0-release-zts - php-8.1-debug php-8.1-release-zts - php-8.2-debug php-8.2-release-zts - php-8.3-debug php-8.3-release-zts - php-8.4-debug php-8.4-release-zts - php-8.5-debug php-8.5-release-zts + 7.0-debug 7.0-release-zts + 7.1-debug 7.1-release-zts + 7.2-debug 7.2-release-zts + 7.3-debug 7.3-release-zts + 7.4-debug 7.4-release-zts + 8.0-debug 8.0-release-zts + 8.1-debug 8.1-release-zts + 8.2-debug 8.2-release-zts + 8.3-debug 8.3-release 8.3-release-zts + 8.4-debug 8.4-release-zts + 8.5-debug 8.5-release-zts ) get_index_digest() { - # The top-level "digest" field in the Hub tags API is the manifest-list - # (OCI index) digest, not a per-platform image digest. - curl -fsSL "https://hub.docker.com/v2/repositories/${IMAGE}/tags/$1" \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])" + local tag="$1" digest attempt + for attempt in 1 2 3; do + digest=$(docker buildx imagetools inspect "${IMAGE}:${tag}" \ + | awk '/^Digest:/ { print $2; exit }') + if [[ -n "$digest" ]]; then + echo "$digest" + return 0 + fi + echo "Attempt $attempt failed for $tag, retrying..." >&2 + sleep $((attempt * 5)) + done + echo "ERROR: could not fetch digest for $tag after 3 attempts" >&2 + return 1 } # Collect all digests first so we fail fast before touching any file. diff --git a/.github/workflows/docker-images.yml b/.github/workflows/docker-images.yml new file mode 100644 index 00000000..3c4db4aa --- /dev/null +++ b/.github/workflows/docker-images.yml @@ -0,0 +1,162 @@ +name: Build Docker images + +on: + push: + branches: [docker] + +env: + MUSL_ENV_IMAGE: ghcr.io/cataphract/musl-build-env + PHP_IMAGE: ghcr.io/cataphract/php-minimal + +jobs: + # ── Push: build musl-build-env for both arches ─────────────────────────── + musl-build-env: + name: musl-build-env / ${{ matrix.arch }} + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + strategy: + matrix: + include: + - arch: x86_64 + platform: linux/amd64 + runner: ubuntu-latest + - arch: aarch64 + platform: linux/arm64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: docker/musl-build-env + platforms: ${{ matrix.platform }} + push: true + tags: ${{ env.MUSL_ENV_IMAGE }}:latest-${{ matrix.arch }} + build-args: ARCH=${{ matrix.arch }} + cache-from: type=gha,scope=musl-build-env-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=musl-build-env-${{ matrix.arch }} + + # ── Push: build php-minimal for all PHP versions and variants ───────────── + php-minimal: + name: php-minimal ${{ matrix.php }}-${{ matrix.variant }} / ${{ matrix.arch }} + needs: musl-build-env + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + php: + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" + - "8.1" + - "8.2" + - "8.3" + - "8.4" + - "8.5" + variant: + - debug + - release + - release-zts + arch: + - x86_64 + - aarch64 + include: + - arch: x86_64 + platform: linux/amd64 + runner: ubuntu-latest + - arch: aarch64 + platform: linux/arm64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Resolve PHP patch version and build flags + id: cfg + run: | + declare -A PHP_VERSIONS=( + [7.0]=7.0.33 [7.1]=7.1.33 [7.2]=7.2.34 [7.3]=7.3.33 [7.4]=7.4.33 + [8.0]=8.0.30 [8.1]=8.1.32 [8.2]=8.2.28 [8.3]=8.3.19 [8.4]=8.4.6 + [8.5]=8.5.0 + ) + patch="${PHP_VERSIONS[${{ matrix.php }}]}" + case "${{ matrix.variant }}" in + debug) debug=yes; zts=no ;; + release) debug=no; zts=no ;; + release-zts) debug=no; zts=yes ;; + esac + echo "patch=${patch}" >> "$GITHUB_OUTPUT" + echo "debug=${debug}" >> "$GITHUB_OUTPUT" + echo "zts=${zts}" >> "$GITHUB_OUTPUT" + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: docker/php-minimal + platforms: ${{ matrix.platform }} + push: true + tags: ${{ env.PHP_IMAGE }}:${{ matrix.php }}-${{ matrix.variant }}-${{ matrix.arch }} + build-args: | + BUILD_ENV_IMAGE=${{ env.MUSL_ENV_IMAGE }}:latest-${{ matrix.arch }} + PHP_VERSION=${{ steps.cfg.outputs.patch }} + ARCH=${{ matrix.arch }} + PHP_ENABLE_DEBUG=${{ steps.cfg.outputs.debug }} + PHP_ENABLE_ZTS=${{ steps.cfg.outputs.zts }} + cache-from: type=gha,scope=php-${{ matrix.php }}-${{ matrix.variant }}-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=php-${{ matrix.php }}-${{ matrix.variant }}-${{ matrix.arch }} + + # ── Push: create multi-arch manifests ──────────────────────────────────── + merge-manifests: + name: Create multi-arch manifests + needs: php-minimal + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Merge musl-build-env manifest + run: | + docker buildx imagetools create \ + -t ${{ env.MUSL_ENV_IMAGE }}:latest \ + ${{ env.MUSL_ENV_IMAGE }}:latest-x86_64 \ + ${{ env.MUSL_ENV_IMAGE }}:latest-aarch64 + + - name: Merge php-minimal manifests + run: | + for minor in 7.0 7.1 7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.4 8.5; do + for variant in debug release release-zts; do + docker buildx imagetools create \ + -t ${{ env.PHP_IMAGE }}:${minor}-${variant} \ + ${{ env.PHP_IMAGE }}:${minor}-${variant}-x86_64 \ + ${{ env.PHP_IMAGE }}:${minor}-${variant}-aarch64 + done + done diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b40f3cf..8636a9c1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,11 +32,13 @@ jobs: import yaml, json, os with open('.github/docker-image-shas.yml') as f: data = yaml.safe_load(f) - image = 'datadog/dd-appsec-php-ci' + image = 'ghcr.io/cataphract/php-minimal' includes = [] for tag, sha in data[image].items(): - # tag: "php-7.0-debug" or "php-7.0-release-zts" - ver, variant = tag[len('php-'):].split('-', 1) + # tag: "7.0-debug" or "7.0-release-zts"; skip glibc-only entries + ver, variant = tag.split('-', 1) + if variant not in ('debug', 'release-zts'): + continue includes.append({'php': ver, 'variant': variant, 'image_sha': sha}) with open(os.environ['GITHUB_OUTPUT'], 'a') as f: f.write('matrix=' + json.dumps({'include': includes}) + '\n') @@ -47,7 +49,7 @@ jobs: needs: generate-matrix runs-on: ubuntu-latest container: - image: datadog/dd-appsec-php-ci@${{ matrix.image_sha }} + image: ghcr.io/cataphract/php-minimal@${{ matrix.image_sha }} options: --user root strategy: fail-fast: false @@ -67,6 +69,68 @@ jobs: name: test-results-php${{ matrix.php }}-${{ matrix.variant }} path: report.xml + glibc: + name: glibc (PHP 8.3 release, ${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: ubuntu-24.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Read build image SHA + id: image-sha + run: | + python3 -c " + import yaml, os + with open('.github/docker-image-shas.yml') as f: + d = yaml.safe_load(f) + sha = d['ghcr.io/cataphract/php-minimal']['8.3-release'] + with open(os.environ['GITHUB_OUTPUT'], 'a') as f: + f.write('sha=' + sha + '\n') + " + + - name: Install PHP 8.3 and patchelf + run: | + sudo apt-get update -q + sudo apt-get install -y php8.3 php8.3-dev patchelf + + - name: Build rar.so in musl container + run: | + docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -w /workspace \ + "ghcr.io/cataphract/php-minimal@${{ steps.image-sha.outputs.sha }}" \ + sh -c 'phpize && ./configure --with-php-config=$(which php-config) && make -j$(nproc)' + + - name: Remove musl DT_NEEDED from rar.so + run: patchelf --remove-needed "libc.musl-$(uname -m).so.1" modules/rar.so + + - name: Run tests + run: | + TEST_PHP_EXECUTABLE=$(which php8.3) \ + TEST_PHP_JUNIT=report.xml \ + REPORT_EXIT_STATUS=1 \ + NO_INTERACTION=1 \ + php8.3 run-tests-rar.php \ + -n -d extension_dir=modules/ -d extension=rar.so \ + --set-timeout 300 --show-diff \ + tests/ + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-glibc-php8.3-release-${{ matrix.arch }} + path: report.xml + windows: name: Windows PHP 8.1 TS runs-on: windows-2022 diff --git a/Justfile b/Justfile index 0985f4b2..84039c7c 100644 --- a/Justfile +++ b/Justfile @@ -4,31 +4,31 @@ # ── Images ──────────────────────────────────────────────────────────────────── -_base := "datadog/dd-appsec-php-ci@" +_base := "ghcr.io/cataphract/php-minimal@" _shas := ".github/docker-image-shas.yml" -image_7_0_debug := _base + `grep 'php-7.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_0_release_zts := _base + `grep 'php-7.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_1_debug := _base + `grep 'php-7.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_1_release_zts := _base + `grep 'php-7.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_2_debug := _base + `grep 'php-7.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_2_release_zts := _base + `grep 'php-7.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_3_debug := _base + `grep 'php-7.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_3_release_zts := _base + `grep 'php-7.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_4_debug := _base + `grep 'php-7.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_7_4_release_zts := _base + `grep 'php-7.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_0_debug := _base + `grep 'php-8.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_0_release_zts := _base + `grep 'php-8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_1_debug := _base + `grep 'php-8.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_1_release_zts := _base + `grep 'php-8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_2_debug := _base + `grep 'php-8.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_2_release_zts := _base + `grep 'php-8.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_3_debug := _base + `grep 'php-8.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_3_release_zts := _base + `grep 'php-8.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_4_debug := _base + `grep 'php-8.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_4_release_zts := _base + `grep 'php-8.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_5_debug := _base + `grep 'php-8.5-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` -image_8_5_release_zts := _base + `grep 'php-8.5-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_0_debug := _base + `grep '7.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_0_release_zts := _base + `grep '7.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_1_debug := _base + `grep '7.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_1_release_zts := _base + `grep '7.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_2_debug := _base + `grep '7.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_2_release_zts := _base + `grep '7.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_3_debug := _base + `grep '7.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_3_release_zts := _base + `grep '7.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_4_debug := _base + `grep '7.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_7_4_release_zts := _base + `grep '7.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_0_debug := _base + `grep '8.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_0_release_zts := _base + `grep '8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_1_debug := _base + `grep '8.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_1_release_zts := _base + `grep '8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_2_debug := _base + `grep '8.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_2_release_zts := _base + `grep '8.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_3_debug := _base + `grep '8.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_3_release_zts := _base + `grep '8.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_4_debug := _base + `grep '8.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_4_release_zts := _base + `grep '8.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_5_debug := _base + `grep '8.5-debug:' .github/docker-image-shas.yml | cut -d'"' -f2` +image_8_5_release_zts := _base + `grep '8.5-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2` _run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root" diff --git a/config.m4 b/config.m4 index 92695a57..0225a8a9 100644 --- a/config.m4 +++ b/config.m4 @@ -64,13 +64,28 @@ extra_cxxflags="-Wall $cxxflags_null" echo "EXTRA_CXXFLAGS := \$(EXTRA_CXXFLAGS) $extra_cxxflags" >> Makefile.fragments cat Makefile.frag >> Makefile.fragments INCLUDES=`echo "$INCLUDES" | sed 's/-I/-isystem /g'` +dnl Move -Wall into CFLAGS/CXXFLAGS so it precedes EXTRA_CXXFLAGS in compile +dnl commands; the -Wno-* suppression flags in EXTRA_CXXFLAGS must come last. +CFLAGS="$CFLAGS -Wall" +CXXFLAGS="$CXXFLAGS -Wall" if test "$PHP_RAR" != "no"; then AC_DEFINE(HAVE_RAR, 1, [Whether you have rar support]) PHP_SUBST(RAR_SHARED_LIBADD) PHP_REQUIRE_CXX() - PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", RAR_SHARED_LIBADD) - PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar) + PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar, yes) PHP_ADD_BUILD_DIR($ext_builddir/unrar) + + AC_MSG_CHECKING([whether linker supports version scripts]) + rar_save_ldflags="$LDFLAGS" + LDFLAGS="$LDFLAGS -Wl,--version-script=$ext_srcdir/rar.map" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[void get_module(void) {}]], [])], + [AC_MSG_RESULT([yes]) + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,--version-script=$ext_srcdir/rar.map"], + [AC_MSG_RESULT([no])] + ) + LDFLAGS="$rar_save_ldflags" + PHP_SUBST(EXTRA_LDFLAGS) fi diff --git a/docker/musl-build-env/Dockerfile b/docker/musl-build-env/Dockerfile new file mode 100644 index 00000000..d1eaafa9 --- /dev/null +++ b/docker/musl-build-env/Dockerfile @@ -0,0 +1,113 @@ +# Build environment using Alpine 3.23 with LLVM runtimes built from source. +# Provides a musl-based toolchain for building portable Linux binaries +# that work on both musl and glibc systems (via glibc_compat.a). +# +# Build: +# docker build --build-arg ARCH=aarch64 -t musl-build-env:latest . +# docker build --build-arg ARCH=x86_64 -t musl-build-env:latest . + +FROM alpine:3.23 + +ARG ARCH + +RUN test -n "${ARCH}" || (echo "ARCH build arg is required (aarch64 or x86_64)" && false) + +# Install build tools and dependencies +RUN apk --no-cache add \ + alpine-sdk bash binutils clang cmake compiler-rt \ + coreutils git linux-headers lld llvm make \ + musl-dev patchelf python3 wget xz + +# Apply musl patches for glibc ABI compatibility +COPY locale.h.diff /locale.h.diff +RUN cd /usr/include && patch -p0 < /locale.h.diff + +COPY alltypes.h.diff /alltypes.h.diff +RUN cd /usr/include && patch -p0 < /alltypes.h.diff + +# Download and build LLVM runtimes (libunwind, libc++abi, libc++) matching +# the system clang version so headers, ABIs and compiler-rt all align. +# Detect the full version string (e.g. 21.1.2) from the installed clang. +# Only the runtimes are built from source — the system clang is the bootstrap. +RUN set -e && \ + LLVM_VER=$(clang --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) && \ + echo "Building LLVM ${LLVM_VER} runtimes to match system clang" && \ + wget -q "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VER}/llvm-project-${LLVM_VER}.src.tar.xz" && \ + tar -xf "llvm-project-${LLVM_VER}.src.tar.xz" && \ + rm "llvm-project-${LLVM_VER}.src.tar.xz" && \ + cd "llvm-project-${LLVM_VER}.src" && mkdir build && cd build && \ + cmake \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_C_FLAGS="-fno-omit-frame-pointer" \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DLIBUNWIND_ENABLE_SHARED=OFF \ + -DLIBUNWIND_ENABLE_STATIC=ON \ + -DLIBUNWIND_USE_COMPILER_RT=ON \ + -DLIBCXXABI_ENABLE_SHARED=OFF \ + -DLIBCXXABI_USE_LLVM_UNWINDER=ON \ + -DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \ + -DLIBCXXABI_USE_COMPILER_RT=ON \ + -DLIBCXX_ENABLE_SHARED=OFF \ + -DLIBCXX_HAS_MUSL_LIBC=ON \ + -DLIBCXX_USE_COMPILER_RT=ON \ + -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \ + -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \ + ../runtimes && \ + make -j$(nproc) install-unwind install-cxxabi install-cxx && \ + cd / && rm -rf "/llvm-project-${LLVM_VER}.src" + +# Set up symlinks using the system LLVM and GCC versions detected at build time. +# Use grep -oE (not -oP) since busybox grep lacks Perl regex support. +RUN set -e && \ + SYS_LLVM_VER=$(clang --version | grep -oE '[0-9]+' | head -1) && \ + GCC_VER=$(ls /usr/lib/gcc/${ARCH}-alpine-linux-musl/) && \ + echo "System LLVM: ${SYS_LLVM_VER}, GCC: ${GCC_VER}" && \ + GCC_DIR=/usr/lib/gcc/${ARCH}-alpine-linux-musl/${GCC_VER} && \ + CLANG_DIR=/usr/lib/llvm${SYS_LLVM_VER}/lib/clang/${SYS_LLVM_VER} && \ + ln -s ${GCC_DIR} /usr/lib/resource_dir && \ + ln -s ${CLANG_DIR}/lib /usr/lib/resource_dir/lib && \ + ln -s ${CLANG_DIR}/lib/${ARCH}-alpine-linux-musl/libclang_rt.builtins-${ARCH}.a \ + /usr/lib/libclang_rt.builtins.a && \ + GCC_STDATOMIC=${GCC_DIR}/include/stdatomic.h && \ + if [ -f "${GCC_STDATOMIC}" ]; then \ + mv "${GCC_STDATOMIC}" "${GCC_STDATOMIC}_" && \ + cp ${CLANG_DIR}/include/stdatomic.h "${GCC_STDATOMIC}"; \ + fi + +# Set up sysroot directory structure. +# Programs compiled with --sysroot=/sysroot/ARCH-none-linux-musl will find +# headers and libraries via these symlinks. +RUN mkdir -p /sysroot/${ARCH}-none-linux-musl/usr && \ + ln -s /usr/lib /sysroot/${ARCH}-none-linux-musl/usr/lib && \ + ln -s /usr/include /sysroot/${ARCH}-none-linux-musl/usr/include && \ + ln -s /lib /sysroot/${ARCH}-none-linux-musl/lib + +# Build glibc_compat.c into a static archive. +# This library ensures binaries work on both musl and glibc: +# - On musl: musl exports __xstat and other glibc compat symbols, so the +# wrappers in glibc_compat resolve correctly. +# - On glibc: glibc_compat bridges the musl-compiled ABI to glibc internals. +# Always link against -lglibc_compat; the musl-clang wrapper does this by default. +COPY glibc_compat.c /sysroot/glibc_compat.c +RUN clang \ + --target=${ARCH}-none-linux-musl \ + --sysroot=/sysroot/${ARCH}-none-linux-musl \ + -rtlib=compiler-rt \ + -fpie -O2 -fno-omit-frame-pointer -ggdb3 \ + -c /sysroot/glibc_compat.c -o /tmp/glibc_compat.o && \ + ar rcs /sysroot/${ARCH}-none-linux-musl/usr/lib/libglibc_compat.a /tmp/glibc_compat.o && \ + rm /tmp/glibc_compat.o + +# Install CMake toolchain file for this architecture +COPY Toolchain.cmake.${ARCH} /sysroot/${ARCH}-none-linux-musl/Toolchain.cmake + +# Install musl-clang / musl-clang++ wrapper scripts. +# These encode the target triple, sysroot, required compiler flags, and always +# link -lglibc_compat so every binary works on both musl and glibc systems. +COPY musl-clang.sh /usr/local/bin/musl-clang +COPY musl-clang++.sh /usr/local/bin/musl-clang++ +RUN chmod +x /usr/local/bin/musl-clang /usr/local/bin/musl-clang++ diff --git a/docker/musl-build-env/Toolchain.cmake.aarch64 b/docker/musl-build-env/Toolchain.cmake.aarch64 new file mode 100644 index 00000000..42dca1bd --- /dev/null +++ b/docker/musl-build-env/Toolchain.cmake.aarch64 @@ -0,0 +1,28 @@ +# vim: set ft=cmake: +set(sysroot /sysroot/aarch64-none-linux-musl) +set(arch aarch64) +set(interpreter ld-musl-aarch64.so.1) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR ${arch}) +set(CMAKE_SYSROOT ${sysroot}) +set(CMAKE_AR /usr/bin/llvm-ar) +set(triple ${arch}-none-linux-musl) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) +set(CMAKE_C_COMPILER /usr/bin/clang) +set(CMAKE_C_COMPILER_TARGET ${triple}) + +set(c_cxx_flags "-Qunused-arguments -rtlib=compiler-rt -unwindlib=libunwind -static-libgcc -fno-omit-frame-pointer") +set(CMAKE_C_FLAGS_INIT ${c_cxx_flags}) +set(CMAKE_CXX_COMPILER /usr/bin/clang++) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++ ${c_cxx_flags}") + +set(linker_flags "-fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${sysroot}/usr/lib/libclang_rt.builtins.a -lunwind -lglibc_compat -Wl,-Bdynamic ${sysroot}/usr/lib/libclang_rt.builtins.a -Wl,--dynamic-linker,${sysroot}/lib/${interpreter} -Wl,-rpath=${sysroot} -resource-dir ${sysroot}/usr/lib/resource_dir") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${linker_flags} -Wl,--dynamic-linker,${sysroot}/lib/${interpreter}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT ${linker_flags}) +set(CMAKE_C_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") +set(CMAKE_CXX_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") + +set(CMAKE_NM /usr/bin/llvm-nm) +set(CMAKE_RANLIB /usr/bin/llvm-ranlib) +set(CMAKE_STRIP /usr/bin/strip) diff --git a/docker/musl-build-env/Toolchain.cmake.x86_64 b/docker/musl-build-env/Toolchain.cmake.x86_64 new file mode 100644 index 00000000..029be042 --- /dev/null +++ b/docker/musl-build-env/Toolchain.cmake.x86_64 @@ -0,0 +1,28 @@ +# vim: set ft=cmake: +set(sysroot /sysroot/x86_64-none-linux-musl) +set(arch x86_64) +set(interpreter ld-musl-x86_64.so.1) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR ${arch}) +set(CMAKE_SYSROOT ${sysroot}) +set(CMAKE_AR /usr/bin/llvm-ar) +set(triple ${arch}-none-linux-musl) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) +set(CMAKE_C_COMPILER /usr/bin/clang) +set(CMAKE_C_COMPILER_TARGET ${triple}) + +set(c_cxx_flags "-Qunused-arguments -rtlib=compiler-rt -unwindlib=libunwind -static-libgcc -fno-omit-frame-pointer") +set(CMAKE_C_FLAGS_INIT ${c_cxx_flags}) +set(CMAKE_CXX_COMPILER /usr/bin/clang++) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_FLAGS_INIT "-stdlib=libc++ ${c_cxx_flags}") + +set(linker_flags "-fuse-ld=lld -nodefaultlibs -Wl,-Bstatic -lc++ -lc++abi ${sysroot}/usr/lib/libclang_rt.builtins.a -lunwind -lglibc_compat -Wl,-Bdynamic ${sysroot}/usr/lib/libclang_rt.builtins.a -Wl,--dynamic-linker,${sysroot}/lib/${interpreter} -Wl,-rpath=${sysroot} -resource-dir ${sysroot}/usr/lib/resource_dir") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${linker_flags} -Wl,--dynamic-linker,${sysroot}/lib/${interpreter}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT ${linker_flags}) +set(CMAKE_C_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") +set(CMAKE_CXX_STANDARD_LIBRARIES "-Wl,-Bdynamic -lc") + +set(CMAKE_NM /usr/bin/llvm-nm) +set(CMAKE_RANLIB /usr/bin/llvm-ranlib) +set(CMAKE_STRIP /usr/bin/strip) diff --git a/docker/musl-build-env/alltypes.h.diff b/docker/musl-build-env/alltypes.h.diff new file mode 100644 index 00000000..967c1b29 --- /dev/null +++ b/docker/musl-build-env/alltypes.h.diff @@ -0,0 +1,60 @@ +--- bits/alltypes.h ++++ bits/alltypes.h +@@ -299,17 +299,32 @@ + #endif + + #if defined(__NEED_pthread_mutexattr_t) && !defined(__DEFINED_pthread_mutexattr_t) +-typedef struct { unsigned __attr; } pthread_mutexattr_t; ++typedef struct { union { unsigned __attr; ++#ifdef __aarch64__ ++ long __glibc_compat; ++#endif ++}; ++} pthread_mutexattr_t; + #define __DEFINED_pthread_mutexattr_t + #endif + + #if defined(__NEED_pthread_condattr_t) && !defined(__DEFINED_pthread_condattr_t) +-typedef struct { unsigned __attr; } pthread_condattr_t; ++typedef struct { union { unsigned __attr; ++#ifdef __aarch64__ ++ long __glibc_compat; ++#endif ++}; ++} pthread_condattr_t; + #define __DEFINED_pthread_condattr_t + #endif + + #if defined(__NEED_pthread_barrierattr_t) && !defined(__DEFINED_pthread_barrierattr_t) +-typedef struct { unsigned __attr; } pthread_barrierattr_t; ++typedef struct { union { unsigned __attr; ++#ifdef __aarch64__ ++ long __glibc_compat; ++#endif ++}; ++} pthread_barrierattr_t; + #define __DEFINED_pthread_barrierattr_t + #endif + +@@ -383,12 +398,20 @@ + + + #if defined(__NEED_pthread_attr_t) && !defined(__DEFINED_pthread_attr_t) +-typedef struct { union { int __i[sizeof(long)==8?14:9]; volatile int __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; } __u; } pthread_attr_t; ++typedef struct { union { int __i[sizeof(long)==8?14:9]; volatile int __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; ++#ifdef __aarch64__ ++ char __glibc_compat[64]; ++#endif ++} __u; } pthread_attr_t; + #define __DEFINED_pthread_attr_t + #endif + + #if defined(__NEED_pthread_mutex_t) && !defined(__DEFINED_pthread_mutex_t) +-typedef struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; } __u; } pthread_mutex_t; ++typedef struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; ++#ifdef __aarch64__ ++ char __glibc_compat[48]; ++#endif ++} __u; } pthread_mutex_t; + #define __DEFINED_pthread_mutex_t + #endif diff --git a/docker/musl-build-env/glibc_compat.c b/docker/musl-build-env/glibc_compat.c new file mode 100644 index 00000000..be843c17 --- /dev/null +++ b/docker/musl-build-env/glibc_compat.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) && !defined(__GLIBC__) + +# ifdef __x86_64__ +float ceilf(float x) +{ + float result; + // NOLINTNEXTLINE(hicpp-no-assembler) + __asm__("roundss $0x0A, %[x], %[result]" + : [result] "=x"(result) + : [x] "x"(x)); + return result; +} +double ceil(double x) +{ + double result; + // NOLINTNEXTLINE(hicpp-no-assembler) + __asm__("roundsd $0x0A, %[x], %[result]" + : [result] "=x"(result) + : [x] "x"(x)); + return result; +} +# endif + +# ifdef __aarch64__ +float ceilf(float x) +{ + float result; + __asm__("frintp %s0, %s1\n" : "=w"(result) : "w"(x)); + return result; +} +double ceil(double x) +{ + double result; + __asm__("frintp %d0, %d1\n" : "=w"(result) : "w"(x)); + return result; +} +# endif + +# ifdef __aarch64__ +# define _STAT_VER 0 +# else +# define _STAT_VER 1 +# endif + +// glibc before 2.33 (2021) doesn't have these +int stat(const char *restrict path, void *restrict buf) +{ + int __xstat(int, const char *restrict, void *restrict); + return __xstat(_STAT_VER, path, buf); +} + +int fstat(int fd, void *buf) +{ + int __fxstat(int, int, void *); + return __fxstat(_STAT_VER, fd, buf); +} + +int lstat(const char *restrict path, void *restrict buf) +{ + int __lxstat(int, const char *restrict, void *restrict); + return __lxstat(_STAT_VER, path, buf); +} + +int fstatat(int dirfd, const char *restrict pathname, void *restrict statbuf, int flags) +{ + int __fxstatat(int, int, const char *restrict, void *restrict, int); + return __fxstatat(_STAT_VER, dirfd, pathname, statbuf, flags); +} + +// glibc doesn't define pthread_atfork on aarch64. We need to delegate to +// glibc's __register_atfork() instead. __register_atfork() takes an extra +// argument, __dso_handle, which is a pointer to the DSO that is registering the +// fork handlers. This is used to ensure that the handlers are not called after +// the DSO is unloaded. glibc on amd64 also implements pthread_atfork() in terms +// of __register_atfork(). (musl never unloads modules so that potential +// problem doesn't exist) + +// On amd64, even though pthread_atfork is exported by glibc, it should not be +// used. Code that uses pthread_atfork will compile to an import to +// __register_atfork(), but here we're compiling against musl, resulting in an +// an import to pthread_atfork. This will cause a runtime error after the test +// that unloads our module. The reason is that when we call pthread_atfork in +// glibc, __register_atfork() is called with the __dso_handle of libc6.so, not +// the __dso_handle of our module. So the fork handler is not unregistered when +// our module is unloaded. + +extern void *__dso_handle __attribute__((weak)); +int __register_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void), void *__dso_handle) __attribute__((weak)); + +int pthread_atfork( + void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + // glibc + if (__dso_handle && __register_atfork) { + return __register_atfork(prepare, parent, child, __dso_handle); + } + + static int (*real_atfork)(void (*)(void), void (*)(void), void (*)(void)); + + if (!real_atfork) { + // dlopen musl +# ifdef __aarch64__ + void *handle = dlopen("ld-musl-aarch64.so.1", RTLD_LAZY); + if (!handle) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlopen of ld-musl-aarch64.so.1 failed: %s\n", + dlerror()); + abort(); + } +# else + void *handle = dlopen("libc.musl-x86_64.so.1", RTLD_LAZY); + if (!handle) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlopen of libc.musl-x86_64.so.1 failed: %s\n", + dlerror()); + abort(); + } +# endif + real_atfork = dlsym(handle, "pthread_atfork"); + if (!real_atfork) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlsym of pthread_atfork failed: %s\n", dlerror()); + abort(); + } + } + + return real_atfork(prepare, parent, child); +} + +# ifdef __x86_64__ +struct pthread_cond; +struct pthread_condattr; +typedef struct pthread_cond pthread_cond_t; +typedef struct pthread_condattr pthread_condattr_t; + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) +{ + static int (*real_pthread_cond_init)(pthread_cond_t *cond, const pthread_condattr_t *cond_attr); + + if (!real_pthread_cond_init) { + void *handle = dlopen("libc.so.6", RTLD_LAZY); + if (!handle) { + void *handle = dlopen("libc.musl-x86_64.so.1", RTLD_LAZY); + if (!handle) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlopen of libc.so.6 and libc.musl-x86_64.so.1 failed: %s\n", + dlerror()); + abort(); + } + } + + real_pthread_cond_init = dlsym(handle, "pthread_cond_init"); + if (!real_pthread_cond_init) { + (void)fprintf( + // NOLINTNEXTLINE(concurrency-mt-unsafe) + stderr, "dlsym of pthread_cond_init failed: %s\n", dlerror()); + abort(); + } + } + + return real_pthread_cond_init(cond, cond_attr); +} +# endif + +// the symbol strerror_r in glibc is not the POSIX version; it returns char * +// __xpg_sterror_r is exported by both glibc and musl +int strerror_r(int errnum, char *buf, size_t buflen) +{ + int __xpg_strerror_r(int, char *, size_t); + return __xpg_strerror_r(errnum, buf, buflen); +} + +// when compiling with --coverage, some references to atexit show up. +// glibc doesn't provide atexit for similar reasons as pthread_atfork presumably +int __cxa_atexit(void (*func)(void *), void *arg, void *dso_handle); +int atexit(void (*function)(void)) +{ + if (!__dso_handle) { + (void)fprintf(stderr, "Aborting because __dso_handle is NULL\n"); + abort(); + } + + // the cast is harmless on amd64 and aarch64. Passing an extra argument to a + // function that expects none causes no problems + return __cxa_atexit((void (*)(void *))function, 0, __dso_handle); +} + +// introduced in glibc 2.25 +ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { + int fd; + size_t bytes_read = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return -1; + } + + while (bytes_read < buflen) { + ssize_t result = read(fd, (char*)buf + bytes_read, buflen - bytes_read); + if (result < 0) { + if (errno == EINTR) { + continue; + } + close(fd); + return -1; + } + bytes_read += result; + } + + close(fd); + return (ssize_t)bytes_read; +} + +#ifdef __x86_64__ +#define MEMFD_CREATE_SYSCALL 319 +#elif __aarch64__ +#define MEMFD_CREATE_SYSCALL 279 +#endif + +// introduced in glibc 2.27 +int memfd_create(const char *name, unsigned flags) { + return syscall(MEMFD_CREATE_SYSCALL, name, flags); +} + +// __flt_rounds is a musl internal that returns the FLT_ROUNDS value. +// glibc doesn't export it; fegetround() provides the same information. +int __flt_rounds(void) +{ + switch (fegetround()) { + case FE_TONEAREST: return 1; + case FE_UPWARD: return 2; + case FE_DOWNWARD: return 3; + case FE_TOWARDZERO: return 0; + default: return -1; + } +} + +// glibc 2.39 no longer exports sigsetjmp as a dynamic symbol (it became a +// compiler builtin / inlined via headers). Only __sigsetjmp is exported. +// Musl-compiled code references sigsetjmp directly, so we bridge to __sigsetjmp. +int sigsetjmp(sigjmp_buf env, int savemask) +{ + int __sigsetjmp(sigjmp_buf, int); + return __sigsetjmp(env, savemask); +} + +// glibc 2.39 no longer exports res_init as a dynamic symbol. Only __res_init +// is exported. Musl-compiled code references res_init directly. +// __res_init is declared weak so linking succeeds on musl (where it doesn't +// exist). At runtime: on glibc it's non-NULL and called directly; on musl +// it's NULL and we fall through to dlopen musl's own res_init. +extern int __res_init(void) __attribute__((weak)); + +int res_init(void) +{ + if (__res_init) + return __res_init(); + + // musl path: find res_init in musl's libc via dlopen + static int (*musl_res_init)(void); + if (!musl_res_init) { +# ifdef __aarch64__ + void *handle = dlopen("ld-musl-aarch64.so.1", RTLD_LAZY); +# else + void *handle = dlopen("libc.musl-x86_64.so.1", RTLD_LAZY); +# endif + if (handle) + musl_res_init = dlsym(handle, "res_init"); + } + if (musl_res_init) + return musl_res_init(); + return 0; +} + +#endif diff --git a/docker/musl-build-env/locale.h.diff b/docker/musl-build-env/locale.h.diff new file mode 100644 index 00000000..408367ec --- /dev/null +++ b/docker/musl-build-env/locale.h.diff @@ -0,0 +1,11 @@ +-- locale.h ++++ locale.h +@@ -71,7 +71,7 @@ + #define LC_COLLATE_MASK (1< --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- diff --git a/tests/012.phpt b/tests/012.phpt index 65474d74..ddf92b97 100644 --- a/tests/012.phpt +++ b/tests/012.phpt @@ -1,7 +1,5 @@ --TEST-- RarEntry::getStream() function (bad RAR file) ---SKIPIF-- - --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- diff --git a/tests/020.phpt b/tests/020.phpt index 8a2006f6..6eaab848 100644 --- a/tests/020.phpt +++ b/tests/020.phpt @@ -2,7 +2,6 @@ RarEntry::getStream() function (solid archive) --SKIPIF-- --FILE-- diff --git a/tests/021.phpt b/tests/021.phpt index 1323e542..285a0715 100644 --- a/tests/021.phpt +++ b/tests/021.phpt @@ -1,7 +1,5 @@ --TEST-- RarEntry::isDirectory() basic test ---SKIPIF-- - --FILE-- --FILE-- --FILE-- --FILE-- should fail (no password):\n"; diff --git a/tests/025.phpt b/tests/025.phpt index cf789671..ee70c062 100644 --- a/tests/025.phpt +++ b/tests/025.phpt @@ -1,7 +1,5 @@ --TEST-- rar_open()/RarEntry::extract() (headers level password) ---SKIPIF-- - --FILE-- --FILE-- should fail (no password):\n"; diff --git a/tests/027.phpt b/tests/027.phpt index 8f104c64..ab7133bb 100644 --- a/tests/027.phpt +++ b/tests/027.phpt @@ -1,7 +1,5 @@ --TEST-- RarEntry::getStream() with Linux directories and links ---SKIPIF-- - --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- diff --git a/tests/047.phpt b/tests/047.phpt index 4a8b2a26..35863919 100644 --- a/tests/047.phpt +++ b/tests/047.phpt @@ -2,7 +2,6 @@ RarEntry::extract() function (broken set fixed with volume callback) --SKIPIF-- --FILE-- diff --git a/tests/048.phpt b/tests/048.phpt index 3b96b24c..933c03ce 100644 --- a/tests/048.phpt +++ b/tests/048.phpt @@ -2,7 +2,6 @@ RarArchive::open() volume callback long return (case MAXPATHLEN <= NM) --SKIPIF-- MAXPATHSIZE) --SKIPIF-- MAXPATHSIZE)) diff --git a/tests/050.phpt b/tests/050.phpt index 8b0c4e7a..49ae467d 100644 --- a/tests/050.phpt +++ b/tests/050.phpt @@ -1,8 +1,5 @@ --TEST-- Stream wrapper basic test ---SKIPIF-- - --CLEAN-- @unlink(dirname(__FILE__).'/extract_temp'); --FILE-- diff --git a/tests/067.phpt b/tests/067.phpt index cc28a583..b535b4f8 100644 --- a/tests/067.phpt +++ b/tests/067.phpt @@ -1,7 +1,7 @@ --TEST-- RarEntry::extract() process extended (Windows) --SKIPIF-- - --FILE-- --FILE-- --FILE-- --FILE-- --CLEAN-- --FILE-- --CLEAN-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- diff --git a/tests/087.phpt b/tests/087.phpt index 3b9894eb..27f555bd 100644 --- a/tests/087.phpt +++ b/tests/087.phpt @@ -1,7 +1,5 @@ --TEST-- RarArchive read_property gives a fatal error on a write context ---SKIPIF-- - --FILE-- --FILE-- diff --git a/tests/089.phpt b/tests/089.phpt index 562567c1..0a8ea981 100644 --- a/tests/089.phpt +++ b/tests/089.phpt @@ -1,7 +1,5 @@ --TEST-- RarArchive unset_property gives a fatal error ---SKIPIF-- - --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --CLEAN-- --CLEAN-- --FILE-- --FILE-- = 7"); --FILE-- = 7"); --FILE-- = 7"); --FILE-- --FILE-- From 755f36602eb807a52671bb5a4802bae65cfd5cb2 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 15 Mar 2026 00:34:25 +0000 Subject: [PATCH 73/76] Linux builds for releases (8.1+) --- .github/docker-image-shas.yml | 4 ++ .github/scripts/update-docker-shas.sh | 8 ++-- .github/workflows/release.yml | 68 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/.github/docker-image-shas.yml b/.github/docker-image-shas.yml index 3c80857c..976f8ac1 100644 --- a/.github/docker-image-shas.yml +++ b/.github/docker-image-shas.yml @@ -16,13 +16,17 @@ ghcr.io/cataphract/php-minimal: 8.0-debug: "sha256:d46c15e648497cf55bb6b4806c726c58714f5b63fcb1096e4a8ec433ec17c835" 8.0-release-zts: "sha256:bcbaab72e7c0e704d7bdef01e8f91e1bc0636e6e98c0ab68cc2fc417e7a98a66" 8.1-debug: "sha256:dbdb437df0a4acbbd11e020461f7cef2f0e949f46f9ca841c5f058012ec7cc98" + 8.1-release: "sha256:f401dba3b4249dc16acb90909951b88e3146dcd2e622203d95e80219258932c9" 8.1-release-zts: "sha256:2b330ce54679f29e09ea77c984cd409618142cc20cab48b5a114cc1e5b09257c" 8.2-debug: "sha256:efecf1cdbd09511aec12897a03682934243498c5bb2ba37874ae2100c1498011" + 8.2-release: "sha256:e09be007096a29d64427afa99ea4d10f7e1a91a8e2129aeaa854daf045986d8f" 8.2-release-zts: "sha256:d67d1c370abdc5e629a2bec1ff4d308f0ccb7d3cbc7ecc3c7eee851cd6c2efaa" 8.3-debug: "sha256:1b8e6ba17c837a4035f6981dd38f9f447be57a3d558ca8f18071c8390a62ce6c" 8.3-release: "sha256:23abac21af8c95ccd6cd6a8b5fbb2d1dd67e122fc7630bbbfcc7b75e6a6fcc96" 8.3-release-zts: "sha256:158060357f81b495a5420b1f9d488abd61d59306946b352e4fbfc4011b7a2d89" 8.4-debug: "sha256:c863e2a60f144856f56d50ed1080bbe0377ca99760195f170c762ad46f0466e5" + 8.4-release: "sha256:571251d31267c6960846727f0788f1d9a593a02adf6a68899a0582c5635d5c50" 8.4-release-zts: "sha256:6451c7104d874f8c4f7e0305e5baaecebbd74672a036c02de2cbb9d1a10c8906" 8.5-debug: "sha256:014a16949dd0da0581cda6768bd7751f66b6ada3becdc949aeb5b8e1efb3c5cf" + 8.5-release: "sha256:bc58405b860c17ea5f36cbd466dc5c93b97283c148e3489ec78171d73727859c" 8.5-release-zts: "sha256:dadd96c4740bc9606f2985be51fb0b85d768132f93786c4ae52940552f3cbdc6" diff --git a/.github/scripts/update-docker-shas.sh b/.github/scripts/update-docker-shas.sh index 6fd441fa..c7b0e295 100755 --- a/.github/scripts/update-docker-shas.sh +++ b/.github/scripts/update-docker-shas.sh @@ -16,11 +16,11 @@ TAGS=( 7.3-debug 7.3-release-zts 7.4-debug 7.4-release-zts 8.0-debug 8.0-release-zts - 8.1-debug 8.1-release-zts - 8.2-debug 8.2-release-zts + 8.1-debug 8.1-release 8.1-release-zts + 8.2-debug 8.2-release 8.2-release-zts 8.3-debug 8.3-release 8.3-release-zts - 8.4-debug 8.4-release-zts - 8.5-debug 8.5-release-zts + 8.4-debug 8.4-release 8.4-release-zts + 8.5-debug 8.5-release 8.5-release-zts ) get_index_digest() { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1efa0f9..547d4f82 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,6 +55,74 @@ jobs: GH_TOKEN: ${{ github.token }} run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --draft --notes-from-tag + linux-extension-matrix: + needs: [create-draft-release] + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + - id: set-matrix + run: | + python3 <<'EOF' + import yaml, json, os + with open('.github/docker-image-shas.yml') as f: + data = yaml.safe_load(f) + image = 'ghcr.io/cataphract/php-minimal' + includes = [] + for tag, sha in data[image].items(): + ver, variant = tag.split('-', 1) + if variant not in ('release', 'release-zts'): + continue + if float(ver) < 8.1: + continue + for arch, runner in [('x86_64', 'ubuntu-latest'), ('aarch64', 'ubuntu-24.04-arm')]: + includes.append({'php': ver, 'variant': variant, 'image_sha': sha, + 'arch': arch, 'runner': runner}) + with open(os.environ['GITHUB_OUTPUT'], 'a') as f: + f.write('matrix=' + json.dumps({'include': includes}) + '\n') + EOF + + linux-build: + name: Linux PHP ${{ matrix.php }} (${{ matrix.variant }}, ${{ matrix.arch }}) + needs: [linux-extension-matrix] + runs-on: ${{ matrix.runner }} + permissions: + contents: write + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.linux-extension-matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v4 + + - name: Install patchelf + run: sudo apt-get install -y patchelf + + - name: Build rar.so in musl container + run: | + docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -w /workspace \ + "ghcr.io/cataphract/php-minimal@${{ matrix.image_sha }}" \ + sh -c 'phpize && ./configure --with-php-config=$(which php-config) && make -j$(nproc)' + + - name: Remove musl DT_NEEDED from rar.so + run: patchelf --remove-needed "libc.musl-$(uname -m).so.1" modules/rar.so + + - name: Package and upload to release + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION="${GITHUB_REF_NAME#v}" + ARCH="${{ matrix.arch }}" + [ "$ARCH" = "aarch64" ] && ARCH="arm64" + ZTS_SUFFIX="" + [[ "${{ matrix.variant }}" == "release-zts" ]] && ZTS_SUFFIX="-zts" + ARTIFACT="php_rar-${VERSION}_php${{ matrix.php }}-${ARCH}-linux${ZTS_SUFFIX}.zip" + zip -j "$ARTIFACT" modules/rar.so + gh release upload "${{ github.ref_name }}" "$ARTIFACT" --clobber + windows-extension-matrix: needs: [create-draft-release] runs-on: ubuntu-latest From d28125cc547b55a51eed1245d65be69c8292fa4d Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 15 Mar 2026 00:49:07 +0000 Subject: [PATCH 74/76] Release v4.3.1 --- package.xml | 28 ++++++++++++++++++++++------ php_rar.h | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/package.xml b/package.xml index adb05acc..599f4c7b 100644 --- a/package.xml +++ b/package.xml @@ -23,10 +23,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> no - 2026-03-08 + 2026-03-15 - 4.3.0 + 4.3.1 4.0.0 @@ -36,10 +36,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP License - - Add PHP 8.1, 8.2, 8.3, 8.4, 8.5 support. -- Drop PHP 5 support; minimum PHP version is now 7.0. -- Update bundled unrar to 7.2.4. -- Fix Windows bug + - Fix bug #75557: error opening archive with non-English characters in path on Windows. +- Do not build unconditionally against listdc++. @@ -157,6 +155,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -375,6 +375,22 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + 4.3.1 + 4.0.0 + + + stable + stable + + 2026-03-15 + Changes in this version: +- Fix bug #75557: error opening archive with non-English characters in path on Windows. +- Do not build unconditionally against listdc++. + + + 4.3.0 diff --git a/php_rar.h b/php_rar.h index ac5d4bc0..fc834145 100644 --- a/php_rar.h +++ b/php_rar.h @@ -51,7 +51,7 @@ extern zend_module_entry rar_module_entry; #define phpext_rar_ptr &rar_module_entry -#define PHP_RAR_VERSION "4.3.1-dev" +#define PHP_RAR_VERSION "4.3.1" #ifdef PHP_WIN32 #define PHP_RAR_API __declspec(dllexport) From f1acbb626582b1943ff5764d8e8956c9cc5c84e7 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Sun, 15 Mar 2026 01:04:07 +0000 Subject: [PATCH 75/76] Bump version to 4.3.2-dev --- php_rar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_rar.h b/php_rar.h index fc834145..98793739 100644 --- a/php_rar.h +++ b/php_rar.h @@ -51,7 +51,7 @@ extern zend_module_entry rar_module_entry; #define phpext_rar_ptr &rar_module_entry -#define PHP_RAR_VERSION "4.3.1" +#define PHP_RAR_VERSION "4.3.2-dev" #ifdef PHP_WIN32 #define PHP_RAR_API __declspec(dllexport) From d049646ee7654111e956258673a8e0c0654f18a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Mon, 16 Mar 2026 15:54:16 +0000 Subject: [PATCH 76/76] Correct comments on glibc_compat.c; call abort() in res_init --- docker/musl-build-env/glibc_compat.c | 53 ++++++++++++++++++---------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/docker/musl-build-env/glibc_compat.c b/docker/musl-build-env/glibc_compat.c index be843c17..3ae15464 100644 --- a/docker/musl-build-env/glibc_compat.c +++ b/docker/musl-build-env/glibc_compat.c @@ -87,11 +87,11 @@ int fstatat(int dirfd, const char *restrict pathname, void *restrict statbuf, in // On amd64, even though pthread_atfork is exported by glibc, it should not be // used. Code that uses pthread_atfork will compile to an import to // __register_atfork(), but here we're compiling against musl, resulting in an -// an import to pthread_atfork. This will cause a runtime error after the test -// that unloads our module. The reason is that when we call pthread_atfork in -// glibc, __register_atfork() is called with the __dso_handle of libc6.so, not -// the __dso_handle of our module. So the fork handler is not unregistered when -// our module is unloaded. +// an import to pthread_atfork. This will cause a runtime error when unloading +// a shared module. The reason is that when we call pthread_atfork in glibc, +// __register_atfork() is called with the __dso_handle of libc6.so, not the +// __dso_handle of our module. So the fork handler is not unregistered when our +// module is unloaded. extern void *__dso_handle __attribute__((weak)); int __register_atfork(void (*prepare)(void), void (*parent)(void), @@ -236,8 +236,14 @@ int memfd_create(const char *name, unsigned flags) { return syscall(MEMFD_CREATE_SYSCALL, name, flags); } -// __flt_rounds is a musl internal that returns the FLT_ROUNDS value. -// glibc doesn't export it; fegetround() provides the same information. +// __flt_rounds is the backing function behind musl's FLT_ROUNDS macro: musl's +// defines FLT_ROUNDS as (__flt_rounds()), making it dynamic — it +// reflects the current rounding mode after fesetround(). glibc/GCC does not +// export __flt_rounds; GCC's defines FLT_ROUNDS as the compile-time +// constant 1 (round-to-nearest), so it never tracks fesetround() at all (GCC +// bug #59046 — GCC's own float.h has the comment "??? This is supposed to +// change with calls to fesetround in "). fegetround() provides the +// actual hardware rounding mode on both. int __flt_rounds(void) { switch (fegetround()) { @@ -249,20 +255,23 @@ int __flt_rounds(void) } } -// glibc 2.39 no longer exports sigsetjmp as a dynamic symbol (it became a -// compiler builtin / inlined via headers). Only __sigsetjmp is exported. -// Musl-compiled code references sigsetjmp directly, so we bridge to __sigsetjmp. +// glibc has never exported sigsetjmp as a dynamic symbol — it is defined in +// as a macro expanding to __sigsetjmp, so only __sigsetjmp appears +// in the DSO. Musl exports both sigsetjmp ajd __sigsetjmp as real symbols. +// sigsetjmp is referenced directly in compiled code, so we provide this +// bridge. int sigsetjmp(sigjmp_buf env, int savemask) { int __sigsetjmp(sigjmp_buf, int); return __sigsetjmp(env, savemask); } -// glibc 2.39 no longer exports res_init as a dynamic symbol. Only __res_init -// is exported. Musl-compiled code references res_init directly. -// __res_init is declared weak so linking succeeds on musl (where it doesn't -// exist). At runtime: on glibc it's non-NULL and called directly; on musl -// it's NULL and we fall through to dlopen musl's own res_init. +// glibc no longer exports res_init as a dynamic symbol (it never did for amd64 +// or aarch64, only for older archs). Only __res_init is exported. +// Musl-compiled code references res_init directly. __res_init is declared weak +// so linking succeeds on musl (where it doesn't exist). At runtime: on glibc +// it's non-NULL and called directly; on musl it's NULL and we fall through to +// dlopen musl's own res_init. extern int __res_init(void) __attribute__((weak)); int res_init(void) @@ -278,12 +287,20 @@ int res_init(void) # else void *handle = dlopen("libc.musl-x86_64.so.1", RTLD_LAZY); # endif - if (handle) + if (handle) { musl_res_init = dlsym(handle, "res_init"); + } else { + (void)fprintf(stderr, "Aborting because dlopen() of musl failed\n"); + abort(); + } } - if (musl_res_init) + if (musl_res_init) { return musl_res_init(); - return 0; + } else { + (void)fprintf(stderr, "Aborting because res_init/__res_init could not " + "be found"); + abort(); + } } #endif