diff options
Diffstat (limited to 'CPP/7zip/Archive/XarHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/XarHandler.cpp | 365 |
1 files changed, 250 insertions, 115 deletions
diff --git a/CPP/7zip/Archive/XarHandler.cpp b/CPP/7zip/Archive/XarHandler.cpp index e7d88b6c..918ef736 100755..100644 --- a/CPP/7zip/Archive/XarHandler.cpp +++ b/CPP/7zip/Archive/XarHandler.cpp @@ -4,13 +4,13 @@ #include "../../../C/CpuArch.h" -#include "Common/ComTry.h" -#include "Common/MyXml.h" -#include "Common/StringToInt.h" -#include "Common/UTFConvert.h" +#include "../../Common/ComTry.h" +#include "../../Common/MyXml.h" +#include "../../Common/StringToInt.h" +#include "../../Common/UTFConvert.h" -#include "Windows/PropVariant.h" -#include "Windows/Time.h" +#include "../../Windows/PropVariant.h" +#include "../../Windows/TimeUtils.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" @@ -24,6 +24,8 @@ #include "Common/OutStreamWithSha1.h" +using namespace NWindows; + #define XAR_SHOW_RAW #define Get16(p) GetBe16(p) @@ -33,6 +35,25 @@ namespace NArchive { namespace NXar { +static const UInt32 kXmlSizeMax = ((UInt32)1 << 30) - (1 << 14); +static const UInt32 kXmlPackSizeMax = kXmlSizeMax; + +/* +#define XAR_CKSUM_NONE 0 +#define XAR_CKSUM_SHA1 1 +#define XAR_CKSUM_MD5 2 + +static const char *k_ChecksumAlgos[] = +{ + "None" + , "SHA-1" + , "MD5" +}; +*/ + +#define METHOD_NAME_ZLIB "zlib" + + struct CFile { AString Name; @@ -41,125 +62,153 @@ struct CFile UInt64 PackSize; UInt64 Offset; - // UInt32 mode; UInt64 CTime; UInt64 MTime; UInt64 ATime; + UInt32 Mode; + + AString User; + AString Group; bool IsDir; bool HasData; - + bool ModeDefined; bool Sha1IsDefined; - Byte Sha1[20]; // bool packSha1IsDefined; - // Byte packSha1[20]; + + Byte Sha1[NCrypto::NSha1::kDigestSize]; + // Byte packSha1[NCrypto::NSha1::kDigestSize]; int Parent; - CFile(): IsDir(false), HasData(false), Sha1IsDefined(false), - /* packSha1IsDefined(false), */ - Parent(-1), Size(0), PackSize(0), CTime(0), MTime(0), ATime(0) {} + CFile(): IsDir(false), HasData(false), ModeDefined(false), Sha1IsDefined(false), + /* packSha1IsDefined(false), */ + Parent(-1), + Size(0), PackSize(0), Offset(0), + CTime(0), MTime(0), ATime(0), Mode(0) {} + + bool IsCopyMethod() const + { + return Method.IsEmpty() || Method == "octet-stream"; + } + + void UpdateTotalPackSize(UInt64 &totalSize) const + { + UInt64 t = Offset + PackSize; + if (totalSize < t) + totalSize = t; + } }; class CHandler: public IInArchive, + public IInArchiveGetStream, public CMyUnknownImp { UInt64 _dataStartPos; CMyComPtr<IInStream> _inStream; AString _xml; CObjectVector<CFile> _files; + // UInt32 _checkSumAlgo; + UInt64 _phySize; + Int32 _mainSubfile; + bool _is_pkg; HRESULT Open2(IInStream *stream); HRESULT Extract(IInStream *stream); public: - MY_UNKNOWN_IMP1(IInArchive) + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; -const UInt32 kXmlSizeMax = ((UInt32)1 << 30) - (1 << 14); +static const Byte kArcProps[] = +{ + kpidSubType, + kpidHeadersSize +}; -STATPROPSTG kProps[] = +static const Byte kProps[] = { - { NULL, kpidPath, VT_BSTR}, - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, - { NULL, kpidMTime, VT_FILETIME}, - { NULL, kpidCTime, VT_FILETIME}, - { NULL, kpidATime, VT_FILETIME}, - { NULL, kpidMethod, VT_BSTR} + kpidPath, + kpidSize, + kpidPackSize, + kpidMTime, + kpidCTime, + kpidATime, + kpidPosixAttrib, + kpidUser, + kpidGroup, + kpidMethod }; IMP_IInArchive_Props -IMP_IInArchive_ArcProps_NO +IMP_IInArchive_ArcProps -static bool ParseNumber(const char *s, int size, UInt32 &res) -{ - const char *end; - res = (UInt32)ConvertStringToUInt64(s, &end); - return (end - s == size); -} +#define PARSE_NUM(_num_, _dest_) \ + { const char *end; _dest_ = ConvertStringToUInt32(p, &end); \ + if ((unsigned)(end - p) != _num_) return 0; p += _num_ + 1; } static bool ParseUInt64(const CXmlItem &item, const char *name, UInt64 &res) { - AString s = item.GetSubStringForTag(name); + const AString s = item.GetSubStringForTag(name); + if (s.IsEmpty()) + return false; const char *end; res = ConvertStringToUInt64(s, &end); - return (end - (const char *)s == s.Length()); + return *end == 0; } static UInt64 ParseTime(const CXmlItem &item, const char *name) { - AString s = item.GetSubStringForTag(name); - if (s.Length() < 20) + const AString s = item.GetSubStringForTag(name); + if (s.Len() < 20) return 0; const char *p = s; if (p[ 4] != '-' || p[ 7] != '-' || p[10] != 'T' || p[13] != ':' || p[16] != ':' || p[19] != 'Z') return 0; UInt32 year, month, day, hour, min, sec; - if (!ParseNumber(p, 4, year )) return 0; - if (!ParseNumber(p + 5, 2, month)) return 0; - if (!ParseNumber(p + 8, 2, day )) return 0; - if (!ParseNumber(p + 11, 2, hour )) return 0; - if (!ParseNumber(p + 14, 2, min )) return 0; - if (!ParseNumber(p + 17, 2, sec )) return 0; + PARSE_NUM(4, year) + PARSE_NUM(2, month) + PARSE_NUM(2, day) + PARSE_NUM(2, hour) + PARSE_NUM(2, min) + PARSE_NUM(2, sec) UInt64 numSecs; - if (!NWindows::NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs)) + if (!NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs)) return 0; return numSecs * 10000000; } -static bool HexToByte(char c, Byte &res) +static int HexToByte(char c) { - if (c >= '0' && c <= '9') res = c - '0'; - else if (c >= 'A' && c <= 'F') res = c - 'A' + 10; - else if (c >= 'a' && c <= 'f') res = c - 'a' + 10; - else return false; - return true; + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; } -#define METHOD_NAME_ZLIB "zlib" - static bool ParseSha1(const CXmlItem &item, const char *name, Byte *digest) { int index = item.FindSubTag(name); - if (index < 0) + if (index < 0) return false; const CXmlItem &checkItem = item.SubItems[index]; - AString style = checkItem.GetPropertyValue("style"); + const AString style = checkItem.GetPropVal("style"); if (style == "SHA1") { - AString s = checkItem.GetSubString(); - if (s.Length() != 40) + const AString s = checkItem.GetSubString(); + if (s.Len() != NCrypto::NSha1::kDigestSize * 2) return false; - for (int i = 0; i < s.Length(); i += 2) + for (unsigned i = 0; i < s.Len(); i += 2) { - Byte b0, b1; - if (!HexToByte(s[i], b0) || !HexToByte(s[i + 1], b1)) + int b0 = HexToByte(s[i]); + int b1 = HexToByte(s[i + 1]); + if (b0 < 0 || b1 < 0) return false; - digest[i / 2] = (b0 << 4) | b1; + digest[i / 2] = (Byte)((b0 << 4) | b1); } return true; } @@ -203,17 +252,17 @@ static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int paren const CXmlItem &encodingItem = dataItem.SubItems[encodingIndex]; if (encodingItem.IsTag) { - AString s = encodingItem.GetPropertyValue("style"); - if (s.Length() >= 0) + AString s = encodingItem.GetPropVal("style"); + if (s.Len() >= 0) { AString appl = "application/"; - if (s.Left(appl.Length()) == appl) + if (s.IsPrefixedBy(appl)) { - s = s.Mid(appl.Length()); + s.DeleteFrontal(appl.Len()); AString xx = "x-"; - if (s.Left(xx.Length()) == xx) + if (s.IsPrefixedBy(xx)) { - s = s.Mid(xx.Length()); + s.DeleteFrontal(xx.Len()); if (s == "gzip") s = METHOD_NAME_ZLIB; } @@ -227,9 +276,23 @@ static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int paren file.CTime = ParseTime(item, "ctime"); file.MTime = ParseTime(item, "mtime"); file.ATime = ParseTime(item, "atime"); + + { + const AString s = item.GetSubStringForTag("mode"); + if (s[0] == '0') + { + const char *end; + file.Mode = ConvertOctStringToUInt32(s, &end); + file.ModeDefined = (*end == 0); + } + } + + file.User = item.GetSubStringForTag("user"); + file.Group = item.GetSubStringForTag("group"); + files.Add(file); } - for (int i = 0; i < item.SubItems.Size(); i++) + FOR_VECTOR (i, item.SubItems) if (!AddItem(item.SubItems[i], files, parent)) return false; return true; @@ -237,28 +300,28 @@ static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int paren HRESULT CHandler::Open2(IInStream *stream) { - UInt64 archiveStartPos; - RINOK(stream->Seek(0, STREAM_SEEK_SET, &archiveStartPos)); - const UInt32 kHeaderSize = 0x1C; Byte buf[kHeaderSize]; RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); UInt32 size = Get16(buf + 4); - // UInt32 ver = Get16(buf + 6); // == 0 + // UInt32 ver = Get16(buf + 6); // == 1 if (Get32(buf) != 0x78617221 || size != kHeaderSize) return S_FALSE; UInt64 packSize = Get64(buf + 8); UInt64 unpackSize = Get64(buf + 0x10); - // UInt32 checkSumAlogo = Get32(buf + 0x18); - if (unpackSize >= kXmlSizeMax) + // _checkSumAlgo = Get32(buf + 0x18); + + if (packSize >= kXmlPackSizeMax || + unpackSize >= kXmlSizeMax) return S_FALSE; - _dataStartPos = archiveStartPos + kHeaderSize + packSize; + _dataStartPos = kHeaderSize + packSize; + _phySize = _dataStartPos; - char *ss = _xml.GetBuffer((int)unpackSize + 1); + char *ss = _xml.GetBuffer((unsigned)unpackSize); NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; @@ -291,6 +354,19 @@ HRESULT CHandler::Open2(IInStream *stream) return S_FALSE; if (!AddItem(toc, _files, -1)) return S_FALSE; + + UInt64 totalPackSize = 0; + FOR_VECTOR(i, _files) + { + const CFile &file = _files[i]; + file.UpdateTotalPackSize(totalPackSize); + if (file.Name == "Payload") + _mainSubfile = i; + if (file.Name == "PackageInfo") + _is_pkg = true; + } + _phySize = _dataStartPos + totalPackSize; + return S_OK; } @@ -311,9 +387,12 @@ STDMETHODIMP CHandler::Open(IInStream *stream, STDMETHODIMP CHandler::Close() { + _phySize = 0; _inStream.Release(); _files.Clear(); _xml.Empty(); + _mainSubfile = -1; + _is_pkg = false; return S_OK; } @@ -327,7 +406,7 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) return S_OK; } -static void TimeToProp(UInt64 t, NWindows::NCOM::CPropVariant &prop) +static void TimeToProp(UInt64 t, NCOM::CPropVariant &prop) { if (t != 0) { @@ -338,34 +417,56 @@ static void TimeToProp(UInt64 t, NWindows::NCOM::CPropVariant &prop) } } +static void Utf8StringToProp(const AString &s, NCOM::CPropVariant &prop) +{ + if (!s.IsEmpty()) + { + UString us; + if (ConvertUTF8ToUnicode(s, us)) + prop = us; + } +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch (propID) + { + case kpidHeadersSize: prop = _dataStartPos; break; + case kpidPhySize: prop = _phySize; break; + case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; + case kpidSubType: if (_is_pkg) prop = "pkg"; break; + case kpidExtension: prop = _is_pkg ? "pkg" : "xar"; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN - NWindows::NCOM::CPropVariant prop; + NCOM::CPropVariant prop; #ifdef XAR_SHOW_RAW - if ((int)index == _files.Size()) + if (index == _files.Size()) { - switch(propID) + switch (propID) { - case kpidPath: prop = L"[TOC].xml"; break; + case kpidPath: prop = "[TOC].xml"; break; case kpidSize: - case kpidPackSize: prop = (UInt64)_xml.Length(); break; + case kpidPackSize: prop = (UInt64)_xml.Len(); break; } } else #endif { const CFile &item = _files[index]; - switch(propID) + switch (propID) { - case kpidMethod: - { - UString name; - if (!item.Method.IsEmpty() && ConvertUTF8ToUnicode(item.Method, name)) - prop = name; - break; - } + case kpidMethod: Utf8StringToProp(item.Method, prop); break; + case kpidPath: { AString path; @@ -373,30 +474,40 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val do { const CFile &item = _files[cur]; - AString s = item.Name; - if (s.IsEmpty()) - s = "unknown"; - if (path.IsEmpty()) - path = s; + if (!path.IsEmpty()) + path.InsertAtFront(CHAR_PATH_SEPARATOR); + if (item.Name.IsEmpty()) + path.Insert(0, "unknown"); else - path = s + CHAR_PATH_SEPARATOR + path; + path.Insert(0, item.Name); cur = item.Parent; } while (cur >= 0); - UString name; - if (ConvertUTF8ToUnicode(path, name)) - prop = name; + Utf8StringToProp(path, prop); break; } - case kpidIsDir: prop = item.IsDir; break; - case kpidSize: if (!item.IsDir) prop = item.Size; break; - case kpidPackSize: if (!item.IsDir) prop = item.PackSize; break; + case kpidIsDir: prop = item.IsDir; break; + case kpidSize: if (!item.IsDir) prop = item.Size; break; + case kpidPackSize: if (!item.IsDir) prop = item.PackSize; break; - case kpidMTime: TimeToProp(item.MTime, prop); break; - case kpidCTime: TimeToProp(item.CTime, prop); break; - case kpidATime: TimeToProp(item.ATime, prop); break; + case kpidMTime: TimeToProp(item.MTime, prop); break; + case kpidCTime: TimeToProp(item.CTime, prop); break; + case kpidATime: TimeToProp(item.ATime, prop); break; + case kpidPosixAttrib: + if (item.ModeDefined) + { + UInt32 mode = item.Mode; + const UInt32 k_PosixAttrib_Dir = (1 << 14); + const UInt32 k_PosixAttrib_RegFile = (1 << 15); + if ((mode & 0xF000) == 0) + mode |= (item.IsDir ? k_PosixAttrib_Dir : k_PosixAttrib_RegFile); + prop = mode; + } + break; + case kpidUser: Utf8StringToProp(item.User, prop); break; + case kpidGroup: Utf8StringToProp(item.Group, prop); break; } } prop.Detach(value); @@ -408,7 +519,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN - bool allFilesMode = (numItems == (UInt32)-1); + bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _files.Size(); if (numItems == 0) @@ -417,10 +528,10 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt32 i; for (i = 0; i < numItems; i++) { - int index = (int)(allFilesMode ? i : indices[i]); + UInt32 index = (allFilesMode ? i : indices[i]); #ifdef XAR_SHOW_RAW if (index == _files.Size()) - totalSize += _xml.Length(); + totalSize += _xml.Len(); else #endif totalSize += _files[index].Size; @@ -433,8 +544,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt64 currentUnpSize = 0; const UInt32 kZeroBufSize = (1 << 14); - CByteBuffer zeroBuf; - zeroBuf.SetCapacity(kZeroBufSize); + CByteBuffer zeroBuf(kZeroBufSize); memset(zeroBuf, 0, kZeroBufSize); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); @@ -478,7 +588,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; - Int32 index = allFilesMode ? i : indices[i]; + UInt32 index = allFilesMode ? i : indices[i]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); if (index < _files.Size()) @@ -504,9 +614,9 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (index == _files.Size()) { outStreamSha1Spec->Init(false); - outStreamLimSpec->Init(_xml.Length()); - RINOK(WriteStream(outStream, (const char *)_xml, _xml.Length())); - currentPackSize = currentUnpSize = _xml.Length(); + outStreamLimSpec->Init(_xml.Len()); + RINOK(WriteStream(outStream, (const char *)_xml, _xml.Len())); + currentPackSize = currentUnpSize = _xml.Len(); } else #endif @@ -524,17 +634,17 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, HRESULT res = S_OK; ICompressCoder *coder = NULL; - if (item.Method.IsEmpty() || item.Method == "octet-stream") + if (item.IsCopyMethod()) if (item.PackSize == item.Size) coder = copyCoder; else - opRes = NExtract::NOperationResult::kUnSupportedMethod; + opRes = NExtract::NOperationResult::kUnsupportedMethod; else if (item.Method == METHOD_NAME_ZLIB) coder = zlibCoder; else if (item.Method == "bzip2") coder = bzip2Coder; else - opRes = NExtract::NOperationResult::kUnSupportedMethod; + opRes = NExtract::NOperationResult::kUnsupportedMethod; if (coder) res = coder->Code(inStream, outStream, NULL, NULL, progress); @@ -578,10 +688,35 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, COM_TRY_END } -static IInArchive *CreateArc() { return new NArchive::NXar::CHandler; } +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + *stream = NULL; + COM_TRY_BEGIN + #ifdef XAR_SHOW_RAW + if (index == _files.Size()) + { + Create_BufInStream_WithNewBuf((const void *)(const char *)_xml, _xml.Len(), stream); + return S_OK; + } + else + #endif + { + const CFile &item = _files[index]; + if (item.HasData && item.IsCopyMethod() && item.PackSize == item.Size) + return CreateLimitedInStream(_inStream, _dataStartPos + item.Offset, item.Size, stream); + } + return S_FALSE; + COM_TRY_END +} + +IMP_CreateArcIn static CArcInfo g_ArcInfo = - { L"Xar", L"xar", 0, 0xE1, { 'x', 'a', 'r', '!', 0, 0x1C }, 6, false, CreateArc, 0 }; + { "Xar", "xar pkg", 0, 0xE1, + 6, { 'x', 'a', 'r', '!', 0, 0x1C }, + 0, + 0, + CreateArc }; REGISTER_ARC(Xar) |