From 839151eaaad24771892afaae6bac690e31e58384 Mon Sep 17 00:00:00 2001 From: Igor Pavlov <87184205+ip7z@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:00:00 +0000 Subject: 26.00 --- CPP/7zip/7zip_gcc.mak | 4 +- CPP/7zip/Archive/7z/7zUpdate.cpp | 2 +- CPP/7zip/Archive/ComHandler.cpp | 1324 ++++++++++++++++++++--------- CPP/7zip/Archive/CpioHandler.cpp | 2 +- CPP/7zip/Archive/QcowHandler.cpp | 5 +- CPP/7zip/Archive/Rar/Rar5Handler.cpp | 11 +- CPP/7zip/Archive/Rar/RarHandler.cpp | 22 +- CPP/7zip/Archive/Tar/TarHandler.cpp | 35 +- CPP/7zip/Archive/Tar/TarIn.cpp | 147 ++-- CPP/7zip/Archive/Tar/TarItem.h | 1 + CPP/7zip/Archive/Udf/UdfIn.cpp | 18 +- CPP/7zip/Archive/Udf/UdfIn.h | 7 +- CPP/7zip/Archive/Zip/ZipIn.cpp | 90 +- CPP/7zip/Archive/Zip/ZipOut.cpp | 44 +- CPP/7zip/Bundles/SFXCon/SfxCon.cpp | 2 +- CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp | 50 +- CPP/7zip/Common/FileStreams.cpp | 2 +- CPP/7zip/Common/FileStreams.h | 4 +- CPP/7zip/UI/Common/ArchiveCommandLine.cpp | 2 +- CPP/7zip/UI/Common/ArchiveName.cpp | 4 +- CPP/7zip/UI/Common/Bench.cpp | 9 +- CPP/7zip/UI/Console/List.cpp | 4 +- CPP/7zip/UI/FileManager/BrowseDialog2.cpp | 94 +- CPP/7zip/UI/FileManager/FSFolder.cpp | 8 +- CPP/7zip/UI/FileManager/LangPage.cpp | 3 +- CPP/7zip/UI/FileManager/MenuPage.cpp | 3 +- CPP/7zip/UI/FileManager/PanelItemOpen.cpp | 25 +- CPP/7zip/UI/FileManager/PanelMenu.cpp | 4 +- CPP/7zip/UI/FileManager/PanelSort.cpp | 55 +- CPP/7zip/UI/GUI/BenchmarkDialog.cpp | 55 +- CPP/7zip/UI/GUI/CompressDialog.cpp | 129 ++- CPP/7zip/UI/GUI/CompressDialog.rc | 4 +- CPP/7zip/UI/GUI/ExtractDialog.cpp | 3 +- CPP/Common/Common0.h | 5 +- CPP/Common/MyBuffer.h | 48 +- CPP/Windows/Control/ComboBox.cpp | 9 + CPP/Windows/Control/ComboBox.h | 2 + CPP/Windows/FileFind.cpp | 13 +- CPP/Windows/FileFind.h | 6 +- CPP/Windows/SecurityUtils.h | 4 + CPP/Windows/System.cpp | 10 +- CPP/Windows/System.h | 23 +- CPP/Windows/SystemInfo.cpp | 4 +- CPP/Windows/TimeUtils.h | 8 + 44 files changed, 1494 insertions(+), 810 deletions(-) (limited to 'CPP') diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak index 12f1ef2..a78c0fa 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -142,8 +142,8 @@ MY_MKDIR=mkdir DEL_OBJ_EXE = -$(RM) $(O)\*.o $(O)\$(PROG).exe $(O)\$(PROG).dll endif -LIB2_GUI = -lOle32 -lGdi32 -lComctl32 -lComdlg32 -lShell32 $(LIB_HTMLHELP) -LIB2 = -loleaut32 -luuid -ladvapi32 -lUser32 $(LIB2_GUI) +LIB2_GUI = -lole32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 $(LIB_HTMLHELP) +LIB2 = -loleaut32 -luuid -ladvapi32 -luser32 $(LIB2_GUI) # v24.00: -DUNICODE and -D_UNICODE are defined in precompilation header files # CXXFLAGS_EXTRA = -DUNICODE -D_UNICODE diff --git a/CPP/7zip/Archive/7z/7zUpdate.cpp b/CPP/7zip/Archive/7z/7zUpdate.cpp index c8c5d26..6ba04a2 100644 --- a/CPP/7zip/Archive/7z/7zUpdate.cpp +++ b/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -721,7 +721,7 @@ static int CompareEmptyItems(const unsigned *p1, const unsigned *p2, void *param return (u1.IsDir && u1.IsAnti) ? -n : n; } -static const char *g_Exts = +static const char * const g_Exts = " 7z xz lzma ace arc arj bz tbz bz2 tbz2 cab deb gz tgz ha lha lzh lzo lzx pak rar rpm sit zoo" " zip jar ear war msi" " 3gp avi mov mpeg mpg mpe wmv" diff --git a/CPP/7zip/Archive/ComHandler.cpp b/CPP/7zip/Archive/ComHandler.cpp index 144369e..40a5349 100644 --- a/CPP/7zip/Archive/ComHandler.cpp +++ b/CPP/7zip/Archive/ComHandler.cpp @@ -2,41 +2,62 @@ #include "StdAfx.h" -#include "../../../C/Alloc.h" #include "../../../C/CpuArch.h" -#include "../../Common/IntToString.h" #include "../../Common/ComTry.h" -#include "../../Common/MyCom.h" -#include "../../Common/MyBuffer.h" -#include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" -#define Get16(p) GetUi16(p) -#define Get32(p) GetUi32(p) +#include "Common/ItemNameUtils.h" + +#define Get16(p) GetUi16a(p) +#define Get32(p) GetUi32a(p) + +// we don't expect to get deleted files in real files +// define Z7_COMPOUND_SHOW_DELETED for debug +// #define Z7_COMPOUND_SHOW_DELETED namespace NArchive { namespace NCom { +static const unsigned k_Long_path_level_limit = 256; + static const Byte kSignature[] = - { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; + { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; + +// encoded "[!]MsiPatchSequence" name in "msp" file +static const Byte k_Sequence_msp[] = + { 0x40, 0x48, 0x96, 0x45, 0x6c, 0x3e, 0xe4, 0x45, + 0xe6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41, + 0x37, 0x41, 0, 0 }; + +// encoded "MergeModule.CABinet" name in "msm" file +static const Byte k_Sequence_msm[] = + { 0x16, 0x42, 0xb5, 0x42, 0xa8, 0x3d, 0xf2, 0x41, + 0xf8, 0x43, 0xa8, 0x47, 0x8c, 0x3a, 0x0b, 0x43, + 0x31, 0x42, 0x37, 0x48, 0, 0 }; + +// static const Byte k_CLSID_AAF_V3[] = { 0x41, 0x41, 0x46, 0x42, 0x0d, 0x00, 0x4f, 0x4d, 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0xff }; +// static const Byte k_CLSID_AAF_V4[] = { 0x01, 0x02, 0x01, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x03, 0x02, 0x01, 0x01 }; -enum EType +enum EArcType { k_Type_Common, k_Type_Msi, k_Type_Msp, + k_Type_Msm, k_Type_Doc, k_Type_Ppt, - k_Type_Xls + k_Type_Xls, + k_Type_Aaf }; static const char * const kExtensions[] = @@ -44,35 +65,63 @@ static const char * const kExtensions[] = "compound" , "msi" , "msp" + , "msm" , "doc" , "ppt" , "xls" + , "aaf" }; namespace NFatID { - // static const UInt32 kFree = 0xFFFFFFFF; - static const UInt32 kEndOfChain = 0xFFFFFFFE; - // static const UInt32 kFatSector = 0xFFFFFFFD; - // static const UInt32 kMatSector = 0xFFFFFFFC; - static const UInt32 kMaxValue = 0xFFFFFFFA; + static const UInt32 kFree = 0xffffffff; + static const UInt32 kEndOfChain = 0xfffffffe; + static const UInt32 kFatSector = 0xfffffffd; + static const UInt32 k_DIF_SECT = 0xfffffffc; // double-indirect file allocation table (DIFAT) + static const UInt32 kMaxValue = 0xfffffffa; } namespace NItemType { - static const Byte kEmpty = 0; - static const Byte kStorage = 1; - // static const Byte kStream = 2; - // static const Byte kLockBytes = 3; - // static const Byte kProperty = 4; - static const Byte kRootStorage = 5; + static const unsigned kEmpty = 0; + static const unsigned kStorage = 1; + static const unsigned kStream = 2; + // static const unsigned kLockBytes = 3; + // static const unsigned kProperty = 4; + static const unsigned kRootStorage = 5; +} + +static const unsigned k_MiniSectorSizeBits = 6; +static const UInt32 k_LongStreamMinSize = 1 << 12; + +static const unsigned k_Msi_NumBits = 6; +static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits; +static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1; +static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1); +static const unsigned k_Msi_StartUnicodeChar = 0x3800; +static const unsigned k_Msi_SpecUnicodeChar = k_Msi_StartUnicodeChar + k_Msi_UnicodeRange; +// (k_Msi_SpecUnicodeChar == 0x4840) is used as special symbol that is used +// as first character in some names in dir entries +/* +static bool IsMsiName(const Byte *p) +{ + unsigned c = Get16(p); + c -= k_Msi_StartUnicodeChar; + return c <= k_Msi_UnicodeRange; +} +*/ + +Z7_FORCE_INLINE static bool IsLargeStream(UInt64 size) +{ + return size >= k_LongStreamMinSize; } static const unsigned kNameSizeMax = 64; +static const UInt32 k_Item_Level_Unused = (UInt32)0 - 1; struct CItem { - Byte Name[kNameSizeMax]; + Byte Name[kNameSizeMax]; // must be aligned for 2-bytes // UInt16 NameSize; // UInt32 Flags; FILETIME CTime; @@ -82,484 +131,792 @@ struct CItem UInt32 RightDid; UInt32 SonDid; UInt32 Sid; - Byte Type; + unsigned Type; // Byte : we use unsigned instead of Byte for alignment + + UInt32 Level; - bool IsEmpty() const { return Type == NItemType::kEmpty; } + bool IsEmptyType() const { return Type == NItemType::kEmpty; } bool IsDir() const { return Type == NItemType::kStorage || Type == NItemType::kRootStorage; } + bool IsStorage() const { return Type == NItemType::kStorage; } + + bool IsLevel_Unused() const { return Level == k_Item_Level_Unused; } - void Parse(const Byte *p, bool mode64bit); + // bool IsSpecMsiName() const { return Get16(Name) == k_Msi_SpecUnicodeChar; } + bool AreMsiChars() const + { + for (unsigned i = 0; i < kNameSizeMax; i += 2) + { + unsigned c = Get16(Name + i); + if (c == 0) + break; + c -= k_Msi_StartUnicodeChar; + if (c <= k_Msi_UnicodeRange) + return true; + } + return false; + } + bool Parse(const Byte *p, bool mode64bit); }; + +static const UInt32 k_Ref_Parent_Root = 0xffffffff; + struct CRef { - int Parent; - UInt32 Did; + UInt32 Parent; // index in Refs[] + UInt32 Did; // index in Items[] }; + class CDatabase { - CObjArray MiniSids; - - HRESULT AddNode(int parent, UInt32 did); - public: + CRecordVector Refs; + CObjectVector Items; CObjArray Fat; CObjArray Mat; - CObjectVector Items; - CRecordVector Refs; -private: - UInt32 NumSectorsInMiniStream; -public: - UInt32 MatSize; + CObjArray MiniSids; + UInt32 FatSize; + UInt32 MatSize; + UInt32 NumSectors_in_MiniStream; - UInt32 LongStreamMinSize; + // UInt32 LongStreamMinSize; unsigned SectorSizeBits; - unsigned MiniSectorSizeBits; Int32 MainSubfile; - EType Type; + EArcType Type; + + bool IsArc; + bool HeadersError; + // bool IsMsi; UInt64 PhySize; - UInt64 PhySize_Aligned; + UInt64 PhySize_Unaligned; + // UInt64 FreeSize; - bool IsNotArcType() const + IArchiveOpenCallback *OpenCallback; + UInt32 Callback_Cur; + +private: + /* + HRESULT IncreaseOpenTotal(UInt32 numSects) { - return - Type != k_Type_Msi && - Type != k_Type_Msp; + if (!OpenCallback) + return S_OK; + const UInt64 total = (UInt64)(Callback_Cur + numSects) << SectorSizeBits; + return OpenCallback->SetTotal(NULL, &total); } + */ + HRESULT AddNodes(); + HRESULT ReadSector(IInStream *inStream, Byte *buf, UInt32 sid); + HRESULT ReadIDs(IInStream *inStream, Byte *buf, UInt32 sid, UInt32 *dest); + HRESULT Check_Item(unsigned index); - void UpdatePhySize(UInt64 val, UInt64 val_Aligned) +public: + bool IsNotArcType() const { - if (PhySize < val) - PhySize = val; - if (PhySize_Aligned < val_Aligned) - PhySize_Aligned = val_Aligned; + return + Type != k_Type_Msi && + Type != k_Type_Msp && + Type != k_Type_Msm; } - HRESULT ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid); - HRESULT ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest); - - HRESULT Update_PhySize_WithItem(unsigned index); void Clear(); - bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; } UString GetItemPath(UInt32 index) const; UInt64 GetItemPackSize(UInt64 size) const { - const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; - return (size + mask) & ~mask; - } - - bool GetMiniCluster(UInt32 sid, UInt64 &res) const - { - const unsigned subBits = SectorSizeBits - MiniSectorSizeBits; - const UInt32 fid = sid >> subBits; - if (fid >= NumSectorsInMiniStream) - return false; - res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1)); - return true; + const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : k_MiniSectorSizeBits)) - 1; + return (size + mask) & ~(UInt64)mask; } HRESULT Open(IInStream *inStream); }; -HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid) +HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, UInt32 sid) { - const UInt64 end = ((UInt64)sid + 2) << sectorSizeBits; - UpdatePhySize(end, end); - RINOK(InStream_SeekSet(inStream, (((UInt64)sid + 1) << sectorSizeBits))) - return ReadStream_FALSE(inStream, buf, (size_t)1 << sectorSizeBits); + const unsigned sb = SectorSizeBits; + RINOK(InStream_SeekSet(inStream, ((UInt64)sid + 1) << sb)) + RINOK(ReadStream_FALSE(inStream, buf, (size_t)1 << sb)) + if (OpenCallback) + { + if ((++Callback_Cur & 0xfff) == 0) + { + const UInt64 processed = (UInt64)Callback_Cur << sb; + const UInt64 numFiles = Items.Size(); + RINOK(OpenCallback->SetCompleted(&numFiles, &processed)) + } + } + return S_OK; } -HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest) +HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, UInt32 sid, UInt32 *dest) { - RINOK(ReadSector(inStream, buf, sectorSizeBits, sid)) - const UInt32 sectorSize = (UInt32)1 << sectorSizeBits; - for (UInt32 t = 0; t < sectorSize; t += 4) + RINOK(ReadSector(inStream, buf, sid)) + const size_t sectorSize = (size_t)1 << SectorSizeBits; + for (size_t t = 0; t < sectorSize; t += 4) *dest++ = Get32(buf + t); return S_OK; } + +Z7_FORCE_INLINE static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) { ft->dwLowDateTime = Get32(p); ft->dwHighDateTime = Get32(p + 4); } -void CItem::Parse(const Byte *p, bool mode64bit) +bool CItem::Parse(const Byte *p, bool mode64bit) { memcpy(Name, p, kNameSizeMax); - // NameSize = Get16(p + 64); + unsigned i; + for (i = 0; i < kNameSizeMax; i += 2) + if (*(const UInt16 *)(const void *)(p + i) == 0) + break; +#if 0 // 1 : for debug : for more strict field check + { + for (unsigned k = i; k < kNameSizeMax; k += 2) + if (*(const UInt16 *)(const void *)(p + k) != 0) + return false; + } +#endif Type = p[66]; + // DOC: names are limited to 32 UTF-16 code points, including the terminating null character. + if (!IsEmptyType()) + if (i == kNameSizeMax || i + 2 != Get16(p + 64)) // NameLength + return false; + if (p[67] >= 2) // Color: 0 (red) or 1 (black) + return false; LeftDid = Get32(p + 68); RightDid = Get32(p + 72); SonDid = Get32(p + 76); - // Flags = Get32(p + 96); + // if (Get32(p + 96) == 0) return false; // State / Flags GetFileTimeFromMem(p + 100, &CTime); GetFileTimeFromMem(p + 108, &MTime); Sid = Get32(p + 116); Size = Get32(p + 120); + /* MS DOC: it is recommended that parsers ignore the most + significant 32 bits of this field in version 3 compound files */ if (mode64bit) Size |= ((UInt64)Get32(p + 124) << 32); + return true; } + void CDatabase::Clear() { + Type = k_Type_Common; + MainSubfile = -1; + IsArc = false; + HeadersError = false; + // IsMsi = false; PhySize = 0; - PhySize_Aligned = 0; - + PhySize_Unaligned = 0; + // FreeSize = 0; + Callback_Cur = 0; + // OpenCallback = NULL; + + FatSize = 0; + MatSize = 0; + NumSectors_in_MiniStream = 0; + Fat.Free(); - MiniSids.Free(); Mat.Free(); + MiniSids.Free(); Items.Clear(); Refs.Clear(); } -static const UInt32 kNoDid = 0xFFFFFFFF; -HRESULT CDatabase::AddNode(int parent, UInt32 did) +static const UInt32 kNoDid = 0xffffffff; + +HRESULT CDatabase::AddNodes() { - if (did == kNoDid) + UInt32 index = Items[0].SonDid; // Items[0] is root item + if (index == kNoDid) // no files case return S_OK; - if (did >= (UInt32)Items.Size()) - return S_FALSE; - const CItem &item = Items[did]; - if (item.IsEmpty()) - return S_FALSE; - CRef ref; - ref.Parent = parent; - ref.Did = did; - const unsigned index = Refs.Add(ref); - if (Refs.Size() > Items.Size()) + if (index == 0 || index >= Items.Size()) return S_FALSE; - RINOK(AddNode(parent, item.LeftDid)) - RINOK(AddNode(parent, item.RightDid)) - if (item.IsDir()) + + CObjArray itemParents(Items.Size()); + CByteArr states(Items.Size()); + memset(itemParents, 0, (size_t)Items.Size() * sizeof(itemParents[0])); // optional + memset(states, 0, Items.Size()); + +#if 1 // 0 : for debug + const UInt32 k_exitParent = 0; + const UInt32 k_startLevel = 1; + // we don't show "Root Entry" dir + states[0] = 3; // we mark root node as processed, also we block any cycle links to root node + // itemParents[0] = 0xffffffff; // optional / unused value +#else + // we show item[0] "Root Entry" dir + const UInt32 k_exitParent = 0xffffffff; + const UInt32 k_startLevel = 0; + index = 0; +#endif + + UInt32 level = k_startLevel; // directory level + unsigned state = 0; + UInt32 parent = k_exitParent; // in Items[], itemParents[], states[] + UInt32 refParent = k_Ref_Parent_Root; // in Refs[] + + for (;;) { - RINOK(AddNode((int)index, item.SonDid)) - } - return S_OK; -} + if (state >= 3) + { + // we return to parent node + if (state != 3) + return E_FAIL; + index = parent; + if (index == k_exitParent) + break; + if (index >= Items.Size()) + return E_FAIL; // (index) was checked already + parent = itemParents[index]; + state = states[index]; + if (state == 0) + return E_FAIL; + if (state == 2) + { + // we return to parent Dir node + if (refParent >= Refs.Size()) + return E_FAIL; + refParent = Refs[refParent].Parent; + level--; + } + continue; + } -static UString CompoundNameToFileName(const UString &s) -{ - UString res; - for (unsigned i = 0; i < s.Len(); i++) + if (index >= Items.Size()) + return S_FALSE; + CItem &item = Items[index]; + if (item.IsEmptyType()) + return S_FALSE; + item.Level = level; + state++; + states[index] = (Byte)state; // we mark current (index) node as used node + + UInt32 newIndex; + if (state != 2) + newIndex = (state < 2) ? item.LeftDid : item.RightDid; + else + { + CRef ref; + ref.Parent = refParent; + ref.Did = index; + const unsigned refIndex = Refs.Add(ref); + if (!item.IsDir()) + continue; + newIndex = item.SonDid; + if (newIndex != kNoDid) + { + level++; + refParent = refIndex; + } + } + + if (newIndex != kNoDid) + { + itemParents[index] = parent; + state = 0; + parent = index; + index = newIndex; + if (index >= Items.Size() || states[index]) + return S_FALSE; + } + } + + if (level != k_startLevel || refParent != k_Ref_Parent_Root) + return E_FAIL; +#if 1 // 1 : optional + // we check that all non-empty items were processed correctly + FOR_VECTOR(i, Items) { - const wchar_t c = s[i]; - if ((unsigned)(int)c < 0x20) + const unsigned st = states[i]; + if (Items[i].IsEmptyType()) { - res.Add_Char('['); - res.Add_UInt32((UInt32)(unsigned)(int)c); - res.Add_Char(']'); + if (st) + return E_FAIL; } + else if (st == 3) + continue; + else if (st) + return E_FAIL; else - res += c; + return S_FALSE; // there is unused directory item } - return res; +#endif + return S_OK; } + static const char k_Msi_Chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._"; - -// static const char * const k_Msi_ID = ""; // "{msi}"; -static const char k_Msi_SpecChar = '!'; - -static const unsigned k_Msi_NumBits = 6; -static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits; -static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1; -static const unsigned k_Msi_StartUnicodeChar = 0x3800; -static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1); - - -static bool IsMsiName(const Byte *p) -{ - UInt32 c = Get16(p); - return - c >= k_Msi_StartUnicodeChar && - c <= k_Msi_StartUnicodeChar + k_Msi_UnicodeRange; -} +static const char k_Msi_SpecChar_Replace = '!'; static bool AreEqualNames(const Byte *rawName, const char *asciiName) { - for (unsigned i = 0; i < kNameSizeMax / 2; i++) + for (;;) { - wchar_t c = Get16(rawName + i * 2); - wchar_t c2 = (Byte)asciiName[i]; + const unsigned c = Get16(rawName); + rawName += 2; + const unsigned c2 = (Byte)*asciiName; + asciiName++; if (c != c2) return false; - if (c == 0) + if (c2 == 0) return true; } - return false; } -static bool CompoundMsiNameToFileName(const UString &name, UString &res) -{ - res.Empty(); - for (unsigned i = 0; i < name.Len(); i++) - { - wchar_t c = name[i]; - if (c < (wchar_t)k_Msi_StartUnicodeChar || c > (wchar_t)(k_Msi_StartUnicodeChar + k_Msi_UnicodeRange)) - return false; - /* - if (i == 0) - res += k_Msi_ID; - */ - c -= (wchar_t)k_Msi_StartUnicodeChar; - - const unsigned c0 = (unsigned)c & k_Msi_CharMask; - const unsigned c1 = (unsigned)c >> k_Msi_NumBits; - if (c1 <= k_Msi_NumChars) - { - res.Add_Char(k_Msi_Chars[c0]); - if (c1 == k_Msi_NumChars) - break; - res.Add_Char(k_Msi_Chars[c1]); - } - else - res.Add_Char(k_Msi_SpecChar); - } - return true; -} - -static UString ConvertName(const Byte *p, bool &isMsi) +static void MsiName_To_FileName(const Byte *p, UString &res) { - isMsi = false; - UString s; - + res.Empty(); for (unsigned i = 0; i < kNameSizeMax; i += 2) { - wchar_t c = Get16(p + i); + unsigned c = Get16(p + i); if (c == 0) break; - s += c; - } - - UString msiName; - if (CompoundMsiNameToFileName(s, msiName)) - { - isMsi = true; - return msiName; + if (c <= k_Msi_SpecUnicodeChar) + { + if (c < k_Msi_StartUnicodeChar) + { + if (c < 0x20) + { + res.Add_Char('['); + res.Add_UInt32((UInt32)c); + c = ']'; + } + } + else + { +#if 0 // 1 : for debug + if (i == 0) res += "{msi}"; +#endif + c -= k_Msi_StartUnicodeChar; + const unsigned c1 = (unsigned)c >> k_Msi_NumBits; + if (c1 <= k_Msi_NumChars) + { + res.Add_Char(k_Msi_Chars[(unsigned)c & k_Msi_CharMask]); + if (c1 == k_Msi_NumChars) + continue; + c = (Byte)k_Msi_Chars[c1]; + } + else + c = k_Msi_SpecChar_Replace; + } + } + res += (wchar_t)c; } - return CompoundNameToFileName(s); } -static UString ConvertName(const Byte *p) -{ - bool isMsi; - return ConvertName(p, isMsi); -} UString CDatabase::GetItemPath(UInt32 index) const { UString s; - while (index != kNoDid) + UString name; + unsigned level = 0; + while (index != k_Ref_Parent_Root) { const CRef &ref = Refs[index]; const CItem &item = Items[ref.Did]; if (!s.IsEmpty()) s.InsertAtFront(WCHAR_PATH_SEPARATOR); - s.Insert(0, ConvertName(item.Name)); - index = (unsigned)ref.Parent; + // if (IsMsi) + MsiName_To_FileName(item.Name, name); + // else NonMsiName_To_FileName(item.Name, name); + NItemName::NormalizeSlashes_in_FileName_for_OsPath(name); + if (name.IsEmpty()) + name = "[]"; + s.Insert(0, name); + index = ref.Parent; +#ifdef Z7_COMPOUND_SHOW_DELETED + if (item.IsLevel_Unused()) + { + s.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); + break; + } +#endif + if (item.Level >= k_Long_path_level_limit && level) + { + s.Insert(0, L"[LONG_PATH]" WSTRING_PATH_SEPARATOR); + break; + } + level = 1; // level++; } return s; } -HRESULT CDatabase::Update_PhySize_WithItem(unsigned index) + +HRESULT CDatabase::Check_Item(unsigned index) { const CItem &item = Items[index]; - const bool isLargeStream = (index == 0 || IsLargeStream(item.Size)); - if (!isLargeStream) + if (item.IsEmptyType() || item.IsStorage()) return S_OK; - const unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits; - // streamSpec->Size = item.Size; - - const UInt32 clusterSize = (UInt32)1 << bsLog; - const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; - if (numClusters64 >= ((UInt32)1 << 31)) - return S_FALSE; - UInt32 sid = item.Sid; UInt64 size = item.Size; - - if (size != 0) + const bool isLargeStream = (index == 0 || IsLargeStream(size)); + if (!isLargeStream) { - for (;; size -= clusterSize) + const unsigned bsLog = k_MiniSectorSizeBits; + const UInt32 clusterSize = (UInt32)1 << bsLog; + const UInt64 numClusters = (size + clusterSize - 1) >> bsLog; + if (numClusters > MatSize) + return S_FALSE; + UInt32 sid = item.Sid; + if (size != 0) { - // if (isLargeStream) + for (;; size -= clusterSize) + { + if (sid >= MatSize) + return S_FALSE; + const unsigned subBits = SectorSizeBits - k_MiniSectorSizeBits; + const UInt32 fid = sid >> subBits; + if (fid >= NumSectors_in_MiniStream) + return false; + sid = Mat[sid]; + if (size <= clusterSize) + break; + } + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + } + else + { + const unsigned bsLog = SectorSizeBits; + const UInt32 clusterSize = (UInt32)1 << bsLog; + const UInt64 numClusters = (size + clusterSize - 1) >> bsLog; + if (numClusters > FatSize) + return S_FALSE; + UInt32 sid = item.Sid; + if (size != 0) + { + for (;; size -= clusterSize) { if (sid >= FatSize) return S_FALSE; - UInt64 end = ((UInt64)sid + 1) << bsLog; - const UInt64 end_Aligned = end + clusterSize; - if (size < clusterSize) - end += size; - else - end = end_Aligned; - UpdatePhySize(end, end_Aligned); + const UInt32 sidPrev = sid; sid = Fat[sid]; + if (size <= clusterSize) + { + const UInt64 phySize = (((UInt64)sidPrev + 1) << SectorSizeBits) + size; + if (PhySize_Unaligned < phySize) + PhySize_Unaligned = phySize; + break; + } } - if (size <= clusterSize) - break; } + if (sid != NFatID::kEndOfChain) + return S_FALSE; } - if (sid != NFatID::kEndOfChain) - return S_FALSE; return S_OK; } -// There is name "[!]MsiPatchSequence" in msp files -static const unsigned kMspSequence_Size = 18; -static const Byte kMspSequence[kMspSequence_Size] = - { 0x40, 0x48, 0x96, 0x45, 0x6C, 0x3E, 0xE4, 0x45, - 0xE6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41, - 0x37, 0x41 }; HRESULT CDatabase::Open(IInStream *inStream) { - MainSubfile = -1; - Type = k_Type_Common; - const UInt32 kHeaderSize = 512; - Byte p[kHeaderSize]; - PhySize = kHeaderSize; - RINOK(ReadStream_FALSE(inStream, p, kHeaderSize)) - if (memcmp(p, kSignature, Z7_ARRAY_SIZE(kSignature)) != 0) + const unsigned kHeaderSize = 512; + UInt32 p32[kHeaderSize / 4]; + RINOK(ReadStream_FALSE(inStream, p32, kHeaderSize)) + const Byte *p = (const Byte *)(const void *)p32; + if (memcmp(p, kSignature, Z7_ARRAY_SIZE(kSignature))) + return S_FALSE; + /* + if (memcmp(p + 8, k_CLSID_AAF_V3, Z7_ARRAY_SIZE(k_CLSID_AAF_V3)) == 0 || + memcmp(p + 8, k_CLSID_AAF_V4, Z7_ARRAY_SIZE(k_CLSID_AAF_V4)) == 0) + */ + if (Get32(p32 + 4) == 0x342b0e06) // simplified AAF signature check + Type = k_Type_Aaf; + if (Get16(p + 0x18) != 0x3e) // minorVer return S_FALSE; - if (Get16(p + 0x1A) > 4) // majorVer + const unsigned ver = Get16(p + 0x1a); // majorVer + if (ver < 3 || ver > 4) return S_FALSE; - if (Get16(p + 0x1C) != 0xFFFE) // Little-endian + if (Get16(p + 0x1c) != 0xfffe) // Little-endian + return S_FALSE; + const unsigned sectorSizeBits = Get16(p + 0x1e); + if (sectorSizeBits != ver * 3) // (ver == 3 ? 9 : 12) return S_FALSE; - unsigned sectorSizeBits = Get16(p + 0x1E); - bool mode64bit = (sectorSizeBits >= 12); - unsigned miniSectorSizeBits = Get16(p + 0x20); SectorSizeBits = sectorSizeBits; - MiniSectorSizeBits = miniSectorSizeBits; + if (Get16(p + 0x20) != k_MiniSectorSizeBits) + return S_FALSE; + + IsArc = true; + HeadersError = true; - if (sectorSizeBits > 24 || - sectorSizeBits < 7 || - miniSectorSizeBits > 24 || - miniSectorSizeBits < 2 || - miniSectorSizeBits > sectorSizeBits) + const bool mode64bit = (sectorSizeBits >= 12); // (ver == 4) + if (Get16(p + 0x22) || p32[9]) // reserved + return S_FALSE; + + const UInt32 numDirSectors = Get32(p32 + 10); + // If (ver==3), the Number of Directory Sectors MUST be zero. + if (ver != 3 + (unsigned)(numDirSectors != 0)) + return S_FALSE; + if (numDirSectors > ((1u << (32 - 2)) >> (sectorSizeBits - (7 + 2)))) + return S_FALSE; + + const UInt32 numSectorsForFAT = Get32(p32 + 11); // SAT + + // MSDOC: A 512-byte sector compound file MUST be no greater than 2 GB in size for compatibility reasons. + // but actual restriction for windows compond creation code can be more strict: + // (numSectorsForFAT < (1 << 15)) : actual restriction in win10 for compound creation code + // (numSectorsForFAT <= (1 << 15)) : relaxed restriction to allow 2 GB files. + if (sectorSizeBits == 9 && + numSectorsForFAT >= (1u << (31 - (9 + 9 - 2)))) // we use most strict check return S_FALSE; - UInt32 numSectorsForFAT = Get32(p + 0x2C); // SAT - LongStreamMinSize = Get32(p + 0x38); - - UInt32 sectSize = (UInt32)1 << sectorSizeBits; - CByteBuffer sect(sectSize); + // const UInt32 TransactionSignatureNumber = Get32(p32 + 13); + if (Get32(p32 + 14) != k_LongStreamMinSize) + return S_FALSE; - unsigned ssb2 = sectorSizeBits - 2; - UInt32 numSidsInSec = (UInt32)1 << ssb2; - UInt32 numFatItems = numSectorsForFAT << ssb2; - if ((numFatItems >> ssb2) != numSectorsForFAT) + const unsigned ssb2 = sectorSizeBits - 2; + const UInt32 numSidsInSec = (UInt32)1 << ssb2; + const UInt32 numFatItems = numSectorsForFAT << ssb2; + if (numFatItems == 0 || (numFatItems >> ssb2) != numSectorsForFAT) return S_FALSE; - FatSize = numFatItems; + const size_t sectSize = (size_t)1 << sectorSizeBits; + CByteArr sect(sectSize); + CByteArr used(numFatItems); + // don't change these const values. These values use same order as (0xffffffff - NFatID::const) + // const Byte k_Used_Free = 0; + const Byte k_Used_ChainTo = 1; + const Byte k_Used_FAT = 2; + const Byte k_Used_DIFAT = 3; + memset(used, 0, numFatItems); + UInt32 *fat; { - UInt32 numSectorsForBat = Get32(p + 0x48); // master sector allocation table - const UInt32 kNumHeaderBatItems = 109; - UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2); - if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat) + // ========== READ FAT ========== + const UInt32 numSectorsForBat = Get32(p32 + 18); // master sector allocation table + const unsigned ssb2_m1 = ssb2 - 1; + if (numSectorsForBat > ((1u << 30) >> ssb2_m1 >> ssb2_m1)) return S_FALSE; + const unsigned kNumHeaderBatItems = 109; + UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2); // real size can be smaller CObjArray bat(numBatItems); - UInt32 i; + size_t i; for (i = 0; i < kNumHeaderBatItems; i++) - bat[i] = Get32(p + 0x4c + i * 4); - UInt32 sid = Get32(p + 0x44); - for (UInt32 s = 0; s < numSectorsForBat; s++) + bat[i] = Get32(p32 + 19 + i); { - RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i)) - i += numSidsInSec - 1; - sid = bat[i]; + UInt32 sid = Get32(p32 + 17); + for (UInt32 s = 0; s < numSectorsForBat; s++) + { + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_DIFAT; + RINOK(ReadIDs(inStream, sect, sid, bat + i)) + i += numSidsInSec - 1; + sid = bat[i]; + } + if (sid != NFatID::kEndOfChain // NFatID::kEndOfChain is expected value for most files + && sid != NFatID::kFree) // NFatID::kFree is used in some AAF files + return S_FALSE; } - numBatItems = i; - + numBatItems = (UInt32)i; // corrected value + if (numSectorsForFAT > numBatItems) + return S_FALSE; + for (i = numSectorsForFAT; i < numBatItems; i++) + if (bat[i] != NFatID::kFree) + return S_FALSE; + + // RINOK(IncreaseOpenTotal(numSectorsForFAT + numDirSectors)) + Fat.Alloc(numFatItems); - UInt32 j = 0; - - for (i = 0; i < numFatItems; j++, i += numSidsInSec) + fat = Fat; + for (i = 0; i < numSectorsForFAT; i++) { - if (j >= numBatItems) + const UInt32 sectorIndex = bat[i]; + if (sectorIndex >= numFatItems) return S_FALSE; - RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i)) + if (used[sectorIndex]) + return S_FALSE; + used[sectorIndex] = k_Used_FAT; + UInt32 *fat2 = fat + ((size_t)i << ssb2); + RINOK(ReadIDs(inStream, sect, sectorIndex, fat2)) + for (size_t k = 0; k < numSidsInSec; k++) + { + const UInt32 sid = fat2[k]; + if (sid > NFatID::kMaxValue) + { + if (sid == NFatID::k_DIF_SECT + && used[((size_t)i << ssb2) + k] != k_Used_DIFAT) + return S_FALSE; + continue; + } + if (sid >= numFatItems || used[sid]) + return S_FALSE; // strict error check + used[sid] = k_Used_ChainTo; + } } - FatSize = numFatItems = i; + { + for (i = 0; i < numSectorsForFAT; i++) + if (fat[bat[i]] != NFatID::kFatSector) + return S_FALSE; + } + FatSize = numFatItems; + } + + { + size_t i = numFatItems; + do + if (fat[i - 1] != NFatID::kFree) + break; + while (--i); + PhySize = ((UInt64)i + 1) << sectorSizeBits; + /* + if (i) + { + const UInt32 *lim = fat + i; + UInt32 num = 0; + do + if (*fat++ == NFatID::kFree) + num++; + while (fat != lim); + FreeSize = num << sectorSizeBits; + } + */ } UInt32 numMatItems; { - UInt32 numSectorsForMat = Get32(p + 0x40); + // ========== READ MAT ========== + const UInt32 numSectorsForMat = Get32(p32 + 16); numMatItems = (UInt32)numSectorsForMat << ssb2; if ((numMatItems >> ssb2) != numSectorsForMat) return S_FALSE; Mat.Alloc(numMatItems); - UInt32 i; - UInt32 sid = Get32(p + 0x3C); // short-sector table SID - for (i = 0; i < numMatItems; i += numSidsInSec) + UInt32 sid = Get32(p32 + 15); // short-sector table SID + if (numMatItems) + { + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_ChainTo; + } + for (UInt32 i = 0; i < numMatItems; i += numSidsInSec) { - RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i)) if (sid >= numFatItems) return S_FALSE; - sid = Fat[sid]; + RINOK(ReadIDs(inStream, sect, sid, Mat + i)) + sid = fat[sid]; } if (sid != NFatID::kEndOfChain) return S_FALSE; } { - CByteBuffer used(numFatItems); - for (UInt32 i = 0; i < numFatItems; i++) - used[i] = 0; - UInt32 sid = Get32(p + 0x30); // directory stream SID - for (;;) + // ========== READ DIR ITEMS ========== + UInt32 sid = Get32(p32 + 12); // directory stream SID + UInt32 numDirSectors_Processed = 0; + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_ChainTo; + do { + // we need to check sid here becase kEndOfChain sid < numFatItems is required if (sid >= numFatItems) return S_FALSE; - if (used[sid]) + if (numDirSectors && numDirSectors_Processed >= numDirSectors) return S_FALSE; - used[sid] = 1; - RINOK(ReadSector(inStream, sect, sectorSizeBits, sid)) - for (UInt32 i = 0; i < sectSize; i += 128) + numDirSectors_Processed++; + RINOK(ReadSector(inStream, sect, sid)) + for (size_t i = 0; i < sectSize; i += (1 << 7)) { CItem item; - item.Parse(sect + i, mode64bit); + item.Level = k_Item_Level_Unused; + if (!item.Parse(sect + i, mode64bit)) + return S_FALSE; // we use (item.Size) check here. // so we don't need additional overflow checks for (item.Size +) in another code - if (item.Size >= ((UInt64)1 << 63)) + if ((UInt32)(item.Size >> 32) >= sectSize) // it's because FAT size is limited by (1 << 32) items. return S_FALSE; + + if (Items.IsEmpty()) + { + if (item.Type != NItemType::kRootStorage + || item.LeftDid != kNoDid + || item.RightDid != kNoDid + || item.SonDid == 0) + return S_FALSE; + if (item.Sid != NFatID::kEndOfChain) + { + if (item.Sid >= numFatItems || used[item.Sid]) + return S_FALSE; + used[item.Sid] = k_Used_ChainTo; + } + } + else if (item.IsStorage()) + { + if (item.Size != 0) // by specification + return S_FALSE; + if (item.Sid != 0 // by specification + && item.Sid != NFatID::kFree) // NFatID::kFree is used in some AAF files + return S_FALSE; + } + // else if (item.Type == NItemType::kRootStorage) return S_FALSE; + else if (item.IsEmptyType()) + { + // kNoDid is expected in *Did fileds, but rare case MSI contains zero in all fields + if ((item.Sid != 0 // expected value + && item.Sid != NFatID::kFree // NFatID::kFree is used in some AAF files + && item.Sid != NFatID::kEndOfChain) // used by some MSI file + || (item.LeftDid != kNoDid && item.LeftDid) + || (item.RightDid != kNoDid && item.RightDid) + || (item.SonDid != kNoDid && item.SonDid) + // || item.Size != 0 // the check is disabled because some MSI file contains non zero + // || Get16(item.Name) != 0 // the check is disabled because some MSI file contains some name + ) + return S_FALSE; + } + else + { + if (item.Type != NItemType::kStream) + return S_FALSE; + // NItemType::kStream case + if (item.SonDid != kNoDid) // optional check + return S_FALSE; + if (item.Size == 0) + { + if (item.Sid != NFatID::kEndOfChain) + return S_FALSE; + } + else if (IsLargeStream(item.Size)) + { + if (item.Sid >= numFatItems || used[item.Sid]) + return S_FALSE; + used[item.Sid] = k_Used_ChainTo; + } + } + Items.Add(item); } - sid = Fat[sid]; - if (sid == NFatID::kEndOfChain) - break; + sid = fat[sid]; } + while (sid != NFatID::kEndOfChain); } - const CItem &root = Items[0]; - { + // root stream contains all data that stored with mini Sectors + const CItem &root = Items[0]; UInt32 numSectorsInMiniStream; { - UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits; - if (numSatSects64 > NFatID::kMaxValue) + const UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits; + if (numSatSects64 > NFatID::kMaxValue + 1) return S_FALSE; numSectorsInMiniStream = (UInt32)numSatSects64; } - NumSectorsInMiniStream = numSectorsInMiniStream; - MiniSids.Alloc(numSectorsInMiniStream); { - UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits; - if (matSize64 > NFatID::kMaxValue) + const UInt64 matSize64 = (root.Size + (1 << k_MiniSectorSizeBits) - 1) >> k_MiniSectorSizeBits; + if (matSize64 > numMatItems) return S_FALSE; MatSize = (UInt32)matSize64; - if (numMatItems < MatSize) - return S_FALSE; } - + MiniSids.Alloc(numSectorsInMiniStream); + UInt32 * const miniSids = MiniSids; UInt32 sid = root.Sid; for (UInt32 i = 0; ; i++) { @@ -571,95 +928,186 @@ HRESULT CDatabase::Open(IInStream *inStream) } if (i >= numSectorsInMiniStream) return S_FALSE; - MiniSids[i] = sid; if (sid >= numFatItems) return S_FALSE; - sid = Fat[sid]; + miniSids[i] = sid; + sid = fat[sid]; } + NumSectors_in_MiniStream = numSectorsInMiniStream; } - RINOK(AddNode(-1, root.SonDid)) - - unsigned numCabs = 0; - - FOR_VECTOR (i, Refs) + { - const CItem &item = Items[Refs[i].Did]; - if (item.IsDir() || numCabs > 1) - continue; - bool isMsiName; - const UString msiName = ConvertName(item.Name, isMsiName); - if (isMsiName && !msiName.IsEmpty()) - { - // bool isThereExt = (msiName.Find(L'.') >= 0); - bool isMsiSpec = (msiName[0] == k_Msi_SpecChar); - if ((msiName.Len() >= 4 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(4), ".cab")) - || (!isMsiSpec && msiName.Len() >= 3 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(3), "exe")) - // || (!isMsiSpec && !isThereExt) - ) +/* + MS DOCs: + The range lock sector covers file offsets 0x7FFFFF00-0x7FFFFFFF. + These offsets are reserved for byte-range locking to support + concurrency, transactions, and other compound file features. + The range lock sector MUST be allocated in the FAT and marked with + ENDOFCHAIN (0xFFFFFFFE), when the compound file grows beyond 2 GB. + If the compound file is greater than 2 GB and then shrinks to below 2 GB, + the range lock sector SHOULD be marked as FREESECT (0xFFFFFFFF) in the FAT. +*/ + { + const UInt32 lockSector = (0x7fffffff >> sectorSizeBits) - 1; + if (lockSector < numFatItems) { - numCabs++; - MainSubfile = (int)i; + if (used[lockSector]) + return S_FALSE; + const UInt32 f = fat[lockSector]; + if (f == NFatID::kEndOfChain) + used[lockSector] = k_Used_ChainTo; // we use fake state to pass the check in loop below + else if (f != NFatID::kFree) + return S_FALSE; } } + for (size_t i = 0; i < numFatItems; i++) + { + UInt32 f = fat[i]; + const UInt32 u = ~(UInt32)used[i]; // (0xffffffff - used[i]) + if (f < NFatID::kMaxValue + 1) + f = NFatID::kEndOfChain; + if (f != u) + return S_FALSE; + } } - - if (numCabs > 1) - MainSubfile = -1; { - FOR_VECTOR (t, Items) + // Don't move that code up, becase Check_Item uses Mat[] array. + FOR_VECTOR(t, Items) { - Update_PhySize_WithItem(t); + RINOK(Check_Item(t)) } } + + RINOK(AddNodes()) + + { + // some msi (in rare cases) have unaligned size of archive, + // unaligned size of compond files is also possible if we create just one stream + // where there is no padding data after payload data in last cluster of archive + UInt64 fileSize; + RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize)) + if ( fileSize < PhySize + && fileSize > PhySize - sectSize + && fileSize >= PhySize_Unaligned + && PhySize_Unaligned > PhySize - sectSize) + PhySize = PhySize_Unaligned; + } + + bool isMsi = false; { - if (PhySize != PhySize_Aligned) + FOR_VECTOR (i, Refs) { - /* some msi (in rare cases) have unaligned size of archive, - where there is no padding data after payload data in last cluster of archive */ - UInt64 fileSize; - RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize)) - if (PhySize != fileSize) - PhySize = PhySize_Aligned; + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir()) + continue; + if (item.AreMsiChars()) + // if (item.IsSpecMsiName()) + { + isMsi = true; + break; + } } } + + // IsMsi = isMsi; + if (isMsi) { - FOR_VECTOR (t, Items) + unsigned numCabs = 0; + UString name; + FOR_VECTOR (i, Refs) { - const CItem &item = Items[t]; - - if (IsMsiName(item.Name)) + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir() /* || item.IsSpecMsiName() */) + continue; + MsiName_To_FileName(item.Name, name); + if ( (name.Len() >= 4 && StringsAreEqualNoCase_Ascii(name.RightPtr(4), ".cab")) + || (name.Len() >= 3 && StringsAreEqualNoCase_Ascii(name.RightPtr(3), "exe")) + ) { - Type = k_Type_Msi; - if (memcmp(item.Name, kMspSequence, kMspSequence_Size) == 0) + numCabs++; + if (numCabs > 1) { - Type = k_Type_Msp; + MainSubfile = -1; break; } - continue; - } - if (AreEqualNames(item.Name, "WordDocument")) - { - Type = k_Type_Doc; - break; + MainSubfile = (int)i; } - if (AreEqualNames(item.Name, "PowerPoint Document")) + } + } + + if (isMsi) // we provide msi priority over AAF + Type = k_Type_Msi; + if (Type != k_Type_Aaf) + { + FOR_VECTOR (i, Refs) + { + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir()) + continue; + const Byte *name = item.Name; + // if (IsMsiName(name)) + if (isMsi) { - Type = k_Type_Ppt; - break; + if (memcmp(name, k_Sequence_msp, sizeof(k_Sequence_msp)) == 0) + { + Type = k_Type_Msp; + break; + } + if (memcmp(name, k_Sequence_msm, sizeof(k_Sequence_msm)) == 0) + { + Type = k_Type_Msm; + break; + } } - if (AreEqualNames(item.Name, "Workbook")) + else { - Type = k_Type_Xls; - break; + if (AreEqualNames(name, "WordDocument")) + { + Type = k_Type_Doc; + break; + } + if (AreEqualNames(name, "PowerPoint Document")) + { + Type = k_Type_Ppt; + break; + } + if (AreEqualNames(name, "Workbook")) + { + Type = k_Type_Xls; + break; + } } } } +#ifdef Z7_COMPOUND_SHOW_DELETED + { + // we skip Items[0] that is root item + for (unsigned t = 1; t < Items.Size(); t++) + { + const CItem &item = Items[t]; + if ( +#if 1 // 0 for debug to show empty files + item.IsEmptyType() || +#endif + !item.IsLevel_Unused()) + continue; + CRef ref; + ref.Parent = k_Ref_Parent_Root; + ref.Did = t; + Refs.Add(ref); + } + } +#endif + + HeadersError = false; return S_OK; } + + Z7_CLASS_IMP_CHandler_IInArchive_1( IInArchiveGetStream ) @@ -674,13 +1122,15 @@ static const Byte kProps[] = kpidPackSize, kpidCTime, kpidMTime + // , kpidCharacts // for debug }; static const Byte kArcProps[] = { kpidExtension, - kpidClusterSize, - kpidSectorSize + kpidClusterSize + // , kpidSectorSize + // , kpidFreeSpace }; IMP_IInArchive_Props @@ -695,9 +1145,20 @@ Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) case kpidExtension: prop = kExtensions[(unsigned)_db.Type]; break; case kpidPhySize: prop = _db.PhySize; break; case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break; - case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; + // case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break; + // case kpidFreeSpace: prop = _db.FreeSize; break; case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_db.IsArc) + v |= kpv_ErrorFlags_IsNotArc; + if (_db.HeadersError) + v |= kpv_ErrorFlags_HeadersError; + prop = v; + break; + } } prop.Detach(value); return S_OK; @@ -719,6 +1180,7 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidMTime: prop = item.MTime; break; case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break; case kpidSize: if (!item.IsDir()) prop = item.Size; break; + // case kpidCharacts: prop = item.Level; break; } prop.Detach(value); return S_OK; @@ -727,17 +1189,17 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 * /* maxCheckStartPosition */, - IArchiveOpenCallback * /* openArchiveCallback */)) + IArchiveOpenCallback *openArchiveCallback)) { COM_TRY_BEGIN Close(); - try + _db.OpenCallback = openArchiveCallback; + // try { - if (_db.Open(inStream) != S_OK) - return S_FALSE; + RINOK(_db.Open(inStream)) _stream = inStream; } - catch(...) { return S_FALSE; } + // catch(...) { return S_FALSE; } return S_OK; COM_TRY_END } @@ -775,52 +1237,57 @@ Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr2_Create lps; lps->Init(extractCallback, false); - for (i = 0; i < numItems; i++) + for (i = 0;; i++) { lps->InSize = totalPackSize; lps->OutSize = totalSize; RINOK(lps->SetCur()) + if (i >= numItems) + break; + const UInt32 index = allFilesMode ? i : indices[i]; const CItem &item = _db.Items[_db.Refs[index].Did]; Int32 res; - { - CMyComPtr outStream; - const Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - RINOK(extractCallback->GetStream(index, &outStream, askMode)) - - if (item.IsDir()) { + CMyComPtr outStream; + const Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(index, &outStream, askMode)) + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)) + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) + continue; + } + + totalPackSize += _db.GetItemPackSize(item.Size); + totalSize += item.Size; + + if (!testMode && !outStream) + continue; RINOK(extractCallback->PrepareOperation(askMode)) - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) - continue; - } - - totalPackSize += _db.GetItemPackSize(item.Size); - totalSize += item.Size; - - if (!testMode && !outStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)) - res = NExtract::NOperationResult::kDataError; - CMyComPtr inStream; - HRESULT hres = GetStream(index, &inStream); - if (hres == S_FALSE) res = NExtract::NOperationResult::kDataError; - else if (hres == E_NOTIMPL) - res = NExtract::NOperationResult::kUnsupportedMethod; - else - { - RINOK(hres) - if (inStream) + CMyComPtr inStream; + const HRESULT hres = GetStream(index, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kDataError; + /* + else if (hres == E_NOTIMPL) + res = NExtract::NOperationResult::kUnsupportedMethod; + */ + else { - RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps)) - if (copyCoder->TotalSize == item.Size) - res = NExtract::NOperationResult::kOK; + RINOK(hres) + if (inStream) + { + RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps)) + if (copyCoder->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } } } - } RINOK(extractCallback->SetOperationResult(res)) } return S_OK; @@ -839,20 +1306,64 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) *stream = NULL; const UInt32 itemIndex = _db.Refs[index].Did; const CItem &item = _db.Items[itemIndex]; + if (item.IsDir()) + return S_FALSE; + const bool isLargeStream = (itemIndex == 0 || IsLargeStream(item.Size)); + if (!isLargeStream) + { + CBufferInStream *streamSpec = new CBufferInStream; + CMyComPtr streamTemp = streamSpec; + + UInt32 size = (UInt32)item.Size; + streamSpec->Buf.Alloc(size); + streamSpec->Init(); + + UInt32 sid = item.Sid; + Byte *dest = streamSpec->Buf; + + UInt64 phyPos = 0; + while (size) + { + if (sid >= _db.MatSize) + return S_FALSE; + const unsigned subBits = _db.SectorSizeBits - k_MiniSectorSizeBits; + const UInt32 fid = sid >> subBits; + if (fid >= _db.NumSectors_in_MiniStream) + return false; + const UInt64 offset = (((UInt64)_db.MiniSids[fid] + 1) << _db.SectorSizeBits) + + ((sid & ((1u << subBits) - 1)) << k_MiniSectorSizeBits); + if (phyPos != offset) + { + RINOK(InStream_SeekSet(_stream, offset)) + phyPos = offset; + } + UInt32 readSize = (UInt32)1 << k_MiniSectorSizeBits; + if (readSize > size) + readSize = size; + RINOK(ReadStream_FALSE(_stream, dest, readSize)) + phyPos += readSize; + dest += readSize; + sid = _db.Mat[sid]; + size -= readSize; + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + *stream = streamTemp.Detach(); + return S_OK; + } + CClusterInStream *streamSpec = new CClusterInStream; CMyComPtr streamTemp = streamSpec; streamSpec->Stream = _stream; streamSpec->StartOffset = 0; - - const bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size)); - const unsigned bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits; + const unsigned bsLog = _db.SectorSizeBits; streamSpec->BlockSizeLog = bsLog; streamSpec->Size = item.Size; const UInt32 clusterSize = (UInt32)1 << bsLog; const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; - if (numClusters64 >= ((UInt32)1 << 31)) - return E_NOTIMPL; + if (numClusters64 > _db.FatSize) + return S_FALSE; streamSpec->Vector.ClearAndReserve((unsigned)numClusters64); UInt32 sid = item.Sid; UInt64 size = item.Size; @@ -861,21 +1372,10 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) { for (;; size -= clusterSize) { - if (isLargeStream) - { - if (sid >= _db.FatSize) - return S_FALSE; - streamSpec->Vector.AddInReserved(sid + 1); - sid = _db.Fat[sid]; - } - else - { - UInt64 val = 0; - if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32) - return S_FALSE; - streamSpec->Vector.AddInReserved((UInt32)val); - sid = _db.Mat[sid]; - } + if (sid >= _db.FatSize) + return S_FALSE; + streamSpec->Vector.AddInReserved(sid + 1); + sid = _db.Fat[sid]; if (size <= clusterSize) break; } @@ -889,7 +1389,7 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) } REGISTER_ARC_I( - "Compound", "msi msp doc xls ppt", NULL, 0xE5, + "Compound", "msi msp msm doc xls ppt aaf", NULL, 0xe5, kSignature, 0, 0, diff --git a/CPP/7zip/Archive/CpioHandler.cpp b/CPP/7zip/Archive/CpioHandler.cpp index 62184f0..e1d6d81 100644 --- a/CPP/7zip/Archive/CpioHandler.cpp +++ b/CPP/7zip/Archive/CpioHandler.cpp @@ -927,7 +927,7 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val { #ifdef _WIN32 UString u; - ConvertUTF8ToUnicode(item.Name, u); + ConvertUTF8ToUnicode(s, u); #else const UString u = MultiByteToUnicodeString(s, CP_OEMCP); #endif diff --git a/CPP/7zip/Archive/QcowHandler.cpp b/CPP/7zip/Archive/QcowHandler.cpp index b072880..6edf86d 100644 --- a/CPP/7zip/Archive/QcowHandler.cpp +++ b/CPP/7zip/Archive/QcowHandler.cpp @@ -482,6 +482,10 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) if (_phySize < headerSize) _phySize = headerSize; + // we use 32 MiB limit for L1 size, as QEMU with QCOW_MAX_L1_SIZE limit. + if (l1Size > (1u << 22)) // if (l1Size > (1u << (sizeof(size_t) * 8 - 4))) + return S_FALSE; + _isArc = true; { const UInt64 backOffset = Get64((const Byte *)(const void *)buf64 + 8); @@ -519,7 +523,6 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) } CObjArray table64(l1Size); { - // if ((t1SizeBytes >> 3) != l1Size) return S_FALSE; RINOK(InStream_SeekSet(stream, l1Offset)) RINOK(ReadStream_FALSE(stream, table64, t1SizeBytes)) } diff --git a/CPP/7zip/Archive/Rar/Rar5Handler.cpp b/CPP/7zip/Archive/Rar/Rar5Handler.cpp index a639d8b..c15ff52 100644 --- a/CPP/7zip/Archive/Rar/Rar5Handler.cpp +++ b/CPP/7zip/Archive/Rar/Rar5Handler.cpp @@ -8,6 +8,7 @@ #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/MyBuffer2.h" +#include "../../../Common/MyLinux.h" #include "../../../Common/UTFConvert.h" #include "../../../Windows/PropVariantUtils.h" @@ -1184,7 +1185,15 @@ HRESULT CUnpacker::Code(const CItem &item, const CItem &lastItem, UInt64 packSiz const UInt64 processedSize = outStream->GetPos(); if (res == S_OK && !lastItem.Is_UnknownSize() && processedSize != lastItem.Size) - res = S_FALSE; + { + // rar_v7.13-: linux archive contains symLink with (packSize == 0 && lastItem.Size != 0) + // v25.02: we ignore such record in rar headers: + if (packSize != 0 + || method != 0 + || lastItem.HostOS != kHost_Unix + || !MY_LIN_S_ISLNK(lastItem.Attrib)) + res = S_FALSE; + } // if (res == S_OK) { diff --git a/CPP/7zip/Archive/Rar/RarHandler.cpp b/CPP/7zip/Archive/Rar/RarHandler.cpp index dfbad33..6c53847 100644 --- a/CPP/7zip/Archive/Rar/RarHandler.cpp +++ b/CPP/7zip/Archive/Rar/RarHandler.cpp @@ -7,6 +7,7 @@ #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/MyBuffer2.h" +#include "../../../Common/MyLinux.h" #include "../../../Common/UTFConvert.h" #include "../../../Windows/PropVariantUtils.h" @@ -70,8 +71,14 @@ bool CItem::IsDir() const case NHeader::NFile::kHostMSDOS: case NHeader::NFile::kHostOS2: case NHeader::NFile::kHostWin32: - if ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0) + if (Attrib & FILE_ATTRIBUTE_DIRECTORY) return true; + break; + case NHeader::NFile::kHostUnix: + case NHeader::NFile::kHostBeOS: + if (MY_LIN_S_ISDIR(Attrib)) + return true; + break; } return false; } @@ -86,11 +93,20 @@ UInt32 CItem::GetWinAttrib() const case NHeader::NFile::kHostWin32: a = Attrib; break; + case NHeader::NFile::kHostUnix: + case NHeader::NFile::kHostBeOS: + a = Attrib << 16; + a |= 0x8000; // add posix mode marker + break; + // case NHeader::NFile::kHostMacOS: + // kHostMacOS was used only by some very old rare case rar. + // New rar4-rar7 for macos probably uses kHostUnix. + // So we process kHostMacOS without attribute parsing: default: - a = 0; // must be converted from unix value; + a = 0; } if (IsDir()) - a |= NHeader::NFile::kWinFileDirectoryAttributeMask; + a |= FILE_ATTRIBUTE_DIRECTORY; return a; } diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp index 29f28e8..5761ea3 100644 --- a/CPP/7zip/Archive/Tar/TarHandler.cpp +++ b/CPP/7zip/Archive/Tar/TarHandler.cpp @@ -65,7 +65,7 @@ static const Byte kArcProps[] = kpidComment }; -static const char *k_Characts_Prefix = "PREFIX"; +static const char * const k_Characts_Prefix = "PREFIX"; IMP_IInArchive_Props IMP_IInArchive_ArcProps @@ -684,10 +684,14 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val s.Add_OptSpaced("SCHILY.fflags="); s += item->SCHILY_fflags; } + if (item->Is_Sparse()) + s.Add_OptSpaced("SPARSE"); if (item->IsThereWarning()) s.Add_OptSpaced("WARNING"); if (item->HeaderError) s.Add_OptSpaced("ERROR"); + if (item->Method_Error) + s.Add_OptSpaced("METHOD_ERROR"); if (item->Pax_Error) s.Add_OptSpaced("PAX_error"); if (!item->PaxExtra.RawLines.IsEmpty()) @@ -812,11 +816,16 @@ Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, inStream2 = inStream; else { - GetStream(index, &inStream2); - if (!inStream2) - return E_FAIL; + const HRESULT hres = GetStream(index, &inStream2); + if (hres == E_NOTIMPL) + opRes = NExtract::NOperationResult::kHeadersError; // kUnsupportedMethod + else if (!inStream2) + { + opRes = NExtract::NOperationResult::kDataError; + // return E_FAIL; + } } - + if (opRes == NExtract::NOperationResult::kOK) { if (item->Is_SymLink()) { @@ -855,9 +864,9 @@ Z7_CLASS_IMP_IInStream( bool _needStartSeek; public: + unsigned ItemIndex; CHandler *Handler; CMyComPtr HandlerRef; - unsigned ItemIndex; CRecordVector PhyOffsets; void Init() @@ -879,7 +888,7 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) if (_virtPos >= item.Size) return S_OK; { - UInt64 rem = item.Size - _virtPos; + const UInt64 rem = item.Size - _virtPos; if (size > rem) size = (UInt32)rem; } @@ -903,17 +912,17 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) } const CSparseBlock &sb = item.SparseBlocks[left]; - UInt64 relat = _virtPos - sb.Offset; + const UInt64 relat = _virtPos - sb.Offset; if (_virtPos >= sb.Offset && relat < sb.Size) { - UInt64 rem = sb.Size - relat; + const UInt64 rem = sb.Size - relat; if (size > rem) size = (UInt32)rem; - UInt64 phyPos = PhyOffsets[left] + relat; + const UInt64 phyPos = PhyOffsets[left] + relat; if (_needStartSeek || _phyPos != phyPos) { - RINOK(InStream_SeekSet(Handler->_stream, (item.Get_DataPos() + phyPos))) + RINOK(InStream_SeekSet(Handler->_stream, item.Get_DataPos() + phyPos)) _needStartSeek = false; _phyPos = phyPos; } @@ -927,7 +936,7 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) next = sb.Offset; else if (left + 1 < item.SparseBlocks.Size()) next = item.SparseBlocks[left + 1].Offset; - UInt64 rem = next - _virtPos; + const UInt64 rem = next - _virtPos; if (size > rem) size = (UInt32)rem; memset(data, 0, size); @@ -965,6 +974,8 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) if (item.Is_Sparse()) { + if (item.Method_Error) + return E_NOTIMPL; // S_FALSE CSparseStream *streamSpec = new CSparseStream; CMyComPtr streamTemp = streamSpec; streamSpec->Init(); diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp index 22b8902..e702b68 100644 --- a/CPP/7zip/Archive/Tar/TarIn.cpp +++ b/CPP/7zip/Archive/Tar/TarIn.cpp @@ -181,6 +181,7 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) { char buf[NFileHeader::kRecordSize]; + item.Method_Error = false; error = k_ErrorType_OK; filled = false; @@ -218,10 +219,7 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) break; item.HeaderSize += NFileHeader::kRecordSize; thereAreEmptyRecords = true; - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } + RINOK(Progress(item, 0)) } if (thereAreEmptyRecords) { @@ -335,84 +333,83 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse) { - Byte isExtended = (Byte)buf[482]; - if (isExtended != 0 && isExtended != 1) - return S_OK; + // OLD GNU format: parse sparse file information: + // PackSize = cumulative size of all non-empty blocks of the file. + // We read actual file size from 'realsize' member of oldgnu_header: RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin)) - UInt64 min = 0; - for (unsigned i = 0; i < 4; i++) - { - p = buf + 386 + 24 * i; - if (GetBe32(p) == 0) - { - if (isExtended != 0) - return S_OK; - break; - } - CSparseBlock sb; - RIF(ParseSize(p, sb.Offset)) - RIF(ParseSize(p + 12, sb.Size)) - item.SparseBlocks.Add(sb); - if (sb.Offset < min || sb.Offset > item.Size) - return S_OK; - if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0) - return S_OK; - min = sb.Offset + sb.Size; - if (min < sb.Offset) - return S_OK; - } - if (min > item.Size) + if (item.Size < item.PackSize) // additional check return S_OK; - while (isExtended != 0) - { - size_t processedSize = NFileHeader::kRecordSize; - RINOK(ReadStream(SeqStream, buf, &processedSize)) - if (processedSize != NFileHeader::kRecordSize) - { - error = k_ErrorType_UnexpectedEnd; - return S_OK; - } - - item.HeaderSize += NFileHeader::kRecordSize; - - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } + p = buf + 386; + + UInt64 end = 0, packSum = 0; + unsigned numRecords = 4; + unsigned isExtended = (Byte)p[4 * 24]; // (Byte)p[numRecords * 24]; + // the list of blocks contains non-empty blocks. All another data is empty. - isExtended = (Byte)buf[21 * 24]; - if (isExtended != 0 && isExtended != 1) + for (;;) + { + // const unsigned isExtended = (Byte)p[numRecords * 24]; + if (isExtended > 1) return S_OK; - for (unsigned i = 0; i < 21; i++) + do { - p = buf + 24 * i; if (GetBe32(p) == 0) { - if (isExtended != 0) + if (isExtended) return S_OK; break; } CSparseBlock sb; RIF(ParseSize(p, sb.Offset)) RIF(ParseSize(p + 12, sb.Size)) - item.SparseBlocks.Add(sb); - if (sb.Offset < min || sb.Offset > item.Size) - return S_OK; - if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0) - return S_OK; - min = sb.Offset + sb.Size; - if (min < sb.Offset) + p += 24; + /* for all non-last blocks we expect : + ((sb.Size & 0x1ff) == 0) && ((sb.Offset & 0x1ff) == 0) + for last block : (sb.Size == 0) is possible. + */ + if (sb.Offset < end + || item.Size < sb.Offset + || item.Size - sb.Offset < sb.Size) return S_OK; + // optional check: + if (sb.Size && ((end & 0x1ff) || (sb.Offset & 0x1ff))) + { + item.Method_Error = true; // relaxed check + // return S_OK; + } + end = sb.Offset + sb.Size; + packSum += sb.Size; + item.SparseBlocks.Add(sb); + } + while (--numRecords); + + if (!isExtended) + break; + + size_t processedSize = NFileHeader::kRecordSize; + RINOK(ReadStream(SeqStream, buf, &processedSize)) + if (processedSize != NFileHeader::kRecordSize) + { + error = k_ErrorType_UnexpectedEnd; + return S_OK; } + item.HeaderSize += NFileHeader::kRecordSize; + RINOK(Progress(item, 0)) + p = buf; + numRecords = 21; + isExtended = (Byte)p[21 * 24]; // (Byte)p[numRecords * 24]; + } + // optional checks for strict size consistency: + if (end != item.Size || packSum != item.PackSize) + { + item.Method_Error = true; // relaxed check + // return S_OK; } - if (min > item.Size) - return S_OK; } - if (item.PackSize >= (UInt64)1 << 63) + if (item.PackSize >= (UInt64)1 << 63) // optional check. It was checked in ParseSize() already return S_OK; - filled = true; error = k_ErrorType_OK; return S_OK; @@ -421,6 +418,8 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset) { + if (!OpenCallback) + return S_OK; const UInt64 pos = item.Get_DataPos() + posOffset; if (NumFiles - NumFiles_Prev < (1 << 16) // && NumRecords - NumRecords_Prev < (1 << 16) @@ -500,10 +499,7 @@ HRESULT CArchive::ReadDataToBuffer(const CItemEx &item, do { - if (OpenCallback) - { - RINOK(Progress(item, pos)) - } + RINOK(Progress(item, pos)) unsigned size = kBufSize; if (size > packSize) @@ -813,6 +809,7 @@ HRESULT CArchive::ReadItem2(CItemEx &item) item.LongLink_WasUsed_2 = false; item.HeaderError = false; + item.Method_Error = false; item.IsSignedChecksum = false; item.Prefix_WasUsed = false; @@ -838,13 +835,8 @@ HRESULT CArchive::ReadItem2(CItemEx &item) for (;;) { - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } - + RINOK(Progress(item, 0)) RINOK(GetNextItemReal(item)) - // NumRecords++; if (!filled) @@ -1064,9 +1056,14 @@ HRESULT CArchive::ReadItem2(CItemEx &item) // GNU TAR ignores (item.Size) in that case if (item.Size != 0 && item.Size != piSize) item.Pax_Error = true; - item.Size = piSize; - item.PackSize = piSize; - item.pax_size_WasUsed = true; + if (piSize >= ((UInt64)1 << 63)) + item.Pax_Error = true; + else + { + item.Size = piSize; + item.PackSize = piSize; + item.pax_size_WasUsed = true; + } } item.PaxTimes = paxInfo; diff --git a/CPP/7zip/Archive/Tar/TarItem.h b/CPP/7zip/Archive/Tar/TarItem.h index 112f38d..d4e2ea5 100644 --- a/CPP/7zip/Archive/Tar/TarItem.h +++ b/CPP/7zip/Archive/Tar/TarItem.h @@ -322,6 +322,7 @@ struct CPaxExtra struct CItemEx: public CItem { bool HeaderError; + bool Method_Error; bool IsSignedChecksum; bool Prefix_WasUsed; diff --git a/CPP/7zip/Archive/Udf/UdfIn.cpp b/CPP/7zip/Archive/Udf/UdfIn.cpp index ce87c54..a9e4ebf 100644 --- a/CPP/7zip/Archive/Udf/UdfIn.cpp +++ b/CPP/7zip/Archive/Udf/UdfIn.cpp @@ -500,10 +500,15 @@ size_t CFileId::Parse(const Byte *p, size_t size) processed += impLen; Id.Parse(p + processed, idLen); processed += idLen; + // const size_t processed2 = processed; for (;(processed & 3) != 0; processed++) if (p[processed] != 0) return 0; - if ((size_t)tag.CrcLen + 16 != processed) return 0; + // some program can create non-standard UDF file where CrcLen doesn't include Padding data + if ((size_t)tag.CrcLen + 16 != processed + // && (size_t)tag.CrcLen + 16 != processed2 // we can enable this check to support non-standard UDF + ) + return 0; return (processed <= size) ? processed : 0; } @@ -577,15 +582,20 @@ HRESULT CInArchive::ReadItem(unsigned volIndex, int fsIndex, const CLongAllocDes item.IcbTag.Parse(p + 16); + // maybe another FileType values are possible in rare cases. + // Shoud we ignore FileType here? if (fsIndex < 0) { + // if (item.IcbTag.FileType == ICB_FILE_TYPE_DIR) return S_FALSE; if (item.IcbTag.FileType != ICB_FILE_TYPE_METADATA && - item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_MIRROR) + item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_MIRROR && + item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_BITMAP) return S_FALSE; } else if ( item.IcbTag.FileType != ICB_FILE_TYPE_DIR && - item.IcbTag.FileType != ICB_FILE_TYPE_FILE) + item.IcbTag.FileType != ICB_FILE_TYPE_FILE && + item.IcbTag.FileType != ICB_FILE_TYPE_REAL_TIME_FILE) // M2TS files in /BDMV/STREAM/ in Blu-ray movie return S_FALSE; item.Parse(p); @@ -1210,7 +1220,7 @@ HRESULT CInArchive::Open2() if (tag.Id != DESC_TYPE_FileSet) return S_FALSE; - PRF(printf("\n FileSet", volIndex)); + PRF(printf("\n FileSet")); CFileSet fs; fs.RecordingTime.Parse(p + 16); // fs.InterchangeLevel = Get16(p + 18); diff --git a/CPP/7zip/Archive/Udf/UdfIn.h b/CPP/7zip/Archive/Udf/UdfIn.h index 9ccbf74..cbe1a27 100644 --- a/CPP/7zip/Archive/Udf/UdfIn.h +++ b/CPP/7zip/Archive/Udf/UdfIn.h @@ -250,9 +250,10 @@ enum EIcbFileType { ICB_FILE_TYPE_DIR = 4, ICB_FILE_TYPE_FILE = 5, - - ICB_FILE_TYPE_METADATA = 250, // 2.2.13.1 Metadata File - ICB_FILE_TYPE_METADATA_MIRROR = 251 + ICB_FILE_TYPE_REAL_TIME_FILE = 249, // 2.3.5.2.1 + ICB_FILE_TYPE_METADATA = 250, // 2.2.13.1 + ICB_FILE_TYPE_METADATA_MIRROR = 251, // 2.2.13.1 + ICB_FILE_TYPE_METADATA_BITMAP = 252 // 2.2.13.2 }; enum EIcbDescriptorType diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp index 788810f..9d77e87 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.cpp +++ b/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -1718,61 +1718,49 @@ HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) HRESULT CInArchive::FindCd(bool checkOffsetMode) { - CCdInfo &cdInfo = Vols.ecd; - - UInt64 endPos; - // There are no useful data in cache in most cases here. - // So here we don't use cache data from previous operations . - + // So here we don't use cache data from previous operations. InitBuf(); + UInt64 endPos; RINOK(InStream_GetSize_SeekToEnd(Stream, endPos)) _streamPos = endPos; - - // const UInt32 kBufSizeMax2 = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; - const size_t kBufSizeMax = ((size_t)1 << 17); // must be larger than kBufSizeMax2 - + const size_t kBufSizeMax = (size_t)1 << 17; // must be larger than + // (1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize const size_t bufSize = (endPos < kBufSizeMax) ? (size_t)endPos : kBufSizeMax; if (bufSize < kEcdSize) return S_FALSE; - // CByteArr byteBuffer(bufSize); - RINOK(AllocateBuffer(kBufSizeMax)) + { + RINOK(Seek_SavePos(endPos - bufSize)) + size_t processed = bufSize; + const HRESULT res = ReadStream(Stream, Buffer, &processed); + _streamPos += processed; + _bufCached = processed; + _bufPos = 0; + _cnt += processed; + if (res != S_OK) + return res; + if (processed != bufSize) + return S_FALSE; + } - RINOK(Seek_SavePos(endPos - bufSize)) - - size_t processed = bufSize; - HRESULT res = ReadStream(Stream, Buffer, &processed); - _streamPos += processed; - _bufCached = processed; - _bufPos = 0; - _cnt += processed; - if (res != S_OK) - return res; - if (processed != bufSize) - return S_FALSE; - + CCdInfo &cdInfo = Vols.ecd; for (size_t i = bufSize - kEcdSize + 1;;) { - if (i == 0) - return S_FALSE; - const Byte *buf = Buffer; - - for (;;) { - i--; - if (buf[i] == 0x50) - break; - if (i == 0) - return S_FALSE; - } - - if (Get32(buf + i) != NSignature::kEcd) - continue; + const Byte *p = buf + i; + do + if (p == buf) + return S_FALSE; + while (*(--p) != 0x50); - cdInfo.ParseEcd32(buf + i); + i = (size_t)(p - buf); + if (Get32(p) != NSignature::kEcd) + continue; + cdInfo.ParseEcd32(p); + } if (i >= kEcd64Locator_Size) { @@ -1793,29 +1781,24 @@ HRESULT CInArchive::FindCd(bool checkOffsetMode) // Most of the zip64 use fixed size Zip64 ECD // we try relative backward reading. - - UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + const UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); if (locatorIndex >= kEcd64_FullSize) if (checkOffsetMode || absEcd64 == locator.Ecd64Offset) { const Byte *ecd64 = buf + locatorIndex - kEcd64_FullSize; - if (Get32(ecd64) == NSignature::kEcd64) + if (Get32(ecd64) == NSignature::kEcd64 && + Get64(ecd64 + 4) == kEcd64_MainSize) { - UInt64 mainEcd64Size = Get64(ecd64 + 4); - if (mainEcd64Size == kEcd64_MainSize) - { - cdInfo.ParseEcd64e(ecd64 + 12); - ArcInfo.Base = (Int64)(absEcd64 - locator.Ecd64Offset); - // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; - return S_OK; - } + cdInfo.ParseEcd64e(ecd64 + 12); + ArcInfo.Base = (Int64)(absEcd64 - locator.Ecd64Offset); + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + return S_OK; } } // some zip64 use variable size Zip64 ECD. // we try to use absolute offset from locator. - if (absEcd64 != locator.Ecd64Offset) { if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK) @@ -1881,6 +1864,9 @@ HRESULT CInArchive::TryReadCd(CObjectVector &items, const CCdInfo &cdIn items.Clear(); IsCdUnsorted = false; + if ((Int64)cdOffset < 0) + return S_FALSE; + // _startLocalFromCd_Disk = (UInt32)(Int32)-1; // _startLocalFromCd_Offset = (UInt64)(Int64)-1; diff --git a/CPP/7zip/Archive/Zip/ZipOut.cpp b/CPP/7zip/Archive/Zip/ZipOut.cpp index 63f1a71..e8a21c2 100644 --- a/CPP/7zip/Archive/Zip/ZipOut.cpp +++ b/CPP/7zip/Archive/Zip/ZipOut.cpp @@ -49,42 +49,54 @@ void COutArchive::SeekToCurPos() // #define DOES_NEED_ZIP64(v) (v >= 0) +Z7_NO_INLINE void COutArchive::WriteBytes(const void *data, size_t size) { m_OutBuffer.WriteBytes(data, size); m_CurPos += size; } +Z7_NO_INLINE void COutArchive::Write8(Byte b) { m_OutBuffer.WriteByte(b); m_CurPos++; } +Z7_NO_INLINE void COutArchive::Write16(UInt16 val) { Write8((Byte)val); Write8((Byte)(val >> 8)); } +Z7_NO_INLINE void COutArchive::Write32(UInt32 val) { for (int i = 0; i < 4; i++) { - Write8((Byte)val); + // Write8((Byte)val); + m_OutBuffer.WriteByte((Byte)val); val >>= 8; } + m_CurPos += 4; } +#define WRITE_CONST_PAIR_16_16(a, b) { Write32((a) | ((UInt32)(b) << 16)); } + +Z7_NO_INLINE void COutArchive::Write64(UInt64 val) { for (int i = 0; i < 8; i++) { - Write8((Byte)val); + // Write8((Byte)val); + m_OutBuffer.WriteByte((Byte)val); val >>= 8; } + m_CurPos += 8; } +Z7_NO_INLINE void COutArchive::WriteExtra(const CExtraBlock &extra) { FOR_VECTOR (i, extra.SubBlocks) @@ -134,11 +146,9 @@ void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs) if (writeNtfs) { // windows explorer ignores that extra - Write16(NFileHeader::NExtraID::kNTFS); - Write16(k_Ntfs_ExtraSize); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kNTFS, k_Ntfs_ExtraSize) Write32(0); // reserved - Write16(NFileHeader::NNtfsExtra::kTagTime); - Write16(8 * 3); + WRITE_CONST_PAIR_16_16(NFileHeader::NNtfsExtra::kTagTime, 8 * 3) WriteNtfsTime(item.Ntfs_MTime); WriteNtfsTime(item.Ntfs_ATime); WriteNtfsTime(item.Ntfs_CTime); @@ -148,8 +158,7 @@ void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs) { // windows explorer ignores that extra // by specification : should we write to local header also? - Write16(NFileHeader::NExtraID::kUnixTime); - Write16(k_UnixTime_ExtraSize); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kUnixTime, k_UnixTime_ExtraSize) const Byte flags = (Byte)((unsigned)1 << NFileHeader::NUnixTime::kMTime); Write8(flags); UInt32 unixTime; @@ -217,8 +226,7 @@ void COutArchive::WriteLocalHeader(CItemOut &item, bool needCheck) if (isZip64) { - Write16(NFileHeader::NExtraID::kZip64); - Write16(8 + 8); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kZip64, 8 + 8) Write64(size); Write64(packSize); } @@ -357,8 +365,9 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const const UInt64 cdSize = cd64EndOffset - cdOffset; const bool cdOffset64 = DOES_NEED_ZIP64(cdOffset); const bool cdSize64 = DOES_NEED_ZIP64(cdSize); - const bool items64 = items.Size() >= 0xFFFF; - const bool isZip64 = (cdOffset64 || cdSize64 || items64); + const bool need_Items_64 = items.Size() >= 0xFFFF; + const unsigned items16 = (UInt16)(need_Items_64 ? 0xFFFF: items.Size()); + const bool isZip64 = (cdOffset64 || cdSize64 || need_Items_64); // isZip64 = true; // to test Zip64 @@ -371,8 +380,8 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const // const UInt32 extraSize = 1 << 26; // Write64(kEcd64_MainSize + extraSize); - Write16(45); // made by version - Write16(45); // extract version + WRITE_CONST_PAIR_16_16(45, // made by version + 45) // extract version Write32(0); // ThisDiskNumber Write32(0); // StartCentralDirectoryDiskNumber Write64((UInt64)items.Size()); @@ -389,10 +398,9 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const } Write32(NSignature::kEcd); - Write16(0); // ThisDiskNumber - Write16(0); // StartCentralDirectoryDiskNumber - Write16((UInt16)(items64 ? 0xFFFF: items.Size())); - Write16((UInt16)(items64 ? 0xFFFF: items.Size())); + WRITE_CONST_PAIR_16_16(0, 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber + Write16((UInt16)items16); + Write16((UInt16)items16); WRITE_32_VAL_SPEC(cdSize, cdSize64) WRITE_32_VAL_SPEC(cdOffset, cdOffset64) diff --git a/CPP/7zip/Bundles/SFXCon/SfxCon.cpp b/CPP/7zip/Bundles/SFXCon/SfxCon.cpp index aac4e28..9e2d13d 100644 --- a/CPP/7zip/Bundles/SFXCon/SfxCon.cpp +++ b/CPP/7zip/Bundles/SFXCon/SfxCon.cpp @@ -153,7 +153,7 @@ namespace NCommandType }; } -static const char *g_Commands = "txl"; +static const char * const g_Commands = "txl"; struct CArchiveCommand { diff --git a/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp b/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp index 0c09807..d4240d9 100644 --- a/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp +++ b/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp @@ -48,72 +48,60 @@ static bool ReadDataString(CFSTR fileName, LPCSTR startID, NIO::CInFile inFile; if (!inFile.Open(fileName)) return false; - const size_t kBufferSize = (1 << 12); + const size_t kBufferSize = 1 << 12; Byte buffer[kBufferSize]; - const unsigned signatureStartSize = MyStringLen(startID); - const unsigned signatureEndSize = MyStringLen(endID); + const size_t signatureStartSize = MyStringLen(startID + 1); + const size_t signatureEndSize = MyStringLen(endID + 1); size_t numBytesPrev = 0; bool writeMode = false; - UInt64 posTotal = 0; + UInt32 posTotal = 0; for (;;) { - if (posTotal > (1 << 20)) - return (stringResult.IsEmpty()); const size_t numReadBytes = kBufferSize - numBytesPrev; size_t processedSize; if (!inFile.ReadFull(buffer + numBytesPrev, numReadBytes, processedSize)) return false; if (processedSize == 0) return true; - const size_t numBytesInBuffer = numBytesPrev + processedSize; - UInt32 pos = 0; + numBytesPrev += processedSize; + size_t pos = 0; for (;;) { if (writeMode) { - if (pos + signatureEndSize > numBytesInBuffer) + if (pos + signatureEndSize > numBytesPrev) break; - if (memcmp(buffer + pos, endID, signatureEndSize) == 0) - return true; - const Byte b = buffer[pos]; + const Byte b = buffer[pos++]; if (b == 0) return false; + if (b == ';' && memcmp(buffer + pos, endID + 1, signatureEndSize) == 0) + return true; stringResult += (char)b; - pos++; } else { - if (pos + signatureStartSize > numBytesInBuffer) + if (pos + signatureStartSize > numBytesPrev) break; - if (memcmp(buffer + pos, startID, signatureStartSize) == 0) + const Byte b = buffer[pos++]; + if (b == ';' && memcmp(buffer + pos, startID + 1, signatureStartSize) == 0) { writeMode = true; pos += signatureStartSize; } - else - pos++; } } - numBytesPrev = numBytesInBuffer - pos; - posTotal += pos; + posTotal += (UInt32)pos; + if (posTotal > (1 << 21)) + return stringResult.IsEmpty(); + numBytesPrev -= pos; memmove(buffer, buffer + pos, numBytesPrev); } } -static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 }; -static char kEndID[] = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 }; - -static struct CInstallIDInit -{ - CInstallIDInit() - { - kStartID[0] = ';'; - kEndID[0] = ';'; - } -} g_CInstallIDInit; - +static const char * const kStartID = ",!@Install@!UTF-8!"; +static const char * const kEndID = ",!@InstallEnd@!"; #if defined(_WIN32) && defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE) #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1; diff --git a/CPP/7zip/Common/FileStreams.cpp b/CPP/7zip/Common/FileStreams.cpp index f90e280..b7e4fbe 100644 --- a/CPP/7zip/Common/FileStreams.cpp +++ b/CPP/7zip/Common/FileStreams.cpp @@ -753,7 +753,7 @@ Z7_COM7F_IMF(CInFileStream::GetProperty(PROPID propID, PROPVARIANT *value)) { if (StoreOwnerName) { - const uid_t gid = st.st_gid; + const gid_t gid = st.st_gid; { if (!OwnerGroup.IsEmpty() && _gid == gid) prop = OwnerGroup; diff --git a/CPP/7zip/Common/FileStreams.h b/CPP/7zip/Common/FileStreams.h index 212d4f0..7f465cf 100644 --- a/CPP/7zip/Common/FileStreams.h +++ b/CPP/7zip/Common/FileStreams.h @@ -84,8 +84,8 @@ public: BY_HANDLE_FILE_INFORMATION _info; #else struct stat _info; - UInt32 _uid; - UInt32 _gid; + uid_t _uid; // uid_t can be unsigned or signed int + gid_t _gid; UString OwnerName; UString OwnerGroup; bool StoreOwnerId; diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 7fe18fb..73974e6 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -426,7 +426,7 @@ static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) } } -static const char *g_Commands = "audtexlbih"; +static const char * const g_Commands = "audtexlbih"; static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) { diff --git a/CPP/7zip/UI/Common/ArchiveName.cpp b/CPP/7zip/UI/Common/ArchiveName.cpp index 3c0976d..f859d94 100644 --- a/CPP/7zip/UI/Common/ArchiveName.cpp +++ b/CPP/7zip/UI/Common/ArchiveName.cpp @@ -17,14 +17,14 @@ using namespace NWindows; using namespace NFile; -static const char *g_ArcExts = +static const char * const g_ArcExts = "7z" "\0" "zip" "\0" "tar" "\0" "wim" "\0"; -static const char *g_HashExts = +static const char * const g_HashExts = "sha256" "\0"; diff --git a/CPP/7zip/UI/Common/Bench.cpp b/CPP/7zip/UI/Common/Bench.cpp index eb24e7f..316c980 100644 --- a/CPP/7zip/UI/Common/Bench.cpp +++ b/CPP/7zip/UI/Common/Bench.cpp @@ -3038,7 +3038,7 @@ AString GetProcessThreadsInfo(const NSystem::CProcessAffinity &ti) FOR_VECTOR (i, ti.Groups.GroupSizes) { if (i != 0) - s.Add_Char(' '); + s.Add_Space(); s.Add_UInt32(ti.Groups.GroupSizes[i]); } } @@ -3773,10 +3773,11 @@ HRESULT Bench( #ifndef Z7_ST - if (threadsInfo.Get() && threadsInfo.GetNumProcessThreads() != 0) - numCPUs = threadsInfo.GetNumProcessThreads(); - else + if (!threadsInfo.Get() + || (numCPUs = threadsInfo.GetNumProcessThreads()) == 0) numCPUs = NSystem::GetNumberOfProcessors(); + // numCPUs : is number of threads assigned to process with affinity, + // or it's total number of threads in all groups, if IsGroupMode == true, and there is default affinity. #endif diff --git a/CPP/7zip/UI/Console/List.cpp b/CPP/7zip/UI/Console/List.cpp index 874caef..2d9f5a3 100644 --- a/CPP/7zip/UI/Console/List.cpp +++ b/CPP/7zip/UI/Console/List.cpp @@ -201,8 +201,8 @@ static const CFieldInfoInit kStandardFieldTable[] = { kpidPath, "Name", kLeft, kLeft, 2, 24 } }; -const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width -static const char *g_Spaces = +static const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width +static const char * const g_Spaces = " " ; static void PrintSpaces(unsigned numSpaces) diff --git a/CPP/7zip/UI/FileManager/BrowseDialog2.cpp b/CPP/7zip/UI/FileManager/BrowseDialog2.cpp index ee98ab4..9f083c5 100644 --- a/CPP/7zip/UI/FileManager/BrowseDialog2.cpp +++ b/CPP/7zip/UI/FileManager/BrowseDialog2.cpp @@ -9,6 +9,7 @@ #include #include "../../../Common/IntToString.h" +#include "../../../Common/MyCom.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" @@ -19,6 +20,7 @@ #include "../../../Windows/Menu.h" #include "../../../Windows/ProcessUtils.h" #include "../../../Windows/PropVariantConv.h" +#include "../../../Windows/Shell.h" #include "../../../Windows/Control/ComboBox.h" #include "../../../Windows/Control/Dialog.h" #include "../../../Windows/Control/Edit.h" @@ -57,7 +59,7 @@ static const int kParentIndex = -1; // static const UINT k_Message_RefreshPathEdit = WM_APP + 1; -static const wchar_t *k_Message_Link_operation_was_Blocked = +static const wchar_t * const k_Message_Link_operation_was_Blocked = L"link openning was blocked by 7-Zip"; extern UString HResultToMessage(HRESULT errorCode); @@ -978,35 +980,61 @@ void CBrowseDialog2::OnHelp() #endif +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, + HWND hwnd, const UString &path, LPITEMIDLIST *ppidl); + HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process); HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process) { UString path2 = path; - - #ifdef _WIN32 + UINT32 result; + { +#ifdef _WIN32 + NShell::CItemIDList pidl; + // SHELLEXECUTEINFO::pidl is more accurate way than SHELLEXECUTEINFO::lpFile + { + CMyComPtr desktop; + if (SHGetDesktopFolder(&desktop) == S_OK && desktop) + if (ShellFolder_ParseDisplayName(desktop, + NULL, // HWND : do we need (window) or NULL here? + path, + &pidl) != S_OK) + pidl.Detach(); + } { const int dot = path2.ReverseFind_Dot(); const int separ = path2.ReverseFind_PathSepar(); - if (dot < 0 || dot < separ) - path2.Add_Dot(); + if (separ != (int)path2.Len() - 1) + if (dot < 0 || dot < separ) + path2.Add_Dot(); } - #endif +#endif // _WIN32 - UINT32 result; - #ifndef _UNICODE if (g_IsNT) { SHELLEXECUTEINFOW execInfo; + memset(&execInfo, 0, sizeof(execInfo)); + // execInfo.hwnd = NULL; + // execInfo.lpVerb = NULL; + // execInfo.lpFile = NULL; + // execInfo.lpDirectory = NULL; + // execInfo.lpParameters = NULL; + // execInfo.hProcess = NULL; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT; - execInfo.hwnd = NULL; - execInfo.lpVerb = NULL; - execInfo.lpFile = path2; - execInfo.lpParameters = NULL; - execInfo.lpDirectory = dir.IsEmpty() ? NULL : (LPCWSTR)dir; + if (!dir.IsEmpty()) + execInfo.lpDirectory = dir; execInfo.nShow = SW_SHOWNORMAL; - execInfo.hProcess = NULL; + + if ((LPCITEMIDLIST)pidl) + { + execInfo.lpIDList = pidl; + execInfo.fMask |= SEE_MASK_IDLIST; + } + else + execInfo.lpFile = path2; + typedef BOOL (WINAPI * Func_ShellExecuteExW)(LPSHELLEXECUTEINFOW lpExecInfo); Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION const @@ -1024,34 +1052,40 @@ Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION #endif { SHELLEXECUTEINFO execInfo; + memset(&execInfo, 0, sizeof(execInfo)); + // execInfo.hwnd = NULL; + // execInfo.lpVerb = NULL; + // execInfo.lpFile = NULL; + // execInfo.lpDirectory = NULL; + // execInfo.lpParameters = NULL; + // execInfo.hProcess = NULL; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS #ifndef UNDER_CE | SEE_MASK_FLAG_DDEWAIT #endif ; - execInfo.hwnd = NULL; - execInfo.lpVerb = NULL; + execInfo.nShow = SW_SHOWNORMAL; const CSysString sysPath (GetSystemString(path2)); const CSysString sysDir (GetSystemString(dir)); - execInfo.lpFile = sysPath; - execInfo.lpParameters = NULL; - execInfo.lpDirectory = - #ifdef UNDER_CE - NULL - #else - sysDir.IsEmpty() ? NULL : (LPCTSTR)sysDir - #endif - ; - execInfo.nShow = SW_SHOWNORMAL; - execInfo.hProcess = NULL; + #ifndef UNDER_CE + if (!sysDir.IsEmpty()) + execInfo.lpDirectory = sysDir; + #endif + + if ((LPCITEMIDLIST)pidl) + { + execInfo.lpIDList = pidl; + execInfo.fMask |= SEE_MASK_IDLIST; + } + else + execInfo.lpFile = sysPath; ::ShellExecuteEx(&execInfo); result = (UINT32)(UINT_PTR)execInfo.hInstApp; process.Attach(execInfo.hProcess); } - // DEBUG_PRINT_NUM("-- ShellExecuteEx -- execInfo.hInstApp = ", result) - + } if (result <= 32) { switch (result) @@ -1063,10 +1097,8 @@ Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION // L"There is no application associated with the given file name extension", ); } - return E_FAIL; // fixed in 15.13. Can we use it for any Windows version? } - return S_OK; } diff --git a/CPP/7zip/UI/FileManager/FSFolder.cpp b/CPP/7zip/UI/FileManager/FSFolder.cpp index 7956d86..51dfaa9 100644 --- a/CPP/7zip/UI/FileManager/FSFolder.cpp +++ b/CPP/7zip/UI/FileManager/FSFolder.cpp @@ -748,8 +748,8 @@ Z7_COM7F_IMF2(Int32, CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPI case kpidMTime: return CompareFileTime(&fi1.MTime, &fi2.MTime); case kpidIsDir: { - bool isDir1 = /* ss1 ? false : */ fi1.IsDir(); - bool isDir2 = /* ss2 ? false : */ fi2.IsDir(); + const bool isDir1 = /* ss1 ? false : */ fi1.IsDir(); + const bool isDir2 = /* ss2 ? false : */ fi2.IsDir(); if (isDir1 == isDir2) return 0; return isDir1 ? -1 : 1; @@ -798,7 +798,9 @@ Z7_COM7F_IMF2(Int32, CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPI return MyStringCompareNoCase(comment1, comment2); } case kpidPrefix: - if (fi1.Parent < 0) return (fi2.Parent < 0) ? 0 : -1; + if (fi1.Parent == fi2.Parent) + return 0; + if (fi1.Parent < 0) return -1; if (fi2.Parent < 0) return 1; return CompareFileNames_ForFolderList( Folders[fi1.Parent], diff --git a/CPP/7zip/UI/FileManager/LangPage.cpp b/CPP/7zip/UI/FileManager/LangPage.cpp index 3aeaf13..626c91b 100644 --- a/CPP/7zip/UI/FileManager/LangPage.cpp +++ b/CPP/7zip/UI/FileManager/LangPage.cpp @@ -253,8 +253,7 @@ bool CLangPage::OnInit() temp += " "; temp += rec.Mark; } - const int index = (int)_langCombo.AddString(temp); - _langCombo.SetItemData(index, (LPARAM)rec.LangInfoIndex); + const int index = (int)_langCombo.AddString_SetItemData(temp, (LPARAM)rec.LangInfoIndex); if (rec.IsSelected) _langCombo.SetCurSel(index); } diff --git a/CPP/7zip/UI/FileManager/MenuPage.cpp b/CPP/7zip/UI/FileManager/MenuPage.cpp index e8736b8..61dd8cb 100644 --- a/CPP/7zip/UI/FileManager/MenuPage.cpp +++ b/CPP/7zip/UI/FileManager/MenuPage.cpp @@ -222,8 +222,7 @@ bool CMenuPage::OnInit() s.Add_UInt32(val); if (i == 0) s.Insert(0, L"* "); - const int index = (int)_zoneCombo.AddString(s); - _zoneCombo.SetItemData(index, (LPARAM)val); + const int index = (int)_zoneCombo.AddString_SetItemData(s, (LPARAM)val); if (val == wz) _zoneCombo.SetCurSel(index); } diff --git a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp index aa56ef5..9d78368 100644 --- a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp +++ b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp @@ -825,7 +825,10 @@ void CPanel::EditItem(unsigned index, bool useEditor) return; } CProcess process; - StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this, process); + StartEditApplication(GetItemFullPath(index), useEditor, + // (HWND)*this, + GetParent(), + process); } @@ -854,7 +857,10 @@ void CPanel::OpenFolderExternal(unsigned index) path.Add_PathSepar(); } - StartApplicationDontWait(prefix, path, (HWND)*this); + StartApplicationDontWait(prefix, path, + // (HWND)*this + GetParent() + ); } @@ -981,7 +987,10 @@ void CPanel::OpenItem(unsigned index, bool tryInternal, bool tryExternal, const { // SetCurrentDirectory opens HANDLE to folder!!! // NDirectory::MySetCurrentDirectory(prefix); - StartApplicationDontWait(prefix, fullPath, (HWND)*this); + StartApplicationDontWait(prefix, fullPath, + // (HWND)*this + GetParent() + ); } } @@ -1732,9 +1741,15 @@ void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExterna CProcess process; HRESULT res; if (editMode) - res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process); + res = StartEditApplication(fs2us(tempFilePath), useEditor, + // (HWND)*this, + GetParent(), + process); else - res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process); + res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), + // (HWND)*this, + GetParent(), + process); if ((HANDLE)process == NULL) { diff --git a/CPP/7zip/UI/FileManager/PanelMenu.cpp b/CPP/7zip/UI/FileManager/PanelMenu.cpp index 9086996..e655843 100644 --- a/CPP/7zip/UI/FileManager/PanelMenu.cpp +++ b/CPP/7zip/UI/FileManager/PanelMenu.cpp @@ -488,7 +488,9 @@ struct CFolderPidls }; -static HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, + HWND hwnd, const UString &path, LPITEMIDLIST *ppidl); +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, HWND hwnd, const UString &path, LPITEMIDLIST *ppidl) { ULONG eaten = 0; diff --git a/CPP/7zip/UI/FileManager/PanelSort.cpp b/CPP/7zip/UI/FileManager/PanelSort.cpp index f95f8ee..57ac877 100644 --- a/CPP/7zip/UI/FileManager/PanelSort.cpp +++ b/CPP/7zip/UI/FileManager/PanelSort.cpp @@ -82,7 +82,7 @@ static inline const wchar_t *GetExtensionPtr(const UString &name) void CPanel::SetSortRawStatus() { - _isRawSortProp = false; + _isRawSortProp = 0; // false; FOR_VECTOR (i, _columns) { const CPropColumn &prop = _columns[i]; @@ -95,21 +95,15 @@ void CPanel::SetSortRawStatus() } -static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) +static int CALLBACK CompareItems2(const LPARAM lParam1, const LPARAM lParam2, + const CPanel * const panel, const PROPID propID, const Int32 isRawProp) { - if (lpData == 0) - return 0; - CPanel *panel = (CPanel*)lpData; - - - PROPID propID = panel->_sortID; - if (propID == kpidNoProperty) return MyCompare(lParam1, lParam2); - if (panel->_isRawSortProp) + if (isRawProp) { - // Sha1, NtSecurity, NtReparse + // Sha1, Checksum, NtSecurity, NtReparse const void *data1; const void *data2; UInt32 dataSize1; @@ -135,7 +129,7 @@ static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) } if (panel->_folderCompare) - return panel->_folderCompare->CompareItems((UInt32)lParam1, (UInt32)lParam2, propID, panel->_isRawSortProp); + return panel->_folderCompare->CompareItems((UInt32)lParam1, (UInt32)lParam2, propID, isRawProp); switch (propID) { @@ -189,16 +183,41 @@ int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) if (lParam1 == (int)kParentIndex) return -1; if (lParam2 == (int)kParentIndex) return 1; - CPanel *panel = (CPanel*)lpData; + const CPanel *panel = (CPanel*)lpData; const bool isDir1 = panel->IsItem_Folder((unsigned)lParam1); const bool isDir2 = panel->IsItem_Folder((unsigned)lParam2); - - if (isDir1 && !isDir2) return -1; - if (isDir2 && !isDir1) return 1; + if (isDir1 != isDir2) + return isDir1 ? -1 : 1; - const int result = CompareItems2(lParam1, lParam2, lpData); - return panel->_ascending ? result: (-result); + /* + we have up to 3 iterations: + 1: prop, + 2: kpidName, kpidPrefix + 3: prop, kpidName, kpidPrefix + 3: kpidPrefix, kpidName, kpidPrefix : is some rare case + */ + PROPID propID = panel->_sortID; + int res = 0; + for (unsigned iter = 0; iter < 3; iter++) + { + res = CompareItems2(lParam1, lParam2, panel, propID, + iter ? 0 : panel->_isRawSortProp); + if (res) + break; + if (propID == kpidName) + { + // if (!_flatMode.IsEmpty()) break; // !_flatMode ; + propID = kpidPrefix; + continue; + } + if (iter) + break; + propID = kpidName; + } + if (res == 0) + res = MyCompare(lParam1, lParam2); // order of LoadSubItems() + return panel->_ascending ? res: -res; } diff --git a/CPP/7zip/UI/GUI/BenchmarkDialog.cpp b/CPP/7zip/UI/GUI/BenchmarkDialog.cpp index 1686c69..980161f 100644 --- a/CPP/7zip/UI/GUI/BenchmarkDialog.cpp +++ b/CPP/7zip/UI/GUI/BenchmarkDialog.cpp @@ -440,11 +440,9 @@ static const size_t kMaxDicSize = (size_t)1 << (22 + sizeof(size_t) / 4 * 5); static int ComboBox_Add_UInt32(NWindows::NControl::CComboBox &cb, UInt32 v) { - TCHAR s[16]; + WCHAR s[16]; ConvertUInt32ToString(v, s); - const int index = (int)cb.AddString(s); - cb.SetItemData(index, (LPARAM)v); - return index; + return (int)cb.AddString_SetItemData(s, (LPARAM)v); } @@ -481,21 +479,17 @@ bool CBenchmarkDialog::OnInit() _consoleEdit.SendMsg(WM_SETFONT, (WPARAM)_font._font, TRUE); } - UInt32 numCPUs = 1; + UInt32 numCPUs = 1; // process threads + UInt32 numCPUs_Sys = 1; // system threads { - AString s ("/ "); - NSystem::CProcessAffinity threadsInfo; threadsInfo.InitST(); +#ifndef Z7_ST + threadsInfo.Get_and_return_NumProcessThreads_and_SysThreads(numCPUs, numCPUs_Sys); +#endif - #ifndef Z7_ST - if (threadsInfo.Get() && threadsInfo.processAffinityMask != 0) - numCPUs = threadsInfo.GetNumProcessThreads(); - else - numCPUs = NSystem::GetNumberOfProcessors(); - #endif - + AString s ("/ "); s.Add_UInt32(numCPUs); s += GetProcessThreadsInfo(threadsInfo); SetItemTextA(IDT_BENCH_HARDWARE_THREADS, s); @@ -506,10 +500,8 @@ bool CBenchmarkDialog::OnInit() SetItemTextA(IDT_BENCH_SYS1, s); if (s != s2 && !s2.IsEmpty()) SetItemTextA(IDT_BENCH_SYS2, s2); - } - { - AString registers; - GetCpuName_MultiLine(s, registers); + + GetCpuName_MultiLine(s, s2); // s2==registers SetItemTextA(IDT_BENCH_CPU, s); } { @@ -526,22 +518,18 @@ bool CBenchmarkDialog::OnInit() // ----- Num Threads ---------- - if (numCPUs < 1) - numCPUs = 1; - numCPUs = MyMin(numCPUs, (UInt32)(1 << 6)); // it's WIN32 limit - UInt32 numThreads = Sync.NumThreads; - if (numThreads == (UInt32)(Int32)-1) numThreads = numCPUs; - if (numThreads > 1) - numThreads &= ~(UInt32)1; - const UInt32 kNumThreadsMax = (1 << 12); - if (numThreads > kNumThreadsMax) - numThreads = kNumThreadsMax; + numThreads &= ~(UInt32)1; + if (numThreads == 0) + numThreads = 1; + numThreads = MyMin(numThreads, (UInt32)(1u << 14)); m_NumThreads.Attach(GetItem(IDC_BENCH_NUM_THREADS)); - const UInt32 numTheads_Combo = numCPUs * 2; + if (numCPUs_Sys == 0) + numCPUs_Sys = 1; + const UInt32 numTheads_Combo = numCPUs_Sys * 2; UInt32 v = 1; int cur = 0; for (; v <= numTheads_Combo;) @@ -1069,16 +1057,17 @@ static void AddUsageString(UString &s, const CTotalBenchRes &info) numIter = 1000000; UInt64 usage = GetUsagePercents(info.Usage / numIter); - wchar_t w[64]; - ConvertUInt64ToString(usage, w); - unsigned len = MyStringLen(w); + wchar_t w[32]; + wchar_t *p = ConvertUInt64ToString(usage, w); + p[0] = '%'; + p[1] = 0; + unsigned len = (unsigned)(size_t)(p - w); while (len < 5) { s.Add_Space(); len++; } s += w; - s += "%"; } diff --git a/CPP/7zip/UI/GUI/CompressDialog.cpp b/CPP/7zip/UI/GUI/CompressDialog.cpp index 85d7186..53e56fe 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.cpp +++ b/CPP/7zip/UI/GUI/CompressDialog.cpp @@ -506,8 +506,7 @@ bool CCompressDialog::OnInit() { const unsigned arcIndex = ArcIndices[i]; const CArcInfoEx &ai = (*ArcFormats)[arcIndex]; - const int index = (int)m_Format.AddString(ai.Name); - m_Format.SetItemData(index, (LPARAM)arcIndex); + const int index = (int)m_Format.AddString_SetItemData(ai.Name, (LPARAM)arcIndex); if (!needSetMain) { if (Info.FormatIndex == (int)arcIndex) @@ -540,11 +539,6 @@ bool CCompressDialog::OnInit() AddComboItems(m_PathMode, k_PathMode_IDs, Z7_ARRAY_SIZE(k_PathMode_IDs), k_PathMode_Vals, Info.PathMode); - - TCHAR s[32] = { TEXT('/'), TEXT(' '), 0 }; - ConvertUInt32ToString(NSystem::GetNumberOfProcessors(), s + 2); - SetItemText(IDT_COMPRESS_HARDWARE_THREADS, s); - CheckButton(IDX_COMPRESS_SHARED, Info.OpenShareForWrite); CheckButton(IDX_COMPRESS_DEL, Info.DeleteAfterCompressing); @@ -653,7 +647,19 @@ void CCompressDialog::EnableMultiCombo(unsigned id) EnableItem(id, enable); } -static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s); +static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s) +{ + return cb.AddString((CSysString)s); +} + +static LRESULT ComboBox_AddStringAscii_SetItemData(NControl::CComboBox &cb, + const char *s, LPARAM lParam) +{ + const LRESULT index = ComboBox_AddStringAscii(cb, s); + if (index >= 0) // optional check + cb.SetItemData((int)index, lParam); + return index; +} static void Combine_Two_BoolPairs(const CBoolPair &b1, const CBoolPair &b2, CBool1 &res) { @@ -1604,20 +1610,14 @@ void CCompressDialog::SetLevel2() AddLangString(s, langID); } } - const int index = (int)m_Level.AddString(s); - m_Level.SetItemData(index, (LPARAM)i); + m_Level.AddString_SetItemData(s, (LPARAM)i); } } SetNearestSelectComboBox(m_Level, level); } -static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s) -{ - return cb.AddString((CSysString)s); -} - -static const char *k_Auto_Prefix = "* "; +static const char * const k_Auto_Prefix = "* "; static void Modify_Auto(AString &s) { @@ -1690,8 +1690,8 @@ void CCompressDialog::SetMethod2(int keepMethodId) writtenMethodId = -1; Modify_Auto(s); } - const int itemIndex = (int)ComboBox_AddStringAscii(m_Method, s); - m_Method.SetItemData(itemIndex, writtenMethodId); + const int itemIndex = (int)ComboBox_AddStringAscii_SetItemData(m_Method, + s, writtenMethodId); if (keepMethodId == methodID) { m_Method.SetCurSel(itemIndex); @@ -1731,7 +1731,7 @@ void CCompressDialog::SetEncryptionMethod() } else if (ai.Is_Zip()) { - int index = FindRegistryFormat(ai.Name); + const int index = FindRegistryFormat(ai.Name); UString encryptionMethod; if (index >= 0) { @@ -1836,9 +1836,7 @@ static int Combo_AddDict2(NWindows::NControl::CComboBox &cb, size_t sizeReal, si s.Add_Char('B'); if (sizeReal == k_Auto_Dict) Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(cb, s); - cb.SetItemData(index, (LPARAM)sizeReal); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(cb, s, (LPARAM)sizeReal); } int CCompressDialog::AddDict2(size_t sizeReal, size_t sizeShow) @@ -2201,9 +2199,7 @@ int CCompressDialog::AddOrder(UInt32 size) { char s[32]; ConvertUInt32ToString(size, s); - const int index = (int)ComboBox_AddStringAscii(m_Order, s); - m_Order.SetItemData(index, (LPARAM)size); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(m_Order, s, (LPARAM)size); } int CCompressDialog::AddOrder_Auto() @@ -2211,9 +2207,7 @@ int CCompressDialog::AddOrder_Auto() AString s; s.Add_UInt32(_auto_Order); Modify_Auto(s); - int index = (int)ComboBox_AddStringAscii(m_Order, s); - m_Order.SetItemData(index, (LPARAM)(INT_PTR)(-1)); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(m_Order, s, (LPARAM)(INT_PTR)(-1)); } void CCompressDialog::SetOrder2() @@ -2490,9 +2484,7 @@ void CCompressDialog::SetSolidBlockSize2() AString s; Add_Size(s, _auto_Solid); Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(m_Solid, s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)(Int32)-1); - curSel = index; + curSel = (int)ComboBox_AddStringAscii_SetItemData(m_Solid, s, (LPARAM)(UInt32)(Int32)-1); } if (is7z) @@ -2501,8 +2493,7 @@ void CCompressDialog::SetSolidBlockSize2() // kSolidLog_NoSolid = 0 for xz means default blockSize if (is7z) LangString(IDS_COMPRESS_NON_SOLID, s); - const int index = (int)m_Solid.AddString(s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)kSolidLog_NoSolid); + const int index = (int)m_Solid.AddString_SetItemData(s, (LPARAM)(UInt32)kSolidLog_NoSolid); if (defaultBlockSize == kSolidLog_NoSolid) curSel = index; } @@ -2511,16 +2502,15 @@ void CCompressDialog::SetSolidBlockSize2() { AString s; Add_Size(s, (UInt64)1 << i); - const int index = (int)ComboBox_AddStringAscii(m_Solid, s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)i); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_Solid, s, (LPARAM)(UInt32)i); if (defaultBlockSize != (UInt32)(Int32)-1) if (i <= defaultBlockSize || index <= 1) curSel = index; } { - const int index = (int)m_Solid.AddString(LangString(IDS_COMPRESS_SOLID)); - m_Solid.SetItemData(index, (LPARAM)kSolidLog_FullSolid); + const int index = (int)m_Solid.AddString_SetItemData( + LangString(IDS_COMPRESS_SOLID), (LPARAM)kSolidLog_FullSolid); if (defaultBlockSize == kSolidLog_FullSolid) curSel = index; } @@ -2564,7 +2554,7 @@ static bool Is_Zstd_Mt_Supported() } */ -static const char *k_ST_Threads = " (ST)"; +static const char * const k_ST_Threads = " (ST)"; void CCompressDialog::SetNumThreads2() { @@ -2575,15 +2565,31 @@ void CCompressDialog::SetNumThreads2() if (!fi.MultiThread_()) return; - const UInt32 numHardwareThreads = NSystem::GetNumberOfProcessors(); - // 64; // for debug: + UInt32 numCPUs = 1; // process threads + UInt32 numHardwareThreads = 1; // system threads + NSystem::CProcessAffinity threadsInfo; + threadsInfo.InitST(); +#ifndef Z7_ST + threadsInfo.Get_and_return_NumProcessThreads_and_SysThreads(numCPUs, numHardwareThreads); +#endif - UInt32 defaultValue = numHardwareThreads; + AString s ("/ "); + { + s.Add_UInt32(numCPUs); + if (numCPUs != numHardwareThreads) + { + s += " / "; + s.Add_UInt32(numHardwareThreads); + } + SetItemTextA(IDT_COMPRESS_HARDWARE_THREADS, s.Ptr()); + } + + UInt32 defaultValue = numCPUs; bool useAutoThreads = true; { const CArcInfoEx &ai = Get_ArcInfoEx(); - int index = FindRegistryFormat(ai.Name); + const int index = FindRegistryFormat(ai.Name); if (index >= 0) { const NCompression::CFormatOptions &fo = m_RegistryInfo.Formats[index]; @@ -2597,19 +2603,19 @@ void CCompressDialog::SetNumThreads2() // const UInt32 num_ZSTD_threads_MAX = Is_Zstd_Mt_Supported() ? MY_ZSTDMT_NBWORKERS_MAX : 0; - UInt32 numAlgoThreadsMax = numHardwareThreads * 2; const int methodID = GetMethodID(); - const bool isZip = IsZipFormat(); + + UInt32 numAlgoThreadsMax = numHardwareThreads * 2; // for unknow methods if (isZip) numAlgoThreadsMax = 8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit else if (IsXzFormat()) - numAlgoThreadsMax = 256 * 2; + numAlgoThreadsMax = 256 * 2; // MTCODER_THREADS_MAX * 2 else switch (methodID) { case kLZMA: numAlgoThreadsMax = 2; break; - case kLZMA2: numAlgoThreadsMax = 256; break; + case kLZMA2: numAlgoThreadsMax = 256 * 2; break; // MTCODER_THREADS_MAX * 2 case kBZip2: numAlgoThreadsMax = 64; break; // case kZSTD: numAlgoThreadsMax = num_ZSTD_threads_MAX; break; case kCopy: @@ -2619,9 +2625,9 @@ void CCompressDialog::SetNumThreads2() case kPPMdZip: numAlgoThreadsMax = 1; } - UInt32 autoThreads = numHardwareThreads; + UInt32 autoThreads = numCPUs; if (autoThreads > numAlgoThreadsMax) - autoThreads = numAlgoThreadsMax; + autoThreads = numAlgoThreadsMax; const UInt64 memUse_Limit = Get_MemUse_Bytes(); @@ -2676,13 +2682,12 @@ void CCompressDialog::SetNumThreads2() int curSel = -1; { - AString s; + s.Empty(); s.Add_UInt32(autoThreads); if (autoThreads == 0) s += k_ST_Threads; Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(m_NumThreads, s); - m_NumThreads.SetItemData(index, (LPARAM)(INT_PTR)(-1)); - // m_NumThreads.SetItemData(index, autoThreads); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_NumThreads, + s, (LPARAM)(INT_PTR)(-1)); if (useAutoThreads) curSel = index; } @@ -2693,11 +2698,11 @@ void CCompressDialog::SetNumThreads2() 1; i <= numHardwareThreads * 2 && i <= numAlgoThreadsMax; i++) { - AString s; + s.Empty(); s.Add_UInt32(i); if (i == 0) s += k_ST_Threads; - const int index = (int)ComboBox_AddStringAscii(m_NumThreads, s); - m_NumThreads.SetItemData(index, (LPARAM)(UInt32)i); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_NumThreads, + s, (LPARAM)(UInt32)i); if (!useAutoThreads && i == defaultValue) curSel = index; } @@ -2754,9 +2759,7 @@ int CCompressDialog::AddMemComboItem(UInt64 val, bool isPercent, bool isDefault) sRegistry.DeleteBack(); } const unsigned dataIndex = _memUse_Strings.Add(sRegistry); - const int index = (int)m_MemUse.AddString(sUser); - m_MemUse.SetItemData(index, (LPARAM)dataIndex); - return index; + return (int)m_MemUse.AddString_SetItemData(sUser, (LPARAM)dataIndex); } @@ -3439,11 +3442,7 @@ static const unsigned kTimePrec_1ns = 3; static void AddTimeOption(UString &s, UInt32 val, const UString &unit, const char *sys = NULL) { // s += " : "; - { - AString s2; - s2.Add_UInt32(val); - s += s2; - } + s.Add_UInt32(val); s.Add_Space(); s += unit; if (sys) @@ -3476,9 +3475,7 @@ int COptionsDialog::AddPrec(unsigned prec, bool isDefault) } else s.Add_UInt32(prec); - const int index = (int)m_Prec.AddString(s); - m_Prec.SetItemData(index, (LPARAM)writePrec); - return index; + return (int)m_Prec.AddString_SetItemData(s, (LPARAM)writePrec); } diff --git a/CPP/7zip/UI/GUI/CompressDialog.rc b/CPP/7zip/UI/GUI/CompressDialog.rc index 9c3ed88..df1516c 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.rc +++ b/CPP/7zip/UI/GUI/CompressDialog.rc @@ -87,8 +87,8 @@ BEGIN COMBOBOX IDC_COMPRESS_SOLID, g1x, 144, g1xs, 140, MY_COMBO LTEXT "Number of CPU &threads:", IDT_COMPRESS_THREADS, m, 167, g0xs, 8 - COMBOBOX IDC_COMPRESS_THREADS, g1x, 165, g1xs - 35, 140, MY_COMBO - RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 35 + 10, 167, 25, MY_TEXT_NOPREFIX + COMBOBOX IDC_COMPRESS_THREADS, g1x, 165, g1xs - 40, 140, MY_COMBO + RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 40, 167, 40, 16, SS_NOPREFIX LTEXT "Memory usage for Compressing:", IDT_COMPRESS_MEMORY, m, 184, g2xs, 8 diff --git a/CPP/7zip/UI/GUI/ExtractDialog.cpp b/CPP/7zip/UI/GUI/ExtractDialog.cpp index 4628482..467cf18 100644 --- a/CPP/7zip/UI/GUI/ExtractDialog.cpp +++ b/CPP/7zip/UI/GUI/ExtractDialog.cpp @@ -102,8 +102,7 @@ void AddComboItems(NControl::CComboBox &combo, const UInt32 *langIDs, unsigned n { UString s = LangString(langIDs[i]); s.RemoveChar(L'&'); - const int index = (int)combo.AddString(s); - combo.SetItemData(index, (LPARAM)i); + combo.AddString_SetItemData(s, (LPARAM)i); if (values[i] == curVal) curSel = i; } diff --git a/CPP/Common/Common0.h b/CPP/Common/Common0.h index 55606cd..5781a95 100644 --- a/CPP/Common/Common0.h +++ b/CPP/Common/Common0.h @@ -126,8 +126,9 @@ if compiled with new GCC libstdc++, GCC libstdc++ can use: #pragma GCC diagnostic ignored "-Wglobal-constructors" #pragma GCC diagnostic ignored "-Wexit-time-destructors" -#if defined(Z7_LLVM_CLANG_VERSION) && __clang_major__ >= 18 // 18.1.0RC -#pragma GCC diagnostic ignored "-Wswitch-default" +#if defined(Z7_LLVM_CLANG_VERSION) && __clang_major__ >= 18 /* 18.1.0RC */ \ + || defined(Z7_APPLE_CLANG_VERSION) && __clang_major__ >= 16 // for APPLE=17 (LLVM=19) + #pragma GCC diagnostic ignored "-Wswitch-default" #endif // #pragma GCC diagnostic ignored "-Wunused-private-field" // #pragma GCC diagnostic ignored "-Wnonportable-system-include-path" diff --git a/CPP/Common/MyBuffer.h b/CPP/Common/MyBuffer.h index 80f0205..08c10a3 100644 --- a/CPP/Common/MyBuffer.h +++ b/CPP/Common/MyBuffer.h @@ -202,7 +202,53 @@ public: } }; -typedef CObjArray CByteArr; + +/* CSmallObjArray can be used for Byte arrays + or for arrays whose total size in bytes does not exceed size_t ranges. + So there is no need to use Z7_ARRAY_NEW macro in CSmallObjArray code. */ +template class CSmallObjArray +{ +protected: + T *_items; +private: + // we disable copy + CSmallObjArray(const CSmallObjArray &buffer); + void operator=(const CSmallObjArray &buffer); +public: + void Free() + { + delete []_items; + _items = NULL; + } + CSmallObjArray(size_t size): _items(NULL) + { + if (size != 0) + { + // Z7_ARRAY_NEW(_items, T, size) + _items = new T[size]; + } + } + CSmallObjArray(): _items(NULL) {} + ~CSmallObjArray() { delete []_items; } + + operator T *() { return _items; } + operator const T *() const { return _items; } + const T* ConstData() const { return _items; } + T* NonConstData() const { return _items; } + T* NonConstData() { return _items; } + // const T* Data() const { return _items; } + // T* Data() { return _items; } + + void Alloc(size_t newSize) + { + delete []_items; + _items = NULL; + // Z7_ARRAY_NEW(_items, T, newSize) + _items = new T[newSize]; + } +}; + +typedef CSmallObjArray CByteArr; typedef CObjArray CBoolArr; typedef CObjArray CIntArr; typedef CObjArray CUIntArr; diff --git a/CPP/Windows/Control/ComboBox.cpp b/CPP/Windows/Control/ComboBox.cpp index 8da487d..2e9c8cb 100644 --- a/CPP/Windows/Control/ComboBox.cpp +++ b/CPP/Windows/Control/ComboBox.cpp @@ -63,4 +63,13 @@ LRESULT CComboBox::GetLBText(int index, UString &s) } #endif +LRESULT CComboBox::AddString_SetItemData(LPCWSTR s, LPARAM lParam) +{ + const LRESULT index = AddString(s); + // NOTE: SetItemData((int)-1, lParam) works as unexpected. + if (index >= 0) // optional check, because (index < 0) is not expected for normal inputs + SetItemData((int)index, lParam); + return index; +} + }} diff --git a/CPP/Windows/Control/ComboBox.h b/CPP/Windows/Control/ComboBox.h index 2a60b8a..224efca 100644 --- a/CPP/Windows/Control/ComboBox.h +++ b/CPP/Windows/Control/ComboBox.h @@ -21,6 +21,8 @@ public: LRESULT AddString(LPCWSTR s); #endif + LRESULT AddString_SetItemData(LPCWSTR s, LPARAM lParam); + /* If this parameter is -1, any current selection in the list is removed and the edit control is cleared.*/ LRESULT SetCurSel(int index) { return SendMsg(CB_SETCURSEL, MY_int_TO_WPARAM(index), 0); } LRESULT SetCurSel(unsigned index) { return SendMsg(CB_SETCURSEL, index, 0); } diff --git a/CPP/Windows/FileFind.cpp b/CPP/Windows/FileFind.cpp index 64075ab..669541e 100644 --- a/CPP/Windows/FileFind.cpp +++ b/CPP/Windows/FileFind.cpp @@ -1162,6 +1162,15 @@ void CFileInfoBase::SetFrom_stat(const struct stat &st) MTime = st.st_mtimespec; ATime = st.st_atimespec; + #elif defined(__QNXNTO__) && defined(__ARM__) && !defined(__aarch64__) + + // CTime = ST_CTIME(st); + // MTime = ST_MTIME(st); + // ATime = ST_ATIME(st); + CTime.tv_sec = st.st_ctime; CTime.tv_nsec = 0; + MTime.tv_sec = st.st_mtime; MTime.tv_nsec = 0; + ATime.tv_sec = st.st_atime; ATime.tv_nsec = 0; + #else // timespec_To_FILETIME(st.st_ctim, CTime, &CTime_ns100); // timespec_To_FILETIME(st.st_mtim, MTime, &MTime_ns100); @@ -1312,7 +1321,7 @@ bool CDirEntry::IsDots() const throw() /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN) we can call fstatat() for that case, but we use only (Name) check here */ -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) if (Type != DT_DIR && Type != DT_UNKNOWN) return false; #endif @@ -1352,7 +1361,7 @@ bool CEnumerator::NextAny(CDirEntry &fi, bool &found) fi.iNode = de->d_ino; -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) fi.Type = de->d_type; /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN) we can set (Type) from fstatat() in that case. diff --git a/CPP/Windows/FileFind.h b/CPP/Windows/FileFind.h index f68673a..944bca2 100644 --- a/CPP/Windows/FileFind.h +++ b/CPP/Windows/FileFind.h @@ -277,13 +277,13 @@ typedef CFileInfo CDirEntry; struct CDirEntry { ino_t iNode; -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) Byte Type; #endif FString Name; /* -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) bool IsDir() const { // (Type == DT_UNKNOWN) on some systems @@ -310,7 +310,7 @@ public: bool Fill_FileInfo(const CDirEntry &de, CFileInfo &fileInfo, bool followLink) const; bool DirEntry_IsDir(const CDirEntry &de, bool followLink) const { -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) if (de.Type == DT_DIR) return true; if (de.Type != DT_UNKNOWN) diff --git a/CPP/Windows/SecurityUtils.h b/CPP/Windows/SecurityUtils.h index 7219f06..022a8f3 100644 --- a/CPP/Windows/SecurityUtils.h +++ b/CPP/Windows/SecurityUtils.h @@ -3,7 +3,11 @@ #ifndef ZIP7_INC_WINDOWS_SECURITY_UTILS_H #define ZIP7_INC_WINDOWS_SECURITY_UTILS_H +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#else #include +#endif #include "Defs.h" diff --git a/CPP/Windows/System.cpp b/CPP/Windows/System.cpp index 4745785..6999ef9 100644 --- a/CPP/Windows/System.cpp +++ b/CPP/Windows/System.cpp @@ -5,8 +5,9 @@ #ifndef _WIN32 #include #include -#if defined(__APPLE__) || defined(__DragonFly__) || \ - defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__DragonFly__) \ + || defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__QNXNTO__) #include #else #include @@ -299,8 +300,9 @@ bool GetRamSize(size_t &size) size = (size_t)sizeof(size_t) << 29; size64 = size; -#if defined(__APPLE__) || defined(__DragonFly__) || \ - defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__DragonFly__) \ + || defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__QNXNTO__) uint64_t val = 0; int mib[2]; diff --git a/CPP/Windows/System.h b/CPP/Windows/System.h index 0c80373..041a44d 100644 --- a/CPP/Windows/System.h +++ b/CPP/Windows/System.h @@ -15,6 +15,8 @@ namespace NWindows { namespace NSystem { +UInt32 GetNumberOfProcessors(); + #ifdef _WIN32 struct CCpuGroups @@ -103,6 +105,25 @@ struct CProcessAffinity return CountAffinity(systemAffinityMask); } + // it returns normilized number of threads + void Get_and_return_NumProcessThreads_and_SysThreads(UInt32 &numProcessThreads, UInt32 &numSysThreads) + { + UInt32 num1 = 0, num2 = 0; + if (Get()) + { + num1 = GetNumProcessThreads(); + num2 = GetNumSystemThreads(); + } + if (num1 == 0) + num1 = NSystem::GetNumberOfProcessors(); + if (num1 == 0) + num1 = 1; + if (num2 < num1) + num2 = num1; + numProcessThreads = num1; + numSysThreads = num2; + } + BOOL Get(); BOOL SetProcAffinity() const @@ -177,8 +198,6 @@ struct CProcessAffinity #endif // _WIN32 -UInt32 GetNumberOfProcessors(); - bool GetRamSize(size_t &size); // returns false, if unknown ram size unsigned long Get_File_OPEN_MAX(); diff --git a/CPP/Windows/SystemInfo.cpp b/CPP/Windows/SystemInfo.cpp index 35846e0..2eced2a 100644 --- a/CPP/Windows/SystemInfo.cpp +++ b/CPP/Windows/SystemInfo.cpp @@ -22,7 +22,7 @@ #if defined(__GLIBC__) && (__GLIBC__ * 100 + __GLIBC_MINOR__ >= 216) #define Z7_GETAUXV_AVAILABLE -#else +#elif !defined(__QNXNTO__) // #pragma message("=== is not NEW GLIBC === ") #if defined __has_include #if __has_include () @@ -58,7 +58,7 @@ #ifdef USE_HWCAP -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) // #if (__FreeBSD__ >= 13) // (FreeBSD 12.01 is required for elf_aux_info() ???) static unsigned long MY_getauxval(int aux) diff --git a/CPP/Windows/TimeUtils.h b/CPP/Windows/TimeUtils.h index 4a9d0f2..8e1e478 100644 --- a/CPP/Windows/TimeUtils.h +++ b/CPP/Windows/TimeUtils.h @@ -65,6 +65,14 @@ inline bool FILETIME_IsZero(const FILETIME &ft) #define ST_MTIME(st) st.st_mtimespec #define ST_ATIME(st) st.st_atimespec #define ST_CTIME(st) st.st_ctimespec + #elif defined(__QNXNTO__) && defined(__ARM__) && !defined(__aarch64__) + // QNX armv7le (32-bit) for "struct stat" timestamps uses time_t instead of timespec + inline CFiTime ST_MTIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_mtime; ts.tv_nsec = 0; return ts; } + inline CFiTime ST_ATIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_atime; ts.tv_nsec = 0; return ts; } + inline CFiTime ST_CTIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_ctime; ts.tv_nsec = 0; return ts; } #else #define ST_MTIME(st) st.st_mtim #define ST_ATIME(st) st.st_atim -- cgit v1.2.3-55-g6feb