diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2015-08-16 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:55 +0300 |
commit | cba375916fb18db8b9101aedf4fa079e019311b3 (patch) | |
tree | 6275ae5fc2a8dd337ab0327180c871807e6ba5d4 /CPP/7zip/Archive/Rar | |
parent | 54490d51d5c6b0d794dcbad2d634d4c95fc25b6c (diff) |
15.0615.06
Diffstat (limited to 'CPP/7zip/Archive/Rar')
-rw-r--r-- | CPP/7zip/Archive/Rar/Rar5Handler.cpp | 2683 | ||||
-rw-r--r-- | CPP/7zip/Archive/Rar/Rar5Handler.h | 410 | ||||
-rw-r--r-- | CPP/7zip/Archive/Rar/RarHandler.cpp | 417 | ||||
-rw-r--r-- | CPP/7zip/Archive/Rar/RarVol.h | 129 |
4 files changed, 3360 insertions, 279 deletions
diff --git a/CPP/7zip/Archive/Rar/Rar5Handler.cpp b/CPP/7zip/Archive/Rar/Rar5Handler.cpp new file mode 100644 index 00000000..72fe4f68 --- /dev/null +++ b/CPP/7zip/Archive/Rar/Rar5Handler.cpp @@ -0,0 +1,2683 @@ +// Rar5Handler.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" +#include "../../../../C/CpuArch.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/UTFConvert.h" + +#include "../../../Windows/PropVariantUtils.h" +#include "../../../Windows/TimeUtils.h" + +#include "../../IPassword.h" + +#include "../../Common/FilterCoder.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/RegisterArc.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../../Common/RegisterCodec.h" + +#include "../../Compress/CopyCoder.h" + +#include "../../Crypto/Rar5Aes.h" + +#include "../Common/FindSignature.h" +#include "../Common/ItemNameUtils.h" + +#include "RarVol.h" +#include "Rar5Handler.h" + +using namespace NWindows; + +#define Get32(p) GetUi32(p) + +namespace NArchive { + +namespace NRar { + +HRESULT ReadZeroTail(ISequentialInStream *stream, bool &areThereNonZeros, UInt64 &numZeros, UInt64 maxSize); + +} + +namespace NRar5 { + +static const unsigned kMarkerSize = 8; + +#define SIGNATURE { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0 } + +static const Byte kMarker[kMarkerSize] = SIGNATURE; + +static const size_t kCommentSize_Max = (size_t)1 << 16; + +static const char * const kHostOS[] = +{ + "Windows" + , "Unix" +}; + +static const CUInt32PCharPair k_ArcFlags[] = +{ + { 0, "Volume" }, + { 1, "VolumeField" }, + { 2, "Solid" }, + { 3, "Recovery" }, + { 4, "Lock" } +}; + + + +template <unsigned alignMask> +struct CAlignedBuffer +{ + Byte *_buf; + Byte *_bufBase; + size_t _size; + + CAlignedBuffer(): _buf(NULL), _bufBase(NULL), _size(0) {} + ~CAlignedBuffer() { ::MyFree(_bufBase); } +public: + operator Byte *() { return _buf; } + operator const Byte *() const { return _buf; } + + void AllocAtLeast(size_t size) + { + if (_buf && _size >= size) + return; + ::MyFree(_bufBase); + _buf = NULL; + _size = 0; + _bufBase = (Byte *)::MyAlloc(size + alignMask); + + if (_bufBase) + { + _size = size; + // _buf = (Byte *)(((uintptr_t)_bufBase + alignMask) & ~(uintptr_t)alignMask); + _buf = (Byte *)(((ptrdiff_t)_bufBase + alignMask) & ~(ptrdiff_t)alignMask); + } + } +}; + +static unsigned ReadVarInt(const Byte *p, size_t maxSize, UInt64 *val) +{ + *val = 0; + + for (unsigned i = 0; i < maxSize;) + { + Byte b = p[i]; + if (i < 10) + *val |= (UInt64)(b & 0x7F) << (7 * i++); + if ((b & 0x80) == 0) + return i; + } + return 0; +} + + +int CItem::FindExtra(unsigned type, unsigned &recordDataSize) const +{ + recordDataSize = 0; + size_t offset = 0; + + for (;;) + { + size_t rem = Extra.Size() - offset; + if (rem == 0) + return -1; + + { + UInt64 size; + unsigned num = ReadVarInt(Extra + offset, rem, &size); + if (num == 0) + return -1; + offset += num; + rem -= num; + if (size > rem) + return -1; + rem = (size_t)size; + } + { + UInt64 type2; + unsigned num = ReadVarInt(Extra + offset, rem, &type2); + if (num == 0) + return -1; + offset += num; + rem -= num; + + // There was BUG in RAR 5.21- : it stored (size-1) instead of (size) + // for Subdata record in Service header. + // That record always was last in bad archives, so we can fix that case. + if (type2 == NExtraRecordType::kSubdata + && RecordType == NHeaderType::kService + && rem + 1 == Extra.Size() - offset) + rem++; + + if (type2 == type) + { + recordDataSize = (unsigned)rem; + return (int)offset; + } + + offset += rem; + } + } +} + + +bool CCryptoInfo::Parse(const Byte *p, size_t size) +{ + unsigned num = ReadVarInt(p, size, &Algo); + if (num == 0) return false; p += num; size -= num; + + num = ReadVarInt(p, size, &Flags); + if (num == 0) return false; p += num; size -= num; + + if (size != 1 + 16 + 16 + (unsigned)(IsThereCheck() ? 12 : 0)) + return false; + + Cnt = p[0]; + + return true; +} + + +bool CItem::FindExtra_Version(UInt64 &version) const +{ + unsigned size; + int offset = FindExtra(NExtraRecordType::kVersion, size); + if (offset < 0) + return false; + const Byte *p = Extra + (unsigned)offset; + + UInt64 Flags; + unsigned num = ReadVarInt(p, size, &Flags); + if (num == 0) return false; p += num; size -= num; + + num = ReadVarInt(p, size, &version); + if (num == 0) return false; p += num; size -= num; + + return size == 0; +} + +bool CItem::FindExtra_Link(CLinkInfo &link) const +{ + unsigned size; + int offset = FindExtra(NExtraRecordType::kLink, size); + if (offset < 0) + return false; + const Byte *p = Extra + (unsigned)offset; + + unsigned num = ReadVarInt(p, size, &link.Type); + if (num == 0) return false; p += num; size -= num; + + num = ReadVarInt(p, size, &link.Flags); + if (num == 0) return false; p += num; size -= num; + + UInt64 len; + num = ReadVarInt(p, size, &len); + if (num == 0) return false; p += num; size -= num; + + if (size != len) + return false; + + link.NameLen = (unsigned)len; + link.NameOffset = (unsigned)(p - Extra); + return true; +} + +bool CItem::Is_CopyLink() const +{ + CLinkInfo link; + return FindExtra_Link(link) && link.Type == NLinkType::kFileCopy; +} + +void CItem::Link_to_Prop(unsigned linkType, NWindows::NCOM::CPropVariant &prop) const +{ + CLinkInfo link; + if (!FindExtra_Link(link)) + return; + + if (link.Type != linkType) + { + if (linkType != NLinkType::kUnixSymLink) + return; + switch ((unsigned)link.Type) + { + case NLinkType::kUnixSymLink: + case NLinkType::kWinSymLink: + case NLinkType::kWinJunction: + break; + default: return; + } + } + + AString s; + s.SetFrom_CalcLen((const char *)(Extra + link.NameOffset), link.NameLen); + + UString unicode; + if (ConvertUTF8ToUnicode(s, unicode)) + prop = NItemName::GetOSName(unicode); +} + +bool CItem::GetAltStreamName(AString &name) const +{ + name.Empty(); + unsigned size; + int offset = FindExtra(NExtraRecordType::kSubdata, size); + if (offset < 0) + return false; + name.SetFrom_CalcLen((const char *)(Extra + (unsigned)offset), size); + return true; +} + + +class CHash +{ + bool _calcCRC; + UInt32 _crc; + int _blakeOffset; + CBlake2sp _blake; +public: + + void Init_NoCalc() + { + _calcCRC = false; + _crc = CRC_INIT_VAL; + _blakeOffset = -1; + } + + void Init(const CItem &item); + void Update(const void *data, size_t size); + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } + + bool Check(const CItem &item, NCrypto::NRar5::CDecoder *cryptoDecoderSpec); +}; + +void CHash::Init(const CItem &item) +{ + _crc = CRC_INIT_VAL; + _calcCRC = item.Has_CRC(); + + _blakeOffset = item.FindExtra_Blake(); + if (_blakeOffset >= 0) + Blake2sp_Init(&_blake); +} + +void CHash::Update(const void *data, size_t size) +{ + if (_calcCRC) + _crc = CrcUpdate(_crc, data, size); + if (_blakeOffset >= 0) + Blake2sp_Update(&_blake, (const Byte *)data, size); +} + +bool CHash::Check(const CItem &item, NCrypto::NRar5::CDecoder *cryptoDecoderSpec) +{ + if (_calcCRC) + { + UInt32 crc = GetCRC(); + if (cryptoDecoderSpec) + crc = cryptoDecoderSpec->Hmac_Convert_Crc32(crc); + if (crc != item.CRC) + return false; + } + + if (_blakeOffset >= 0) + { + Byte digest[BLAKE2S_DIGEST_SIZE]; + Blake2sp_Final(&_blake, digest); + if (cryptoDecoderSpec) + cryptoDecoderSpec->Hmac_Convert_32Bytes(digest); + if (memcmp(digest, &item.Extra[(unsigned)_blakeOffset], BLAKE2S_DIGEST_SIZE) != 0) + return false; + } + + return true; +} + + +class COutStreamWithHash: + public ISequentialOutStream, + public CMyUnknownImp +{ + ISequentialOutStream *_stream; + UInt64 _pos; + UInt64 _size; + bool _size_Defined; + Byte *_destBuf; +public: + CHash _hash; + + COutStreamWithHash(): _destBuf(NULL) {} + + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void Init(const CItem &item, Byte *destBuf) + { + _size_Defined = false; + _size = 0; + _destBuf = NULL; + if (!item.Is_UnknownSize()) + { + _size_Defined = true; + _size = item.Size; + _destBuf = destBuf; + } + _pos = 0; + _hash.Init(item); + } + UInt64 GetPos() const { return _pos; } +}; + + +STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + HRESULT result = S_OK; + if (_size_Defined) + { + UInt64 rem = _size - _pos; + if (size > rem) + size = (UInt32)rem; + } + if (_stream) + result = _stream->Write(data, size, &size); + if (_destBuf) + memcpy(_destBuf + (size_t)_pos, data, size); + _hash.Update(data, size); + _pos += size; + if (processedSize) + *processedSize = size; + return result; +} + + + + + +class CInArchive +{ + CAlignedBuffer<AES_BLOCK_SIZE - 1> _buf; + size_t _bufSize; + size_t _bufPos; + ISequentialInStream *_stream; + + NCrypto::NRar5::CDecoder *m_CryptoDecoderSpec; + CMyComPtr<ICompressFilter> m_CryptoDecoder; + + + + HRESULT ReadStream_Check(void *data, size_t size); + +public: + bool m_CryptoMode; + + bool WrongPassword; + bool IsArc; + bool UnexpectedEnd; + + UInt64 StreamStartPosition; + UInt64 Position; + + bool ReadVar(UInt64 &val); + + struct CHeader + { + UInt64 Type; + UInt64 Flags; + size_t ExtraSize; + UInt64 DataSize; + }; + + HRESULT ReadBlockHeader(CHeader &h); + bool ReadFileHeader(const CHeader &header, CItem &item); + void AddToSeekValue(UInt64 addValue) + { + Position += addValue; + } + + HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, ICryptoGetTextPassword *getTextPassword, + CInArcInfo &info); +}; + + +static HRESULT MySetPassword(ICryptoGetTextPassword *getTextPassword, NCrypto::NRar5::CDecoder *cryptoDecoderSpec) +{ + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)); + AString utf8; + const unsigned kPasswordLen_MAX = 127; + UString unicode = (LPCOLESTR)password; + if (unicode.Len() > kPasswordLen_MAX) + unicode.DeleteFrom(kPasswordLen_MAX); + ConvertUnicodeToUTF8(unicode, utf8); + cryptoDecoderSpec->SetPassword((const Byte *)(const char *)utf8, utf8.Len()); + return S_OK; +} + + +bool CInArchive::ReadVar(UInt64 &val) +{ + unsigned offset = ReadVarInt(_buf + _bufPos, _bufSize - _bufPos, &val); + _bufPos += offset; + return (offset != 0); +} + + +HRESULT CInArchive::ReadStream_Check(void *data, size_t size) +{ + size_t size2 = size; + RINOK(ReadStream(_stream, data, &size2)); + if (size2 == size) + return S_OK; + UnexpectedEnd = true; + return S_FALSE; +} + + +HRESULT CInArchive::ReadBlockHeader(CHeader &h) +{ + h.Type = 0; + h.Flags = 0; + h.ExtraSize = 0; + h.DataSize = 0; + + const unsigned kStartSize = 4 + 3; + const unsigned kBufSize = AES_BLOCK_SIZE + AES_BLOCK_SIZE; // must be >= kStartSize; + Byte buf[kBufSize]; + unsigned filled; + + if (m_CryptoMode) + { + RINOK(ReadStream_Check(buf, kBufSize)); + memcpy(m_CryptoDecoderSpec->_iv, buf, AES_BLOCK_SIZE); + RINOK(m_CryptoDecoderSpec->Init()); + + _buf.AllocAtLeast(1 << 12); + if (!(Byte *)_buf) + return E_OUTOFMEMORY; + + memcpy(_buf, buf + AES_BLOCK_SIZE, AES_BLOCK_SIZE); + if (m_CryptoDecoderSpec->Filter(_buf, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) + return E_FAIL; + memcpy(buf, _buf, AES_BLOCK_SIZE); + filled = AES_BLOCK_SIZE; + } + else + { + RINOK(ReadStream_Check(buf, kStartSize)); + filled = kStartSize; + } + + UInt64 val; + unsigned offset = ReadVarInt(buf + 4, 3, &val); + if (offset == 0) + return S_FALSE; + { + size_t size = (size_t)val; + _bufPos = (4 + offset); + _bufSize = _bufPos + size; + if (size < 2) + return S_FALSE; + } + + size_t allocSize = _bufSize; + if (m_CryptoMode) + allocSize = (allocSize + AES_BLOCK_SIZE - 1) & ~(size_t)(AES_BLOCK_SIZE - 1); + _buf.AllocAtLeast(allocSize); + if (!(Byte *)_buf) + return E_OUTOFMEMORY; + + memcpy(_buf, buf, filled); + + size_t rem = allocSize - filled; + AddToSeekValue(allocSize + (m_CryptoMode ? AES_BLOCK_SIZE : 0)); + RINOK(ReadStream_Check(_buf + filled, rem)); + if (m_CryptoMode) + { + if (m_CryptoDecoderSpec->Filter(_buf + filled, (UInt32)rem) != rem) + return E_FAIL; + } + + if (CrcCalc(_buf + 4, _bufSize - 4) != Get32(buf)) + return S_FALSE; + + if (!ReadVar(h.Type)) return S_FALSE; + if (!ReadVar(h.Flags)) return S_FALSE; + + if (h.Flags & NHeaderFlags::kExtra) + { + UInt64 extraSize; + if (!ReadVar(extraSize)) + return S_FALSE; + if (extraSize > _bufSize) + return S_FALSE; + h.ExtraSize = (size_t)extraSize; + } + + if (h.Flags & NHeaderFlags::kData) + { + if (!ReadVar(h.DataSize)) + return S_FALSE; + } + + return S_OK; +} + + +/* +int CInArcInfo::FindExtra(unsigned type, unsigned &recordDataSize) const +{ + recordDataSize = 0; + size_t offset = 0; + + for (;;) + { + size_t rem = Extra.Size() - offset; + if (rem == 0) + return -1; + + { + UInt64 size; + unsigned num = ReadVarInt(Extra + offset, rem, &size); + if (num == 0) + return -1; + offset += num; + rem -= num; + if (size > rem) + return -1; + rem = (size_t)size; + } + { + UInt64 type2; + unsigned num = ReadVarInt(Extra + offset, rem, &type2); + if (num == 0) + return -1; + offset += num; + rem -= num; + + if (type2 == type) + { + recordDataSize = (unsigned)rem; + return (int)offset; + } + + offset += rem; + } + } +} + + +bool CInArcInfo::FindExtra_Locator(CLocator &locator) const +{ + locator.Flags = 0; + locator.QuickOpen = 0; + locator.Recovery = 0; + + unsigned size; + int offset = FindExtra(kArcExtraRecordType_Locator, size); + if (offset < 0) + return false; + const Byte *p = Extra + (unsigned)offset; + + unsigned num; + + num = ReadVarInt(p, size, &locator.Flags); + if (num == 0) return false; p += num; size -= num; + + if (locator.Is_QuickOpen()) + { + num = ReadVarInt(p, size, &locator.QuickOpen); + if (num == 0) return false; p += num; size -= num; + } + + if (locator.Is_Recovery()) + { + num = ReadVarInt(p, size, &locator.Recovery); + if (num == 0) return false; p += num; size -= num; + } + + return true; +} +*/ + + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit, ICryptoGetTextPassword *getTextPassword, + CInArcInfo &info) +{ + m_CryptoMode = false; + + WrongPassword = false; + IsArc = false; + UnexpectedEnd = false; + + Position = StreamStartPosition; + + UInt64 arcStartPos = StreamStartPosition; + { + Byte marker[kMarkerSize]; + RINOK(ReadStream_FALSE(stream, marker, kMarkerSize)); + if (memcmp(marker, kMarker, kMarkerSize) == 0) + Position += kMarkerSize; + else + { + if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0) + return S_FALSE; + RINOK(stream->Seek(StreamStartPosition, STREAM_SEEK_SET, NULL)); + RINOK(FindSignatureInStream(stream, kMarker, kMarkerSize, + searchHeaderSizeLimit, arcStartPos)); + arcStartPos += StreamStartPosition; + Position = arcStartPos + kMarkerSize; + RINOK(stream->Seek(Position, STREAM_SEEK_SET, NULL)); + } + } + + info.StartPos = arcStartPos; + _stream = stream; + + CHeader h; + RINOK(ReadBlockHeader(h)); + info.IsEncrypted = false; + + if (h.Type == NHeaderType::kArcEncrypt) + { + info.IsEncrypted = true; + IsArc = true; + if (!getTextPassword) + return E_NOTIMPL; + + m_CryptoMode = true; + + if (!m_CryptoDecoder) + { + m_CryptoDecoderSpec = new NCrypto::NRar5::CDecoder; + m_CryptoDecoder = m_CryptoDecoderSpec; + } + + RINOK(m_CryptoDecoderSpec->SetDecoderProps( + _buf + _bufPos, (unsigned)(_bufSize - _bufPos), false, false)); + + RINOK(MySetPassword(getTextPassword, m_CryptoDecoderSpec)); + + if (!m_CryptoDecoderSpec->CalcKey_and_CheckPassword()) + { + WrongPassword = True; + return S_FALSE; + } + + RINOK(ReadBlockHeader(h)); + } + + if (h.Type != NHeaderType::kArc) + return S_FALSE; + + IsArc = true; + info.VolNumber = 0; + + if (!ReadVar(info.Flags)) + return S_FALSE; + + if (info.Flags & NArcFlags::kVolNumber) + if (!ReadVar(info.VolNumber)) + return S_FALSE; + + if (h.ExtraSize != 0) + { + if (_bufSize - _bufPos < h.ExtraSize) + return S_FALSE; + /* + info.Extra.Alloc(h.ExtraSize); + memcpy(info.Extra, _buf + _bufPos, h.ExtraSize); + */ + _bufPos += h.ExtraSize; + + /* + CInArcInfo::CLocator locator; + if (info.FindExtra_Locator(locator)) + locator.Flags = locator.Flags; + */ + } + + if (_bufPos != _bufSize) + return S_FALSE; + + return S_OK; +} + + +bool CInArchive::ReadFileHeader(const CHeader &header, CItem &item) +{ + item.UnixMTime = 0; + item.CRC = 0; + item.Flags = 0; + + item.CommonFlags = (UInt32)header.Flags; + item.PackSize = header.DataSize; + + UInt64 flags64; + if (!ReadVar(flags64)) return false; + item.Flags = (UInt32)flags64; + + if (!ReadVar(item.Size)) return false; + + { + UInt64 attrib; + if (!ReadVar(attrib)) return false; + item.Attrib = (UInt32)attrib; + } + + if (item.Has_UnixMTime()) + { + if (_bufSize - _bufPos < 4) + return false; + item.UnixMTime = Get32(_buf + _bufPos); + _bufPos += 4; + } + + if (item.Has_CRC()) + { + if (_bufSize - _bufPos < 4) + return false; + item.CRC = Get32(_buf + _bufPos); + _bufPos += 4; + } + + { + UInt64 method; + if (!ReadVar(method)) return false; + item.Method = (UInt32)method; + } + + if (!ReadVar(item.HostOS)) return false; + + { + UInt64 len; + if (!ReadVar(len)) return false; + if (len > _bufSize - _bufPos) + return false; + item.Name.SetFrom_CalcLen((const char *)(_buf + _bufPos), (unsigned)len); + _bufPos += (unsigned)len; + } + + item.Extra.Free(); + size_t extraSize = header.ExtraSize; + if (extraSize != 0) + { + if (_bufSize - _bufPos < extraSize) + return false; + item.Extra.Alloc(extraSize); + memcpy(item.Extra, _buf + _bufPos, extraSize); + _bufPos += extraSize; + } + + + return (_bufPos == _bufSize); +} + + + +struct CLinkFile +{ + unsigned Index; + unsigned NumLinks; + CByteBuffer Data; + HRESULT Res; + bool crcOK; + + CLinkFile(): Index(0), NumLinks(0), Res(S_OK), crcOK(true) {} +}; + + +struct CUnpacker +{ + NCompress::CCopyCoder *copyCoderSpec; + CMyComPtr<ICompressCoder> copyCoder; + + CMyComPtr<ICompressCoder> LzCoders[2]; + bool NeedClearSolid[2]; + + CFilterCoder *filterStreamSpec; + CMyComPtr<ISequentialInStream> filterStream; + + NCrypto::NRar5::CDecoder *cryptoDecoderSpec; + CMyComPtr<ICompressFilter> cryptoDecoder; + + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + + COutStreamWithHash *outStreamSpec; + CMyComPtr<ISequentialOutStream> outStream; + + CByteBuffer _tempBuf; + + CLinkFile *linkFile; + + CUnpacker(): linkFile(NULL) { NeedClearSolid[0] = NeedClearSolid[1] = true; } + + HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS const CItem &item, bool isSolid, bool &wrongPassword); + + HRESULT Code(const CItem &item, const CItem &lastItem, UInt64 packSize, + ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress, + bool &isCrcOK); + + HRESULT DecodeToBuf(DECL_EXTERNAL_CODECS_LOC_VARS const CItem &item, UInt64 packSize, ISequentialInStream *inStream, CByteBuffer &buffer); +}; + + +static const unsigned kLzMethodMax = 5; + +HRESULT CUnpacker::Create(DECL_EXTERNAL_CODECS_LOC_VARS const CItem &item, bool isSolid, bool &wrongPassword) +{ + wrongPassword = false; + + if (item.GetAlgoVersion() != 0) + return E_NOTIMPL; + + if (!outStream) + { + outStreamSpec = new COutStreamWithHash; + outStream = outStreamSpec; + } + + unsigned method = item.GetMethod(); + + if (method == 0) + { + if (!copyCoder) + { + copyCoderSpec = new NCompress::CCopyCoder; + copyCoder = copyCoderSpec; + } + } + else + { + if (method > kLzMethodMax) + return E_NOTIMPL; + + /* + if (item.IsSplitBefore()) + return S_FALSE; + */ + + int lzIndex = item.IsService() ? 1 : 0; + CMyComPtr<ICompressCoder> &lzCoder = LzCoders[lzIndex]; + + if (!lzCoder) + { + const UInt32 methodID = 0x40305; + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS methodID, false, lzCoder)); + if (!lzCoder) + return E_NOTIMPL; + } + + CMyComPtr<ICompressSetDecoderProperties2> csdp; + RINOK(lzCoder.QueryInterface(IID_ICompressSetDecoderProperties2, &csdp)); + + Byte props[2] = { (Byte)(item.GetDictSize()), (Byte)(isSolid ? 1 : 0) }; + RINOK(csdp->SetDecoderProperties2(props, 2)); + } + + unsigned cryptoSize = 0; + int cryptoOffset = item.FindExtra(NExtraRecordType::kCrypto, cryptoSize); + + if (cryptoOffset >= 0) + { + if (!filterStream) + { + filterStreamSpec = new CFilterCoder(false); + filterStream = filterStreamSpec; + } + + if (!cryptoDecoder) + { + cryptoDecoderSpec = new NCrypto::NRar5::CDecoder; + cryptoDecoder = cryptoDecoderSpec; + } + + RINOK(cryptoDecoderSpec->SetDecoderProps(item.Extra + (unsigned)cryptoOffset, cryptoSize, true, item.IsService())); + + if (!getTextPassword) + { + wrongPassword = True; + return E_NOTIMPL; + } + + RINOK(MySetPassword(getTextPassword, cryptoDecoderSpec)); + + if (!cryptoDecoderSpec->CalcKey_and_CheckPassword()) + wrongPassword = True; + } + + return S_OK; +} + + +HRESULT CUnpacker::Code(const CItem &item, const CItem &lastItem, UInt64 packSize, + ISequentialInStream *volsInStream, ISequentialOutStream *realOutStream, ICompressProgressInfo *progress, + bool &isCrcOK) +{ + isCrcOK = true; + + unsigned method = item.GetMethod(); + if (method > kLzMethodMax) + return E_NOTIMPL; + + if (linkFile && !lastItem.Is_UnknownSize()) + { + size_t dataSize = (size_t)lastItem.Size; + if (dataSize != lastItem.Size) + return E_NOTIMPL; + linkFile->Data.Alloc(dataSize); + } + + bool isCryptoMode = false; + ISequentialInStream *inStream; + + if (item.IsEncrypted()) + { + filterStreamSpec->Filter = cryptoDecoder; + filterStreamSpec->SetInStream(volsInStream); + filterStreamSpec->SetOutStreamSize(NULL); + inStream = filterStream; + isCryptoMode = true; + } + else + inStream = volsInStream; + + ICompressCoder *commonCoder = (method == 0) ? copyCoder : LzCoders[item.IsService() ? 1 : 0]; + + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(lastItem, (linkFile ? (Byte *)linkFile->Data : NULL)); + + NeedClearSolid[item.IsService() ? 1 : 0] = false; + + HRESULT res = S_OK; + if (packSize != 0 || lastItem.Is_UnknownSize() || lastItem.Size != 0) + { + res = commonCoder->Code(inStream, outStream, &packSize, + lastItem.Is_UnknownSize() ? NULL : &lastItem.Size, progress); + } + else + { + res = res; + } + + if (isCryptoMode) + filterStreamSpec->ReleaseInStream(); + + UInt64 processedSize = outStreamSpec->GetPos(); + if (res == S_OK && !lastItem.Is_UnknownSize() && processedSize != lastItem.Size) + res = S_FALSE; + + // if (res == S_OK) + { + unsigned cryptoSize = 0; + int cryptoOffset = lastItem.FindExtra(NExtraRecordType::kCrypto, cryptoSize); + NCrypto::NRar5::CDecoder *crypto = NULL; + + if (cryptoOffset >= 0) + { + CCryptoInfo cryptoInfo; + if (cryptoInfo.Parse(lastItem.Extra + (unsigned)cryptoOffset, cryptoSize)) + if (cryptoInfo.UseMAC()) + crypto = cryptoDecoderSpec; + } + + isCrcOK = outStreamSpec->_hash.Check(lastItem, crypto); + } + + if (linkFile) + { + linkFile->Res = res; + linkFile->crcOK = isCrcOK; + if (!lastItem.Is_UnknownSize() && processedSize != lastItem.Size) + linkFile->Data.ChangeSize_KeepData((size_t)processedSize, (size_t)processedSize); + } + + return res; +} + + +HRESULT CUnpacker::DecodeToBuf(DECL_EXTERNAL_CODECS_LOC_VARS const CItem &item, UInt64 packSize, ISequentialInStream *inStream, CByteBuffer &buffer) +{ + CBufPtrSeqOutStream *outSpec = new CBufPtrSeqOutStream; + CMyComPtr<ISequentialOutStream> out = outSpec; + _tempBuf.AllocAtLeast((size_t)item.Size); + outSpec->Init(_tempBuf, (size_t)item.Size); + + bool wrongPassword; + + if (item.IsSolid()) + return E_NOTIMPL; + + HRESULT res = Create(EXTERNAL_CODECS_LOC_VARS item, item.IsSolid(), wrongPassword); + + if (res == S_OK) + { + if (wrongPassword) + return S_FALSE; + + CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> limitedStream(limitedStreamSpec); + limitedStreamSpec->SetStream(inStream); + limitedStreamSpec->Init(packSize); + + bool crcOK = true; + res = Code(item, item, packSize, limitedStream, out, NULL, crcOK); + if (res == S_OK) + { + if (!crcOK || outSpec->GetPos() != item.Size) + res = S_FALSE; + else + buffer.CopyFrom(_tempBuf, (size_t)item.Size); + } + } + + return res; +} + + +struct CTempBuf +{ + CByteBuffer _buf; + size_t _offset; + bool _isOK; + + void Clear() + { + _offset = 0; + _isOK = true; + } + + CTempBuf() { Clear(); } + + HRESULT Decode(DECL_EXTERNAL_CODECS_LOC_VARS + const CItem &item, + ISequentialInStream *inStream, CUnpacker &unpacker, CByteBuffer &destBuf); +}; + + +HRESULT CTempBuf::Decode(DECL_EXTERNAL_CODECS_LOC_VARS + const CItem &item, + ISequentialInStream *inStream, + CUnpacker &unpacker, + CByteBuffer &destBuf) +{ + const size_t kPackSize_Max = (1 << 24); + if (item.Size > (1 << 24) + || item.Size == 0 + || item.PackSize >= kPackSize_Max) + { + Clear(); + return S_OK; + } + + if (item.IsSplit() /* && _isOK */) + { + size_t packSize = (size_t)item.PackSize; + if (packSize > kPackSize_Max - _offset) + return S_OK; + size_t newSize = _offset + packSize; + if (newSize > _buf.Size()) + _buf.ChangeSize_KeepData(newSize, _offset); + + Byte *data = (Byte *)_buf + _offset; + RINOK(ReadStream_FALSE(inStream, data, packSize)); + + _offset += packSize; + + if (item.IsSplitAfter()) + { + CHash hash; + hash.Init(item); + hash.Update(data, packSize); + _isOK = hash.Check(item, NULL); // RAR5 doesn't use HMAC for packed part + } + } + + if (_isOK) + { + if (!item.IsSplitAfter()) + { + if (_offset == 0) + { + RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_LOC_VARS + item, item.PackSize, inStream, destBuf)); + } + else + { + CBufInStream *bufInStreamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> bufInStream = bufInStreamSpec; + bufInStreamSpec->Init(_buf, _offset); + RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_LOC_VARS + item, _offset, bufInStream, destBuf)); + } + } + } + + return S_OK; +} + + + +static const Byte kProps[] = +{ + kpidPath, + kpidIsDir, + kpidSize, + kpidPackSize, + kpidMTime, + kpidCTime, + kpidATime, + kpidAttrib, + + kpidIsAltStream, + kpidEncrypted, + kpidSolid, + kpidSplitBefore, + kpidSplitAfter, + kpidCRC, + kpidHostOS, + kpidMethod, + + kpidSymLink, + kpidHardLink, + kpidCopyLink, +}; + + +static const Byte kArcProps[] = +{ + kpidTotalPhySize, + kpidCharacts, + kpidSolid, + kpidNumBlocks, + kpidEncrypted, + kpidIsVolume, + kpidVolumeIndex, + kpidNumVolumes, + kpidComment +}; + + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + + +UInt64 CHandler::GetPackSize(unsigned refIndex) const +{ + UInt64 size = 0; + unsigned index = _refs[refIndex].Item; + for (;;) + { + const CItem &item = _items[index]; + size += item.PackSize; + if (item.NextItem < 0) + return size; + index = item.NextItem; + } +} + + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + + NCOM::CPropVariant prop; + + const CInArcInfo *arcInfo = NULL; + if (!_arcs.IsEmpty()) + arcInfo = &_arcs[0].Info; + + switch (propID) + { + case kpidVolumeIndex: if (arcInfo && arcInfo->IsVolume()) prop = arcInfo->GetVolIndex(); break; + case kpidSolid: if (arcInfo) prop = arcInfo->IsSolid(); break; + case kpidCharacts: + { + if (!_arcs.IsEmpty()) + { + FLAGS_TO_PROP(k_ArcFlags, (UInt32)arcInfo->Flags, prop); + } + break; + } + case kpidEncrypted: if (arcInfo) prop = arcInfo->IsEncrypted; break; // it's for encrypted names. + case kpidIsVolume: if (arcInfo) prop = arcInfo->IsVolume(); break; + case kpidNumVolumes: prop = (UInt32)_arcs.Size(); break; + case kpidOffset: if (arcInfo && arcInfo->StartPos != 0) prop = arcInfo->StartPos; break; + + case kpidTotalPhySize: + { + if (_arcs.Size() > 1) + { + UInt64 sum = 0; + FOR_VECTOR (v, _arcs) + sum += _arcs[v].Info.GetPhySize(); + prop = sum; + } + break; + } + + case kpidPhySize: + { + if (arcInfo) + prop = arcInfo->GetPhySize(); + break; + } + + case kpidComment: + { + // if (!_arcs.IsEmpty()) + { + // const CArc &arc = _arcs[0]; + const CByteBuffer &cmt = _comment; + if (cmt.Size() != 0 && cmt.Size() < (1 << 16)) + { + AString s; + s.SetFrom_CalcLen((const char *)(const Byte *)cmt, (unsigned)cmt.Size()); + UString unicode; + if (ConvertUTF8ToUnicode(s, unicode)) + prop = unicode; + } + } + break; + } + + case kpidNumBlocks: + { + UInt32 numBlocks = 0; + FOR_VECTOR (i, _refs) + if (!_items[_refs[i].Item].IsSolid()) + numBlocks++; + prop = (UInt32)numBlocks; + break; + } + + case kpidErrorFlags: + { + UInt32 v = _errorFlags; + if (!_isArc) + v |= kpv_ErrorFlags_IsNotArc; + prop = v; + break; + } + + /* + case kpidWarningFlags: + { + if (_warningFlags != 0) + prop = _warningFlags; + break; + } + */ + + case kpidExtension: + if (_arcs.Size() == 1) + { + if (arcInfo->IsVolume()) + { + char sz[32]; + ConvertUInt64ToString(arcInfo->GetVolIndex() + 1, sz); + unsigned len = MyStringLen(sz); + AString s = "part"; + for (; len < 2; len++) + s += '0'; + s += sz; + s += ".rar"; + prop = s; + } + } + break; + + case kpidIsAltStream: prop = true; break; + } + + prop.Detach(value); + return S_OK; + + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _refs.Size(); + return S_OK; +} + + +static const Byte kRawProps[] = +{ + kpidChecksum, + kpidNtSecure +}; + + +STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps) +{ + *numProps = ARRAY_SIZE(kRawProps); + return S_OK; +} + +STDMETHODIMP CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID) +{ + *propID = kRawProps[index]; + *name = 0; + return S_OK; +} + +STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType) +{ + *parentType = NParentType::kDir; + *parent = (UInt32)(Int32)-1; + + if (index >= _refs.Size()) + return S_OK; + + const CRefItem &ref = _refs[index]; + const CItem &item = _items[ref.Item]; + + if (item.Is_STM() && ref.Parent >= 0) + { + *parent = (UInt32)ref.Parent; + *parentType = NParentType::kAltStream; + } + + return S_OK; +} + + +STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) +{ + *data = NULL; + *dataSize = 0; + *propType = 0; + + if (index >= _refs.Size()) + return E_INVALIDARG; + + const CItem &item = _items[_refs[index].Item]; + + if (propID == kpidNtSecure) + { + if (item.ACL >= 0) + { + const CByteBuffer &buf = _acls[item.ACL]; + *dataSize = (UInt32)buf.Size(); + *propType = NPropDataType::kRaw; + *data = (const Byte *)buf; + } + return S_OK; + } + + if (propID == kpidChecksum) + { + int hashRecOffset = item.FindExtra_Blake(); + if (hashRecOffset >= 0) + { + *dataSize = BLAKE2S_DIGEST_SIZE; + *propType = NPropDataType::kRaw; + *data = &item.Extra[hashRecOffset]; + } + return S_OK; + } + + return S_OK; +} + + +static void TimeRecordToProp(const CItem &item, unsigned stampIndex, NCOM::CPropVariant &prop) +{ + unsigned size; + int offset = item.FindExtra(NExtraRecordType::kTime, size); + if (offset < 0) + return; + + const Byte *p = item.Extra + (unsigned)offset; + UInt64 flags; + { + unsigned num = ReadVarInt(p, size, &flags); + if (num == 0) + return; + p += num; + size -= num; + } + + if ((flags & (NTimeRecord::NFlags::kMTime << stampIndex)) == 0) + return; + + unsigned numStamps = 0; + unsigned i; + for (i = 0; i < 3; i++) + if ((flags & (NTimeRecord::NFlags::kMTime << i)) != 0) + numStamps++; + unsigned stampSizeLog = ((flags & NTimeRecord::NFlags::kUnixTime) != 0) ? 2 : 3; + + if ((numStamps << stampSizeLog) != size) + return; + + numStamps = 0; + for (i = 0; i < stampIndex; i++) + if ((flags & (NTimeRecord::NFlags::kMTime << i)) != 0) + numStamps++; + + p += (numStamps << stampSizeLog); + + FILETIME ft; + if ((flags & NTimeRecord::NFlags::kUnixTime) != 0) + NWindows::NTime::UnixTimeToFileTime(Get32(p), ft); + else + { + ft.dwLowDateTime = Get32(p); + ft.dwHighDateTime = Get32(p + 4); + } + + prop = ft; +} + + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + + NCOM::CPropVariant prop; + const CRefItem &ref = _refs[index]; + const CItem &item = _items[ref.Item]; + const CItem &lastItem = _items[ref.Last]; + + switch (propID) + { + case kpidPath: + { + UString unicodeName; + + if (item.Is_STM()) + { + AString s; + if (ref.Parent >= 0) + { + CItem &mainItem = _items[_refs[ref.Parent].Item]; + s = mainItem.Name; + } + + AString name; + item.GetAltStreamName(name); + if (name[0] != ':') + s += ':'; + s += name; + if (!ConvertUTF8ToUnicode(s, unicodeName)) + break; + } + else + { + if (!ConvertUTF8ToUnicode(item.Name, unicodeName)) + break; + if (item.Version_Defined) + { + wchar_t temp[32]; + // temp[0] = ';'; + // ConvertUInt64ToString(item.Version, temp + 1); + // unicodeName += temp; + ConvertUInt64ToString(item.Version, temp); + UString s2 = L"[VER]" WSTRING_PATH_SEPARATOR; + s2 += temp; + s2.Add_PathSepar(); + unicodeName.Insert(0, s2); + } + } + + NItemName::ConvertToOSName2(unicodeName); + prop = unicodeName; + + break; + } + + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: if (!lastItem.Is_UnknownSize()) prop = lastItem.Size; break; + case kpidPackSize: prop = GetPackSize(index); break; + + case kpidMTime: + { + TimeRecordToProp(item, NTimeRecord::k_Index_MTime, prop); + if (prop.vt == VT_EMPTY && item.Has_UnixMTime()) + { + FILETIME ft; + NWindows::NTime::UnixTimeToFileTime(item.UnixMTime, ft); + prop = ft; + } + if (prop.vt == VT_EMPTY && ref.Parent >= 0) + { + const CItem &baseItem = _items[_refs[ref.Parent].Item]; + TimeRecordToProp(baseItem, NTimeRecord::k_Index_MTime, prop); + if (prop.vt == VT_EMPTY && baseItem.Has_UnixMTime()) + { + FILETIME ft; + NWindows::NTime::UnixTimeToFileTime(baseItem.UnixMTime, ft); + prop = ft; + } + } + break; + } + case kpidCTime: TimeRecordToProp(item, NTimeRecord::k_Index_CTime, prop); break; + case kpidATime: TimeRecordToProp(item, NTimeRecord::k_Index_ATime, prop); break; + + case kpidName: + { + if (item.Is_STM()) + { + AString name; + item.GetAltStreamName(name); + if (name[0] == ':') + { + name.DeleteFrontal(1); + UString unicodeName; + if (ConvertUTF8ToUnicode(name, unicodeName)) + prop = unicodeName; + } + } + break; + } + + case kpidIsAltStream: prop = item.Is_STM(); break; + + case kpidSymLink: item.Link_to_Prop(NLinkType::kUnixSymLink, prop); break; + case kpidHardLink: item.Link_to_Prop(NLinkType::kHardLink, prop); break; + case kpidCopyLink: item.Link_to_Prop(NLinkType::kFileCopy, prop); break; + + case kpidAttrib: prop = item.GetWinAttrib(); break; + case kpidEncrypted: prop = item.IsEncrypted(); break; + case kpidSolid: prop = item.IsSolid(); break; + + case kpidSplitBefore: prop = item.IsSplitBefore(); break; + case kpidSplitAfter: prop = lastItem.IsSplitAfter(); break; + case kpidCRC: + { + const CItem *item2 = (lastItem.IsSplitAfter() ? &item : &lastItem); + if (item2->Has_CRC()) + prop = item2->CRC; + break; + } + + case kpidMethod: + { + char temp[64]; + unsigned algo = item.GetAlgoVersion(); + char *s = temp; + if (algo != 0) + { + ConvertUInt32ToString(algo, s); + s += MyStringLen(s); + *s++ = ':'; + } + unsigned m = item.GetMethod(); + { + s[0] = 'm'; + s[1] = (char)(m + '0'); + s[2] = 0; + if (!item.IsDir()) + { + s[2] = ':'; + ConvertUInt32ToString(item.GetDictSize() + 17, s + 3); + } + } + + unsigned cryptoSize = 0; + int cryptoOffset = item.FindExtra(NExtraRecordType::kCrypto, cryptoSize); + if (cryptoOffset >= 0) + { + s = temp + strlen(temp); + *s++ = ' '; + strcpy(s, "AES:"); + CCryptoInfo cryptoInfo; + if (cryptoInfo.Parse(item.Extra + (unsigned)cryptoOffset, cryptoSize)) + { + s += strlen(s); + ConvertUInt32ToString(cryptoInfo.Cnt, s); + s += strlen(s); + *s++ = ':'; + ConvertUInt64ToString(cryptoInfo.Flags, s); + } + } + + prop = temp; + break; + } + + case kpidHostOS: + if (item.HostOS < ARRAY_SIZE(kHostOS)) + prop = kHostOS[(size_t)item.HostOS]; + else + prop = (UInt64)item.HostOS; + break; + } + + prop.Detach(value); + return S_OK; + + COM_TRY_END +} + + + +// ---------- Copy Links ---------- + +static int CompareItemsPaths(const CHandler &handler, unsigned p1, unsigned p2, const AString *name1) +{ + const CItem &item1 = handler._items[handler._refs[p1].Item]; + const CItem &item2 = handler._items[handler._refs[p2].Item]; + + if (item1.Version_Defined) + { + if (!item2.Version_Defined) + return -1; + int res = MyCompare(item1.Version, item2.Version); + if (res != 0) + return res; + } + else if (item2.Version_Defined) + return 1; + + if (!name1) + name1 = &item1.Name; + return strcmp(*name1, item2.Name); +} + +static int CompareItemsPaths2(const CHandler &handler, unsigned p1, unsigned p2, const AString *name1) +{ + int res = CompareItemsPaths(handler, p1, p2, name1); + if (res != 0) + return res; + return MyCompare(p1, p2); +} + +static int CompareItemsPaths_Sort(const unsigned *p1, const unsigned *p2, void *param) +{ + return CompareItemsPaths2(*(const CHandler *)param, *p1, *p2, NULL); +} + +static int FindLink(const CHandler &handler, const CUIntVector &sorted, + const AString &s, unsigned index) +{ + unsigned left = 0, right = sorted.Size(); + for (;;) + { + if (left == right) + { + if (left > 0) + { + unsigned refIndex = sorted[left - 1]; + if (CompareItemsPaths(handler, index, refIndex, &s) == 0) + return refIndex; + } + if (right < sorted.Size()) + { + unsigned refIndex = sorted[right]; + if (CompareItemsPaths(handler, index, refIndex, &s) == 0) + return refIndex; + } + return -1; + } + + unsigned mid = (left + right) / 2; + unsigned refIndex = sorted[mid]; + int compare = CompareItemsPaths2(handler, index, refIndex, &s); + if (compare == 0) + return refIndex; + if (compare < 0) + right = mid; + else + left = mid + 1; + } +} + +void CHandler::FillLinks() +{ + unsigned i; + + for (i = 0; i < _refs.Size(); i++) + { + const CItem &item = _items[_refs[i].Item]; + if (!item.IsDir() && !item.IsService() && item.NeedUse_as_CopyLink()) + break; + } + + if (i == _refs.Size()) + return; + + CUIntVector sorted; + for (i = 0; i < _refs.Size(); i++) + { + const CItem &item = _items[_refs[i].Item]; + if (!item.IsDir() && !item.IsService()) + sorted.Add(i); + } + + if (sorted.IsEmpty()) + return; + + sorted.Sort(CompareItemsPaths_Sort, (void *)this); + + AString link; + + for (i = 0; i < _refs.Size(); i++) + { + CRefItem &ref = _refs[i]; + const CItem &item = _items[ref.Item]; + if (item.IsDir() || item.IsService() || item.PackSize != 0) + continue; + CItem::CLinkInfo linkInfo; + if (!item.FindExtra_Link(linkInfo) || linkInfo.Type != NLinkType::kFileCopy) + continue; + link.SetFrom_CalcLen((const char *)(item.Extra + linkInfo.NameOffset), linkInfo.NameLen); + int linkIndex = FindLink(*this, sorted, link, i); + if (linkIndex < 0) + continue; + if ((unsigned)linkIndex >= i) + continue; // we don't support forward links that can lead to loops + const CRefItem &linkRef = _refs[linkIndex]; + const CItem &linkItem = _items[linkRef.Item]; + if (linkItem.Size == item.Size) + { + if (linkRef.Link >= 0) + ref.Link = linkRef.Link; + else if (!linkItem.NeedUse_as_CopyLink()) + ref.Link = linkIndex; + } + } +} + + + +HRESULT CHandler::Open2(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback) +{ + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + + NRar::CVolumeName seqName; + + UInt64 totalBytes = 0; + UInt64 curBytes = 0; + + if (openCallback) + { + openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); + openCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); + } + + CTempBuf tempBuf; + + CUnpacker unpacker; + unpacker.getTextPassword = getTextPassword; + + int prevSplitFile = -1; + int prevMainFile = -1; + + CInArchive arch; + + for (;;) + { + CMyComPtr<IInStream> inStream; + + if (_arcs.IsEmpty()) + inStream = stream; + else + { + if (!openVolumeCallback) + break; + + if (_arcs.Size() == 1) + { + UString baseName; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + break; + baseName = prop.bstrVal; + } + if (!seqName.InitName(baseName)) + break; + } + + HRESULT result = openVolumeCallback->GetStream(seqName.GetNextName(), &inStream); + if (result == S_FALSE) + break; + if (result != S_OK) + return result; + if (!inStream) + break; + } + + UInt64 endPos = 0; + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &arch.StreamStartPosition)); + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(inStream->Seek(arch.StreamStartPosition, STREAM_SEEK_SET, NULL)); + + if (openCallback) + { + totalBytes += endPos; + RINOK(openCallback->SetTotal(NULL, &totalBytes)); + } + + CInArcInfo arcInfoOpen; + HRESULT res = arch.Open(inStream, maxCheckStartPosition, getTextPassword, arcInfoOpen); + if (arch.IsArc && arch.UnexpectedEnd) + _errorFlags |= kpv_ErrorFlags_UnexpectedEnd; + if (_arcs.IsEmpty()) + { + _isArc = arch.IsArc; + } + + if (res != S_OK) + { + if (res != S_FALSE) + return res; + if (_arcs.IsEmpty()) + return res; + break; + } + + CArc &arc = _arcs.AddNew(); + CInArcInfo &arcInfo = arc.Info; + arcInfo = arcInfoOpen; + arc.Stream = inStream; + + CItem item; + + for (;;) + { + item.Clear(); + + arcInfo.EndPos = arch.Position; + + if (arch.Position > endPos) + { + _errorFlags |= kpv_ErrorFlags_UnexpectedEnd; + break; + } + + RINOK(inStream->Seek(arch.Position, STREAM_SEEK_SET, NULL)); + + { + CInArchive::CHeader h; + HRESULT res = arch.ReadBlockHeader(h); + if (res != S_OK) + { + if (res != S_FALSE) + return res; + if (arch.UnexpectedEnd) + { + _errorFlags |= kpv_ErrorFlags_UnexpectedEnd; + if (arcInfo.EndPos < arch.Position) + arcInfo.EndPos = arch.Position; + if (arcInfo.EndPos < endPos) + arcInfo.EndPos = endPos; + } + else + _errorFlags |= kpv_ErrorFlags_HeadersError; + break; + } + + if (h.Type == NHeaderType::kEndOfArc) + { + arcInfo.EndPos = arch.Position; + arcInfo.EndOfArchive_was_Read = true; + if (!arch.ReadVar(arcInfo.EndFlags)) + _errorFlags |= kpv_ErrorFlags_HeadersError; + if (arcInfo.IsVolume()) + { + // for multivolume archives RAR can add ZERO bytes at the end for alignment. + // We must skip these bytes to prevent phySize warning. + RINOK(inStream->Seek(arcInfo.EndPos, STREAM_SEEK_SET, NULL)); + bool areThereNonZeros; + UInt64 numZeros; + const UInt64 maxSize = 1 << 12; + RINOK(NRar::ReadZeroTail(inStream, areThereNonZeros, numZeros, maxSize)); + if (!areThereNonZeros && numZeros != 0 && numZeros <= maxSize) + arcInfo.EndPos += numZeros; + } + break; + } + + if (h.Type != NHeaderType::kFile && + h.Type != NHeaderType::kService) + { + _errorFlags |= kpv_ErrorFlags_UnsupportedFeature; + break; + } + + item.RecordType = (Byte)h.Type; + + if (!arch.ReadFileHeader(h, item)) + { + _errorFlags |= kpv_ErrorFlags_HeadersError; + break; + } + + // item.MainPartSize = (UInt32)(Position - item.Position); + item.DataPos = arch.Position; + } + + bool isOk_packSize = true; + { + arcInfo.EndPos = arch.Position; + if (arch.Position + item.PackSize < arch.Position) + { + isOk_packSize = false; + _errorFlags |= kpv_ErrorFlags_HeadersError; + if (arcInfo.EndPos < endPos) + arcInfo.EndPos = endPos; + } + else + { + arch.AddToSeekValue(item.PackSize); // Position points to next header; + arcInfo.EndPos = arch.Position; + } + } + + bool needAdd = true; + + { + if (_comment.Size() == 0 + && item.Is_CMT() + && item.PackSize < kCommentSize_Max + && item.PackSize == item.Size + && item.PackSize != 0 + && item.GetMethod() == 0 + && !item.IsSplit()) + { + RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_VARS item, item.PackSize, inStream, _comment)); + needAdd = false; + } + } + + if (needAdd) + { + CRefItem ref; + ref.Item = _items.Size(); + ref.Last = ref.Item; + ref.Parent = -1; + ref.Link = -1; + + if (item.IsService()) + { + if (item.Is_STM()) + { + if (prevMainFile >= 0) + ref.Parent = prevMainFile; + } + else + { + needAdd = false; + if (item.Is_ACL() && (!item.IsEncrypted() || arch.m_CryptoMode)) + { + if (prevMainFile >= 0 && item.Size < (1 << 24) && item.Size != 0) + { + CItem &mainItem = _items[_refs[prevMainFile].Item]; + + if (mainItem.ACL < 0) + { + CByteBuffer acl; + HRESULT res = tempBuf.Decode(EXTERNAL_CODECS_VARS item, inStream, unpacker, acl); + if (!item.IsSplitAfter()) + tempBuf.Clear(); + if (res != S_OK) + { + tempBuf.Clear(); + if (res != S_FALSE && res != E_NOTIMPL) + return res; + } + // RINOK(); + + if (res == S_OK && acl.Size() != 0) + { + if (_acls.IsEmpty() || acl != _acls.Back()) + _acls.Add(acl); + mainItem.ACL = _acls.Size() - 1; + } + } + } + } + } + } + + if (needAdd) + { + if (item.IsSplitBefore()) + { + if (prevSplitFile >= 0) + { + CRefItem &ref = _refs[prevSplitFile]; + CItem &prevItem = _items[ref.Last]; + if (item.IsNextForItem(prevItem)) + { + ref.Last = _items.Size(); + prevItem.NextItem = ref.Last; + needAdd = false; + } + } + } + } + + if (needAdd) + { + if (item.IsSplitAfter()) + prevSplitFile = _refs.Size(); + if (!item.IsService()) + prevMainFile = _refs.Size(); + _refs.Add(ref); + } + } + + { + UInt64 version; + if (item.FindExtra_Version(version)) + { + item.Version_Defined = true; + item.Version = version; + } + } + + item.VolIndex = _arcs.Size() - 1; + _items.Add(item); + + if (openCallback && (_items.Size() & 0xFF) == 0) + { + UInt64 numFiles = _items.Size(); + UInt64 numBytes = curBytes + item.DataPos; + RINOK(openCallback->SetCompleted(&numFiles, &numBytes)); + } + + if (!isOk_packSize) + break; + } + + curBytes += endPos; + if (!arcInfo.IsVolume()) + break; + if (arcInfo.EndOfArchive_was_Read + && !arcInfo.AreMoreVolumes()) + break; + } + + FillLinks(); + + return S_OK; +} + + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback) +{ + COM_TRY_BEGIN + Close(); + return Open2(stream, maxCheckStartPosition, openCallback); + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + COM_TRY_BEGIN + _errorFlags = 0; + // _warningFlags = 0; + _isArc = false; + _refs.Clear(); + _items.Clear(); + _arcs.Clear(); + _acls.Clear(); + _comment.Free(); + return S_OK; + COM_TRY_END +} + + +class CVolsInStream: + public ISequentialInStream, + public CMyUnknownImp +{ + UInt64 _rem; + ISequentialInStream *_stream; + const CObjectVector<CArc> *_arcs; + const CObjectVector<CItem> *_items; + int _itemIndex; +public: + bool CrcIsOK; +private: + CHash _hash; +public: + MY_UNKNOWN_IMP + void Init(const CObjectVector<CArc> *arcs, + const CObjectVector<CItem> *items, + unsigned itemIndex) + { + _arcs = arcs; + _items = items; + _itemIndex = itemIndex; + _stream = NULL; + CrcIsOK = true; + } + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP CVolsInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + UInt32 realProcessedSize = 0; + + while (size != 0) + { + if (!_stream) + { + if (_itemIndex < 0) + break; + const CItem &item = (*_items)[_itemIndex]; + IInStream *s = (*_arcs)[item.VolIndex].Stream; + RINOK(s->Seek(item.GetDataPosition(), STREAM_SEEK_SET, NULL)); + _stream = s; + if (CrcIsOK && item.IsSplitAfter()) + _hash.Init(item); + else + _hash.Init_NoCalc(); + _rem = item.PackSize; + } + { + UInt32 cur = size; + if (cur > _rem) + cur = (UInt32)_rem; + UInt32 num = cur; + HRESULT res = _stream->Read(data, cur, &cur); + _hash.Update(data, cur); + realProcessedSize += cur; + if (processedSize) + *processedSize = realProcessedSize; + data = (Byte *)data + cur; + size -= cur; + _rem -= cur; + if (_rem == 0) + { + const CItem &item = (*_items)[_itemIndex]; + _itemIndex = item.NextItem; + if (!_hash.Check(item, NULL)) // RAR doesn't use MAC here + CrcIsOK = false; + _stream = NULL; + } + if (res != S_OK) + return res; + if (realProcessedSize != 0) + return S_OK; + if (cur == 0 && num != 0) + return S_OK; + } + } + + return S_OK; +} + + +static int FindLinkBuf(CObjectVector<CLinkFile> &linkFiles, unsigned index) +{ + unsigned left = 0, right = linkFiles.Size(); + for (;;) + { + if (left == right) + return -1; + unsigned mid = (left + right) / 2; + unsigned linkIndex = linkFiles[mid].Index; + if (index == linkIndex) + return mid; + if (index < linkIndex) + right = mid; + else + left = mid + 1; + } +} + + +static inline int DecoderRes_to_OpRes(HRESULT res, bool crcOK) +{ + if (res == E_NOTIMPL) + return NExtract::NOperationResult::kUnsupportedMethod; + // if (res == S_FALSE) + if (res != S_OK) + return NExtract::NOperationResult::kDataError; + return crcOK ? + NExtract::NOperationResult::kOK : + NExtract::NOperationResult::kCRCError; +} + + +static HRESULT CopyData_with_Progress(const Byte *data, size_t size, + ISequentialOutStream *outStream, ICompressProgressInfo *progress) +{ + size_t pos = 0; + + while (pos < size) + { + const UInt32 kStepSize = ((UInt32)1 << 24); + UInt32 cur32; + { + size_t cur = size - pos; + if (cur > kStepSize) + cur = kStepSize; + cur32 = (UInt32)cur; + } + RINOK(outStream->Write(data + pos, cur32, &cur32)); + if (cur32 == 0) + return E_FAIL; + pos += cur32; + if (progress) + { + UInt64 pos64 = pos; + RINOK(progress->SetRatioInfo(&pos64, &pos64)); + } + } + + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + + bool allFilesMode = (numItems == (UInt32)(Int32)-1); + if (allFilesMode) + numItems = _refs.Size(); + if (numItems == 0) + return S_OK; + + CByteArr extractStatuses(_refs.Size()); + memset(extractStatuses, 0, _refs.Size()); + + // we don't want to use temp buffer for big link files. + const size_t k_CopyLinkFile_MaxSize = (size_t)1 << (28 + sizeof(size_t) / 2); + + const Byte kStatus_Extract = 1 << 0; + const Byte kStatus_Skip = 1 << 1; + const Byte kStatus_Link = 1 << 2; + + CObjectVector<CLinkFile> linkFiles; + + { + UInt64 total = 0; + bool thereAreLinks = false; + + { + unsigned solidLimit = 0; + for (UInt32 t = 0; t < numItems; t++) + { + unsigned index = allFilesMode ? t : indices[t]; + const CRefItem &ref = _refs[index]; + const CItem &item = _items[ref.Item]; + + extractStatuses[index] |= kStatus_Extract; + total += item.Size; + + if (ref.Link >= 0) + { + if (!testMode) + { + if ((unsigned)ref.Link < index) + { + const CItem &linkItem = _items[_refs[(unsigned)ref.Link].Item]; + if (linkItem.IsSolid() && linkItem.Size <= k_CopyLinkFile_MaxSize) + { + if (extractStatuses[(unsigned)ref.Link] == 0) + total += linkItem.Size; + extractStatuses[(unsigned)ref.Link] |= kStatus_Link; + thereAreLinks = true; + } + } + } + continue; + } + + if (item.IsService()) + continue; + + if (item.IsSolid()) + { + unsigned j = index; + + while (j > solidLimit) + { + j--; + const CItem &item2 = _items[_refs[j].Item]; + if (!item2.IsService()) + { + if (extractStatuses[j] == 0) + total += item2.Size; + extractStatuses[j] |= kStatus_Skip; + if (!item2.IsSolid()) + break; + } + } + } + + solidLimit = index + 1; + } + } + + if (thereAreLinks) + { + unsigned solidLimit = 0; + + FOR_VECTOR(i, _refs) + { + if ((extractStatuses[i] & kStatus_Link) == 0) + continue; + const CItem &item = _items[_refs[i].Item]; + /* + if (item.IsService()) + continue; + */ + + CLinkFile &linkFile = linkFiles.AddNew(); + linkFile.Index = i; + + if (item.IsSolid()) + { + unsigned j = i; + + while (j > solidLimit) + { + j--; + const CItem &item2 = _items[_refs[j].Item]; + if (!item2.IsService()) + { + if (extractStatuses[j] != 0) + break; + extractStatuses[j] = kStatus_Skip; + total += item2.Size; + if (!item2.IsSolid()) + break; + } + } + } + + solidLimit = i + 1; + } + + for (UInt32 t = 0; t < numItems; t++) + { + unsigned index = allFilesMode ? t : indices[t]; + const CRefItem &ref = _refs[index]; + + int linkIndex = ref.Link; + if (linkIndex < 0 || (unsigned)linkIndex >= index) + continue; + const CItem &linkItem = _items[_refs[(unsigned)linkIndex].Item]; + if (!linkItem.IsSolid() || linkItem.Size > k_CopyLinkFile_MaxSize) + continue; + int bufIndex = FindLinkBuf(linkFiles, linkIndex); + if (bufIndex < 0) + return E_FAIL; + linkFiles[bufIndex].NumLinks++; + } + } + + RINOK(extractCallback->SetTotal(total)); + } + + + UInt64 totalUnpacked = 0; + UInt64 totalPacked = 0; + UInt64 curUnpackSize = 0; + UInt64 curPackSize = 0; + + CUnpacker unpacker; + + CVolsInStream *volsInStreamSpec = new CVolsInStream; + CMyComPtr<ISequentialInStream> volsInStream = volsInStreamSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + // bool needClearSolid = true; + + FOR_VECTOR(i, _refs) + { + if (extractStatuses[i] == 0) + continue; + + totalUnpacked += curUnpackSize; + totalPacked += curPackSize; + lps->InSize = totalPacked; + lps->OutSize = totalUnpacked; + RINOK(lps->SetCur()); + + CMyComPtr<ISequentialOutStream> realOutStream; + + Int32 askMode = + ((extractStatuses[i] & kStatus_Extract) != 0) ? (testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract) : + NExtract::NAskMode::kSkip; + + unpacker.linkFile = NULL; + + if (((extractStatuses[i] & kStatus_Link) != 0)) + { + int bufIndex = FindLinkBuf(linkFiles, i); + if (bufIndex < 0) + return E_FAIL; + unpacker.linkFile = &linkFiles[bufIndex]; + } + + UInt32 index = i; + + const CRefItem *ref = &_refs[index]; + const CItem *item = &_items[ref->Item]; + + curUnpackSize = item->Size; + curPackSize = GetPackSize(index); + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + bool isSolid; + { + bool &needClearSolid = unpacker.NeedClearSolid[item->IsService() ? 1 : 0]; + isSolid = (item->IsSolid() && !needClearSolid); + if (item->IsService()) + isSolid = false; + needClearSolid = !item->IsSolid(); + } + + if (item->IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + int index2 = ref->Link; + + int bufIndex = -1; + + if (index2 >= 0) + { + const CRefItem &ref2 = _refs[index2]; + const CItem &item2 = _items[ref2.Item]; + if (!item2.IsSolid()) + { + item = &item2; + ref = &ref2; + curUnpackSize = item->Size; + curPackSize = GetPackSize(index2); + } + else if ((unsigned)index2 < index) + bufIndex = FindLinkBuf(linkFiles, index2); + } + + if (!realOutStream) + { + if (testMode) + { + if (item->Is_CopyLink() && item->PackSize == 0) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + } + else + { + if (item->IsService()) + continue; + + bool needDecode = false; + + for (unsigned n = i + 1; n < _refs.Size(); n++) + { + const CItem &nextItem = _items[_refs[n].Item]; + if (nextItem.IsService()) + continue; + if (!nextItem.IsSolid()) + break; + if (extractStatuses[i] != 0) + { + needDecode = true; + break; + } + } + + if (!needDecode) + continue; + + askMode = NExtract::NAskMode::kSkip; + } + } + + RINOK(extractCallback->PrepareOperation(askMode)); + + if (bufIndex >= 0) + { + CLinkFile &linkFile = linkFiles[bufIndex]; + if (linkFile.NumLinks == 0) + return E_FAIL; + if (realOutStream) + { + RINOK(CopyData_with_Progress(linkFile.Data, linkFile.Data.Size(), realOutStream, progress)); + } + if (--linkFile.NumLinks == 0) + linkFile.Data.Free(); + RINOK(extractCallback->SetOperationResult(DecoderRes_to_OpRes(linkFile.Res, linkFile.crcOK))); + continue; + } + + if (item->Is_CopyLink() && item->PackSize == 0) + { + RINOK(extractCallback->SetOperationResult( + realOutStream ? + NExtract::NOperationResult::kUnsupportedMethod: + NExtract::NOperationResult::kOK)); + continue; + } + + volsInStreamSpec->Init(&_arcs, &_items, ref->Item); + + UInt64 packSize = curPackSize; + + if (item->IsEncrypted()) + if (!unpacker.getTextPassword) + extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&unpacker.getTextPassword); + + bool wrongPassword; + HRESULT result = unpacker.Create(EXTERNAL_CODECS_VARS *item, isSolid, wrongPassword); + + if (wrongPassword) + { + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kWrongPassword)); + continue; + } + + bool crcOK = true; + if (result == S_OK) + result = unpacker.Code(*item, _items[ref->Last], packSize, volsInStream, realOutStream, progress, crcOK); + realOutStream.Release(); + if (!volsInStreamSpec->CrcIsOK) + crcOK = false; + + int opRes = crcOK ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError; + + if (result != S_OK) + { + if (result == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else if (result == E_NOTIMPL) + opRes = NExtract::NOperationResult::kUnsupportedMethod; + else + return result; + } + + RINOK(extractCallback->SetOperationResult(opRes)); + } + + { + FOR_VECTOR(i, linkFiles) + if (linkFiles[i].NumLinks != 0) + return E_FAIL; + } + + return S_OK; + + COM_TRY_END +} + + +IMPL_ISetCompressCodecsInfo + +REGISTER_ARC_I( + "Rar5", "rar r00", 0, 0xCC, + kMarker, + 0, + NArcInfoFlags::kFindSignature, + NULL) + +}} + + +class CBlake2spHasher: + public IHasher, + public CMyUnknownImp +{ + CBlake2sp _blake; + Byte mtDummy[1 << 7]; + +public: + CBlake2spHasher() { Init(); } + + MY_UNKNOWN_IMP + INTERFACE_IHasher(;) +}; + +STDMETHODIMP_(void) CBlake2spHasher::Init() throw() +{ + Blake2sp_Init(&_blake); +} + +STDMETHODIMP_(void) CBlake2spHasher::Update(const void *data, UInt32 size) throw() +{ + Blake2sp_Update(&_blake, (const Byte *)data, size); +} + +STDMETHODIMP_(void) CBlake2spHasher::Final(Byte *digest) throw() +{ + Blake2sp_Final(&_blake, digest); +} + +REGISTER_HASHER(CBlake2spHasher, 0x202, "BLAKE2sp", BLAKE2S_DIGEST_SIZE) diff --git a/CPP/7zip/Archive/Rar/Rar5Handler.h b/CPP/7zip/Archive/Rar/Rar5Handler.h new file mode 100644 index 00000000..5eaf0075 --- /dev/null +++ b/CPP/7zip/Archive/Rar/Rar5Handler.h @@ -0,0 +1,410 @@ +// Rar5Handler.h + +#ifndef __RAR5_HANDLER_H +#define __RAR5_HANDLER_H + +#include "../../../../C/Blake2.h" + +#include "../../../Common/MyBuffer.h" + +#include "../../../Windows/PropVariant.h" + +#include "../../Common/CreateCoder.h" + +#include "../IArchive.h" + +namespace NArchive { +namespace NRar5 { + +namespace NHeaderFlags +{ + const unsigned kExtra = 1 << 0; + const unsigned kData = 1 << 1; + // const unsigned kUnknown = 1 << 2; + const unsigned kPrevVol = 1 << 3; + const unsigned kNextVol = 1 << 4; + // const unsigned kIsChild = 1 << 5; + // const unsigned kPreserveChild = 1 << 6; +} + +namespace NHeaderType +{ + enum + { + kArc = 1, + kFile, + kService, + kArcEncrypt, + kEndOfArc + }; +} + +namespace NArcFlags +{ + const unsigned kVol = 1 << 0; + const unsigned kVolNumber = 1 << 1; + const unsigned kSolid = 1 << 2; + // const unsigned kRecovery = 1 << 3; + // const unsigned kLocked = 1 << 4; +} + +const unsigned kArcExtraRecordType_Locator = 1; + +namespace NLocatorFlags +{ + const unsigned kQuickOpen = 1 << 0; + const unsigned kRecovery = 1 << 1; +} + +namespace NFileFlags +{ + const unsigned kIsDir = 1 << 0; + const unsigned kUnixTime = 1 << 1; + const unsigned kCrc32 = 1 << 2; + const unsigned kUnknownSize = 1 << 3; +} + +namespace NMethodFlags +{ + // const unsigned kVersionMask = 0x3F; + const unsigned kSolid = 1 << 6; +} + +namespace NArcEndFlags +{ + const unsigned kMoreVols = 1 << 0; +} + +enum EHostOS +{ + kHost_Windows = 0, + kHost_Unix +}; + + + +// ---------- Extra ---------- + +namespace NExtraRecordType +{ + enum + { + kCrypto = 1, + kHash, + kTime, + kVersion, + kLink, + kUnixOwner, + kSubdata + }; +} + +// const unsigned kCryptoAlgo_AES = 0; + +namespace NCryptoFlags +{ + const unsigned kPswCheck = 1 << 0; + const unsigned kUseMAC = 1 << 1; +} + +struct CCryptoInfo +{ + UInt64 Algo; + UInt64 Flags; + Byte Cnt; + + bool UseMAC() const { return (Flags & NCryptoFlags::kUseMAC) != 0; } + bool IsThereCheck() const { return (Flags & NCryptoFlags::kPswCheck) != 0; } + bool Parse(const Byte *p, size_t size); +}; + +const unsigned kHashID_Blake2sp = 0; + +namespace NTimeRecord +{ + enum + { + k_Index_MTime = 0, + k_Index_CTime, + k_Index_ATime + }; + + namespace NFlags + { + const unsigned kUnixTime = 1 << 0; + const unsigned kMTime = 1 << 1; + // const unsigned kCTime = 1 << 2; + // const unsigned kATime = 1 << 3; + } +} + +namespace NLinkType +{ + enum + { + kUnixSymLink = 1, + kWinSymLink, + kWinJunction, + kHardLink, + kFileCopy + }; +} + +namespace NLinkFlags +{ + const unsigned kTargetIsDir = 1 << 0; +} + + +struct CItem +{ + UInt32 CommonFlags; + UInt32 Flags; + + Byte RecordType; + bool Version_Defined; + + int ACL; + + AString Name; + + int VolIndex; + int NextItem; + + UInt32 UnixMTime; + UInt32 CRC; + UInt32 Attrib; + UInt32 Method; + + CByteBuffer Extra; + + UInt64 Size; + UInt64 PackSize; + UInt64 HostOS; + + UInt64 DataPos; + UInt64 Version; + + CItem() { Clear(); } + + void Clear() + { + CommonFlags = 0; + Flags = 0; + + VolIndex = 0; + NextItem = -1; + + Version_Defined = false; + Version = 0; + + Name.Empty(); + Extra.Free(); + ACL = -1; + } + + bool IsSplitBefore() const { return (CommonFlags & NHeaderFlags::kPrevVol) != 0; } + bool IsSplitAfter() const { return (CommonFlags & NHeaderFlags::kNextVol) != 0; } + bool IsSplit() const { return (CommonFlags & (NHeaderFlags::kPrevVol | NHeaderFlags::kNextVol)) != 0; } + + bool IsDir() const { return (Flags & NFileFlags::kIsDir) != 0; } + bool Has_UnixMTime() const { return (Flags & NFileFlags::kUnixTime) != 0; } + bool Has_CRC() const { return (Flags & NFileFlags::kCrc32) != 0; } + bool Is_UnknownSize() const { return (Flags & NFileFlags::kUnknownSize) != 0; } + + bool IsNextForItem(const CItem &prev) const + { + return !IsDir() && !prev.IsDir() && IsSplitBefore() && prev.IsSplitAfter() && (Name == prev.Name); + // && false; + } + + bool IsSolid() const { return ((UInt32)Method & NMethodFlags::kSolid) != 0; } + unsigned GetAlgoVersion() const { return (unsigned)Method & 0x3F; } + unsigned GetMethod() const { return ((unsigned)Method >> 7) & 0x7; } + UInt32 GetDictSize() const { return (((UInt32)Method >> 10) & 0xF); } + + bool IsService() const { return RecordType == NHeaderType::kService; } + + bool Is_STM() const { return IsService() && Name == "STM"; } + bool Is_CMT() const { return IsService() && Name == "CMT"; } + bool Is_ACL() const { return IsService() && Name == "ACL"; } + // bool Is_QO() const { return IsService() && Name == "QO"; } + + int FindExtra(unsigned type, unsigned &recordDataSize) const; + + bool IsEncrypted() const + { + unsigned size; + return FindExtra(NExtraRecordType::kCrypto, size) >= 0; + } + + int FindExtra_Blake() const + { + unsigned size = 0; + int offset = FindExtra(NExtraRecordType::kHash, size); + if (offset >= 0 + && size == BLAKE2S_DIGEST_SIZE + 1 + && Extra[(unsigned)offset] == kHashID_Blake2sp) + return offset + 1; + return -1; + } + + bool FindExtra_Version(UInt64 &version) const; + + struct CLinkInfo + { + UInt64 Type; + UInt64 Flags; + unsigned NameOffset; + unsigned NameLen; + }; + + bool FindExtra_Link(CLinkInfo &link) const; + void Link_to_Prop(unsigned linkType, NWindows::NCOM::CPropVariant &prop) const; + bool Is_CopyLink() const; + + bool NeedUse_as_CopyLink() const { return PackSize == 0 && Is_CopyLink(); } + + bool GetAltStreamName(AString &name) const; + + UInt32 GetWinAttrib() const + { + UInt32 a; + switch (HostOS) + { + case kHost_Windows: a = Attrib; break; + case kHost_Unix: a = (Attrib << 16); break; + default: a = 0; + } + // if (IsDir()) a |= FILE_ATTRIBUTE_DIRECTORY; + return a; + } + + UInt64 GetDataPosition() const { return DataPos; } +}; + + +struct CInArcInfo +{ + UInt64 Flags; + UInt64 VolNumber; + UInt64 StartPos; + UInt64 EndPos; + + UInt64 EndFlags; + bool EndOfArchive_was_Read; + + bool IsEncrypted; + + // CByteBuffer Extra; + + /* + struct CLocator + { + UInt64 Flags; + UInt64 QuickOpen; + UInt64 Recovery; + + bool Is_QuickOpen() const { return (Flags & NLocatorFlags::kQuickOpen) != 0; } + bool Is_Recovery() const { return (Flags & NLocatorFlags::kRecovery) != 0; } + }; + + int FindExtra(unsigned type, unsigned &recordDataSize) const; + bool FindExtra_Locator(CLocator &locator) const; + */ + + CInArcInfo(): + Flags(0), + VolNumber(0), + StartPos(0), + EndPos(0), + EndFlags(0), + EndOfArchive_was_Read(false), + IsEncrypted(false) + {} + + /* + void Clear() + { + Flags = 0; + VolNumber = 0; + StartPos = 0; + EndPos = 0; + EndFlags = 0; + EndOfArchive_was_Read = false; + Extra.Free(); + } + */ + + UInt64 GetPhySize() const { return EndPos - StartPos; } + + bool AreMoreVolumes() const { return (EndFlags & NArcEndFlags::kMoreVols) != 0; } + + bool IsVolume() const { return (Flags & NArcFlags::kVol) != 0; } + bool IsSolid() const { return (Flags & NArcFlags::kSolid) != 0; } + bool Is_VolNumber_Defined() const { return (Flags & NArcFlags::kVolNumber) != 0; } + + UInt64 GetVolIndex() const { return Is_VolNumber_Defined() ? VolNumber : 0; } +}; + + +struct CRefItem +{ + unsigned Item; + unsigned Last; + int Parent; + int Link; +}; + + +struct CArc +{ + CMyComPtr<IInStream> Stream; + CInArcInfo Info; +}; + + +class CHandler: + public IInArchive, + public IArchiveGetRawProps, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ +public: + CRecordVector<CRefItem> _refs; + CObjectVector<CItem> _items; +private: + CObjectVector<CArc> _arcs; + CObjectVector<CByteBuffer> _acls; + + UInt32 _errorFlags; + // UInt32 _warningFlags; + bool _isArc; + CByteBuffer _comment; + + DECL_EXTERNAL_CODECS_VARS + + UInt64 GetPackSize(unsigned refIndex) const; + + void FillLinks(); + + HRESULT Open2(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback); + +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + MY_QUERYINTERFACE_ENTRY(IArchiveGetRawProps) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + INTERFACE_IArchiveGetRawProps(;) + + DECL_ISetCompressCodecsInfo +}; + +}} + +#endif diff --git a/CPP/7zip/Archive/Rar/RarHandler.cpp b/CPP/7zip/Archive/Rar/RarHandler.cpp index 516e5d7f..d6d894f4 100644 --- a/CPP/7zip/Archive/Rar/RarHandler.cpp +++ b/CPP/7zip/Archive/Rar/RarHandler.cpp @@ -30,6 +30,7 @@ #include "../Common/ItemNameUtils.h" #include "../Common/OutStreamWithCRC.h" +#include "RarVol.h" #include "RarHandler.h" using namespace NWindows; @@ -44,6 +45,8 @@ namespace NRar { static const Byte kMarker[NHeader::kMarkerSize] = SIGNATURE; +const unsigned kPasswordLen_MAX = 127; + bool CItem::IgnoreItem() const { switch (HostOS) @@ -131,7 +134,7 @@ class CInArchive CByteBuffer _comment; CByteBuffer m_FileHeaderData; NHeader::NBlock::CBlock m_BlockHeader; - NCrypto::NRar29::CDecoder *m_RarAESSpec; + NCrypto::NRar3::CDecoder *m_RarAESSpec; CMyComPtr<ICompressFilter> m_RarAES; CBuffer<Byte> m_DecryptedData; Byte *m_DecryptedDataAligned; @@ -362,6 +365,7 @@ static int ReadTime(const Byte *p, unsigned size, Byte mask, CRarTime &rarTime) _ttt_ .DosTime = Get32(p); p += 4; size -= 4; \ READ_TIME(_mask_, _ttt_); } \ + bool CInArchive::ReadHeaderReal(const Byte *p, unsigned size, CItem &item) { const Byte *pStart = p; @@ -500,7 +504,7 @@ HRESULT CInArchive::GetNextItem(CItem &item, ICryptoGetTextPassword *getTextPass } if (!m_RarAES) { - m_RarAESSpec = new NCrypto::NRar29::CDecoder; + m_RarAESSpec = new NCrypto::NRar3::CDecoder; m_RarAES = m_RarAESSpec; } m_RarAESSpec->SetRar350Mode(ArcInfo.IsEncryptOld()); @@ -518,7 +522,10 @@ HRESULT CInArchive::GetNextItem(CItem &item, ICryptoGetTextPassword *getTextPass unsigned len = 0; if (password) len = MyStringLen(password); - CByteBuffer buffer(len * 2); + if (len > kPasswordLen_MAX) + len = kPasswordLen_MAX; + + CByteArr buffer(len * 2); for (unsigned i = 0; i < len; i++) { wchar_t c = password[i]; @@ -526,7 +533,7 @@ HRESULT CInArchive::GetNextItem(CItem &item, ICryptoGetTextPassword *getTextPass ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } - RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size())); + m_RarAESSpec->SetPassword((const Byte *)buffer, len * 2); const UInt32 kDecryptedBufferSize = (1 << 12); if (m_DecryptedData.Size() == 0) @@ -990,121 +997,8 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } -static bool IsDigit(wchar_t c) -{ - return c >= L'0' && c <= L'9'; -} -class CVolumeName -{ - bool _first; - bool _newStyle; - UString _unchangedPart; - UString _changedPart; - UString _afterPart; -public: - CVolumeName(): _newStyle(true) {}; - - bool InitName(const UString &name, bool newStyle) - { - _first = true; - _newStyle = newStyle; - int dotPos = name.ReverseFind_Dot(); - UString basePart = name; - - if (dotPos >= 0) - { - UString ext = name.Ptr(dotPos + 1); - if (ext.IsEqualTo_Ascii_NoCase("rar")) - { - _afterPart = name.Ptr(dotPos); - basePart = name.Left(dotPos); - } - else if (ext.IsEqualTo_Ascii_NoCase("exe")) - { - _afterPart.SetFromAscii(".rar"); - basePart = name.Left(dotPos); - } - else if (!_newStyle) - { - if (ext.IsEqualTo_Ascii_NoCase("000") || - ext.IsEqualTo_Ascii_NoCase("001") || - ext.IsEqualTo_Ascii_NoCase("r00") || - ext.IsEqualTo_Ascii_NoCase("r01")) - { - _afterPart.Empty(); - _first = false; - _changedPart = ext; - _unchangedPart = name.Left(dotPos + 1); - return true; - } - } - } - - if (!_newStyle) - { - _afterPart.Empty(); - _unchangedPart = basePart; - _unchangedPart += L'.'; - _changedPart.SetFromAscii("r00"); - return true; - } - - if (basePart.IsEmpty()) - return false; - unsigned i = basePart.Len(); - - do - if (!IsDigit(basePart[i - 1])) - break; - while (--i); - - _unchangedPart = basePart.Left(i); - _changedPart = basePart.Ptr(i); - return true; - } - - /* - void MakeBeforeFirstName() - { - unsigned len = _changedPart.Len(); - _changedPart.Empty(); - for (unsigned i = 0; i < len; i++) - _changedPart += L'0'; - } - */ - - UString GetNextName() - { - if (_newStyle || !_first) - { - unsigned i = _changedPart.Len(); - for (;;) - { - wchar_t c = _changedPart[--i]; - if (c == L'9') - { - c = L'0'; - _changedPart.ReplaceOneCharAtPos(i, c); - if (i == 0) - { - _changedPart.InsertAtFront(L'1'); - break; - } - continue; - } - c++; - _changedPart.ReplaceOneCharAtPos(i, c); - break; - } - } - - _first = false; - return _unchangedPart + _changedPart + _afterPart; - } -}; - -static HRESULT ReadZeroTail(ISequentialInStream *stream, bool &areThereNonZeros, UInt64 &numZeros, UInt64 maxSize) +HRESULT ReadZeroTail(ISequentialInStream *stream, bool &areThereNonZeros, UInt64 &numZeros, UInt64 maxSize) { areThereNonZeros = false; numZeros = 0; @@ -1136,7 +1030,6 @@ HRESULT CHandler::Open2(IInStream *stream, { CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; CMyComPtr<ICryptoGetTextPassword> getTextPassword; - CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openCallback; CVolumeName seqName; @@ -1145,8 +1038,8 @@ HRESULT CHandler::Open2(IInStream *stream, if (openCallback) { - openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback); - openArchiveCallbackWrap.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); + openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); + openCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); } CInArchive archive; @@ -1363,108 +1256,91 @@ struct CMethodItem }; -class CFolderInStream: +class CVolsInStream: public ISequentialInStream, public CMyUnknownImp { -public: - MY_UNKNOWN_IMP - - STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); - -private: - const CObjectVector<CArc> *_archives; + UInt64 _rem; + ISequentialInStream *_stream; + const CObjectVector<CArc> *_arcs; const CObjectVector<CItem> *_items; CRefItem _refItem; unsigned _curIndex; UInt32 _crc; - bool _fileIsOpen; - CMyComPtr<ISequentialInStream> _stream; + bool _calcCrc; - HRESULT OpenStream(); - HRESULT CloseStream(); public: - void Init(const CObjectVector<CArc> *archives, - const CObjectVector<CItem> *items, - const CRefItem &refItem); - - CRecordVector<UInt32> CRCs; -}; - - -ISequentialInStream* CArc::CreateLimitedStream(UInt64 offset, UInt64 size) const -{ - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> inStream(streamSpec); - Stream->Seek(offset, STREAM_SEEK_SET, NULL); - streamSpec->SetStream(Stream); - streamSpec->Init(size); - return inStream.Detach(); -} - -void CFolderInStream::Init( - const CObjectVector<CArc> *archives, - const CObjectVector<CItem> *items, - const CRefItem &refItem) -{ - _archives = archives; - _items = items; - _refItem = refItem; - _curIndex = 0; - CRCs.Clear(); - _fileIsOpen = false; -} + MY_UNKNOWN_IMP -HRESULT CFolderInStream::OpenStream() -{ - while (_curIndex < _refItem.NumItems) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + void Init(const CObjectVector<CArc> *arcs, + const CObjectVector<CItem> *items, + const CRefItem &refItem) { - const CItem &item = (*_items)[_refItem.ItemIndex + _curIndex]; - _stream.Attach((*_archives)[_refItem.VolumeIndex + _curIndex]. - CreateLimitedStream(item.GetDataPosition(), item.PackSize)); - _curIndex++; - _fileIsOpen = true; - _crc = CRC_INIT_VAL; - return S_OK; + _arcs = arcs; + _items = items; + _refItem = refItem; + _curIndex = 0; + _stream = NULL; + CrcIsOK = true; } - return S_OK; -} -HRESULT CFolderInStream::CloseStream() -{ - CRCs.Add(CRC_GET_DIGEST(_crc)); - _stream.Release(); - _fileIsOpen = false; - return S_OK; -} + bool CrcIsOK; +}; -STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) + +STDMETHODIMP CVolsInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { + if (processedSize) + *processedSize = 0; UInt32 realProcessedSize = 0; - while ((_curIndex < _refItem.NumItems || _fileIsOpen) && size > 0) + + while (size != 0) { - if (_fileIsOpen) + if (!_stream) { - UInt32 localProcessedSize; - RINOK(_stream->Read( - ((Byte *)data) + realProcessedSize, size, &localProcessedSize)); - _crc = CrcUpdate(_crc, ((Byte *)data) + realProcessedSize, localProcessedSize); - if (localProcessedSize == 0) - { - RINOK(CloseStream()); - continue; - } - realProcessedSize += localProcessedSize; - size -= localProcessedSize; - break; + if (_curIndex >= _refItem.NumItems) + break; + const CItem &item = (*_items)[_refItem.ItemIndex + _curIndex]; + IInStream *s = (*_arcs)[_refItem.VolumeIndex + _curIndex].Stream; + RINOK(s->Seek(item.GetDataPosition(), STREAM_SEEK_SET, NULL)); + _stream = s; + _calcCrc = (CrcIsOK && item.IsSplitAfter()); + _crc = CRC_INIT_VAL; + _rem = item.PackSize; } - else { - RINOK(OpenStream()); + UInt32 cur = size; + if (cur > _rem) + cur = (UInt32)_rem; + UInt32 num = cur; + HRESULT res = _stream->Read(data, cur, &cur); + if (_calcCrc) + _crc = CrcUpdate(_crc, data, cur); + realProcessedSize += cur; + if (processedSize) + *processedSize = realProcessedSize; + data = (Byte *)data + cur; + size -= cur; + _rem -= cur; + if (_rem == 0) + { + const CItem &item = (*_items)[_refItem.ItemIndex + _curIndex]; + _curIndex++; + if (_calcCrc && CRC_GET_DIGEST(_crc) != item.FileCRC) + CrcIsOK = false; + _stream = NULL; + } + if (res != S_OK) + return res; + if (realProcessedSize != 0) + return S_OK; + if (cur == 0 && num != 0) + return S_OK; } } - if (processedSize != 0) - *processedSize = realProcessedSize; + return S_OK; } @@ -1526,13 +1402,13 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CFilterCoder *filterStreamSpec = new CFilterCoder(false); CMyComPtr<ISequentialInStream> filterStream = filterStreamSpec; - NCrypto::NRar20::CDecoder *rar20CryptoDecoderSpec = NULL; + NCrypto::NRar2::CDecoder *rar20CryptoDecoderSpec = NULL; CMyComPtr<ICompressFilter> rar20CryptoDecoder; - NCrypto::NRar29::CDecoder *rar29CryptoDecoderSpec = NULL; - CMyComPtr<ICompressFilter> rar29CryptoDecoder; + NCrypto::NRar3::CDecoder *rar3CryptoDecoderSpec = NULL; + CMyComPtr<ICompressFilter> rar3CryptoDecoder; - CFolderInStream *folderInStreamSpec = NULL; - CMyComPtr<ISequentialInStream> folderInStream; + CVolsInStream *volsInStreamSpec = NULL; + CMyComPtr<ISequentialInStream> volsInStream; CLocalProgress *lps = new CLocalProgress; CMyComPtr<ICompressProgressInfo> progress = lps; @@ -1602,26 +1478,13 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, outStreamSpec->Init(); realOutStream.Release(); - /* - for (unsigned partIndex = 0; partIndex < 1; partIndex++) - { - CMyComPtr<ISequentialInStream> inStream; - - // item redefinition - const CItem &item = _items[refItem.ItemIndex + partIndex]; - - CInArchive &archive = _arcs[refItem.VolumeIndex + partIndex]; - - inStream.Attach(archive.CreateLimitedStream(item.GetDataPosition(), - item.PackSize)); - */ - if (!folderInStream) + if (!volsInStream) { - folderInStreamSpec = new CFolderInStream; - folderInStream = folderInStreamSpec; + volsInStreamSpec = new CVolsInStream; + volsInStream = volsInStreamSpec; } - folderInStreamSpec->Init(&_arcs, &_items, refItem); + volsInStreamSpec->Init(&_arcs, &_items, refItem); UInt64 packSize = currentPackSize; @@ -1632,29 +1495,29 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (item.IsEncrypted()) { - CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + // CMyComPtr<ICryptoSetPassword> cryptoSetPassword; if (item.UnPackVersion >= 29) { - if (!rar29CryptoDecoder) + if (!rar3CryptoDecoder) { - rar29CryptoDecoderSpec = new NCrypto::NRar29::CDecoder; - rar29CryptoDecoder = rar29CryptoDecoderSpec; + rar3CryptoDecoderSpec = new NCrypto::NRar3::CDecoder; + rar3CryptoDecoder = rar3CryptoDecoderSpec; } - rar29CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36); + rar3CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36); /* CMyComPtr<ICompressSetDecoderProperties2> cryptoProperties; - RINOK(rar29CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, + RINOK(rar3CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &cryptoProperties)); */ - RINOK(rar29CryptoDecoderSpec->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0)); - filterStreamSpec->Filter = rar29CryptoDecoder; + RINOK(rar3CryptoDecoderSpec->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0)); + filterStreamSpec->Filter = rar3CryptoDecoder; } else if (item.UnPackVersion >= 20) { if (!rar20CryptoDecoder) { - rar20CryptoDecoderSpec = new NCrypto::NRar20::CDecoder; + rar20CryptoDecoderSpec = new NCrypto::NRar2::CDecoder; rar20CryptoDecoder = rar20CryptoDecoderSpec; } filterStreamSpec->Filter = rar20CryptoDecoder; @@ -1666,49 +1529,66 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, continue; } - RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword)); + // RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword)); if (!getTextPassword) extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); - if (getTextPassword) + + if (!getTextPassword) + { + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod)); + continue; + } + + // if (getTextPassword) { CMyComBSTR password; RINOK(getTextPassword->CryptoGetTextPassword(&password)); + if (item.UnPackVersion >= 29) { - UString unicodePassword; unsigned len = 0; if (password) len = MyStringLen(password); - CByteBuffer buffer(len * 2); + if (len > kPasswordLen_MAX) + len = kPasswordLen_MAX; + CByteArr buffer(len * 2); for (unsigned i = 0; i < len; i++) { wchar_t c = password[i]; ((Byte *)buffer)[i * 2] = (Byte)c; ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } - RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size())); + rar3CryptoDecoderSpec->SetPassword((const Byte *)buffer, len * 2); } else { AString oemPassword; if (password) - oemPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP); - RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)(const char *)oemPassword, oemPassword.Len())); + { + UString unicode = (LPCOLESTR)password; + if (unicode.Len() > kPasswordLen_MAX) + unicode.DeleteFrom(kPasswordLen_MAX); + oemPassword = UnicodeStringToMultiByte(unicode, CP_OEMCP); + } + rar20CryptoDecoderSpec->SetPassword((const Byte *)(const char *)oemPassword, oemPassword.Len()); } } + /* else { - RINOK(cryptoSetPassword->CryptoSetPassword(0, 0)); + RINOK(cryptoSetPassword->CryptoSetPassword(NULL, 0)); } + */ - filterStreamSpec->SetInStream(folderInStream); + filterStreamSpec->SetInStream(volsInStream); filterStreamSpec->SetOutStreamSize(NULL); inStream = filterStream; } else { - inStream = folderInStream; + inStream = volsInStream; } CMyComPtr<ICompressCoder> commonCoder; @@ -1766,7 +1646,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Byte isSolid = (Byte)((IsSolid(index) || item.IsSplitBefore()) ? 1: 0); if (solidStart) { - isSolid = false; + isSolid = 0; solidStart = false; } @@ -1786,46 +1666,25 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (item.IsEncrypted()) filterStreamSpec->ReleaseInStream(); - if (result == S_FALSE) - { - outStream.Release(); - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kDataError)); - continue; - } - if (result != S_OK) - return result; + + const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + int opRes = (volsInStreamSpec->CrcIsOK && outStreamSpec->GetCRC() == lastItem.FileCRC) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError; + outStream.Release(); - /* - if (refItem.NumItems == 1 && - !item.IsSplitBefore() && !item.IsSplitAfter()) - */ - { - const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; - bool crcOK = outStreamSpec->GetCRC() == lastItem.FileCRC; - outStream.Release(); - RINOK(extractCallback->SetOperationResult(crcOK ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kCRCError)); - } - /* - else + if (result != S_OK) { - bool crcOK = true; - for (unsigned partIndex = 0; partIndex < refItem.NumItems; partIndex++) - { - const CItem &item = _items[refItem.ItemIndex + partIndex]; - if (item.FileCRC != folderInStreamSpec->CRCs[partIndex]) - { - crcOK = false; - break; - } - } - RINOK(extractCallback->SetOperationResult(crcOK ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kCRCError)); + if (result == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else if (result == E_NOTIMPL) + opRes = NExtract::NOperationResult::kUnsupportedMethod; + else + return result; } - */ + RINOK(extractCallback->SetOperationResult(opRes)); } + return S_OK; COM_TRY_END } diff --git a/CPP/7zip/Archive/Rar/RarVol.h b/CPP/7zip/Archive/Rar/RarVol.h new file mode 100644 index 00000000..d0f91de6 --- /dev/null +++ b/CPP/7zip/Archive/Rar/RarVol.h @@ -0,0 +1,129 @@ +// RarVol.h + +#ifndef __ARCHIVE_RAR_VOL_H +#define __ARCHIVE_RAR_VOL_H + +#include "../../../Common/StringConvert.h" + +#include "RarHeader.h" + +namespace NArchive { +namespace NRar { + +inline bool IsDigit(wchar_t c) +{ + return c >= L'0' && c <= L'9'; +} + +class CVolumeName +{ + bool _first; + bool _newStyle; + UString _unchangedPart; + UString _changedPart; + UString _afterPart; +public: + CVolumeName(): _newStyle(true) {}; + + bool InitName(const UString &name, bool newStyle = true) + { + _first = true; + _newStyle = newStyle; + int dotPos = name.ReverseFind_Dot(); + UString basePart = name; + + if (dotPos >= 0) + { + UString ext = name.Ptr(dotPos + 1); + if (ext.IsEqualTo_Ascii_NoCase("rar")) + { + _afterPart = name.Ptr(dotPos); + basePart = name.Left(dotPos); + } + else if (ext.IsEqualTo_Ascii_NoCase("exe")) + { + _afterPart.SetFromAscii(".rar"); + basePart = name.Left(dotPos); + } + else if (!_newStyle) + { + if (ext.IsEqualTo_Ascii_NoCase("000") || + ext.IsEqualTo_Ascii_NoCase("001") || + ext.IsEqualTo_Ascii_NoCase("r00") || + ext.IsEqualTo_Ascii_NoCase("r01")) + { + _afterPart.Empty(); + _first = false; + _changedPart = ext; + _unchangedPart = name.Left(dotPos + 1); + return true; + } + } + } + + if (!_newStyle) + { + _afterPart.Empty(); + _unchangedPart = basePart; + _unchangedPart += L'.'; + _changedPart.SetFromAscii("r00"); + return true; + } + + if (basePart.IsEmpty()) + return false; + unsigned i = basePart.Len(); + + do + if (!IsDigit(basePart[i - 1])) + break; + while (--i); + + _unchangedPart = basePart.Left(i); + _changedPart = basePart.Ptr(i); + return true; + } + + /* + void MakeBeforeFirstName() + { + unsigned len = _changedPart.Len(); + _changedPart.Empty(); + for (unsigned i = 0; i < len; i++) + _changedPart += L'0'; + } + */ + + UString GetNextName() + { + if (_newStyle || !_first) + { + unsigned i = _changedPart.Len(); + for (;;) + { + wchar_t c = _changedPart[--i]; + if (c == L'9') + { + c = L'0'; + _changedPart.ReplaceOneCharAtPos(i, c); + if (i == 0) + { + _changedPart.InsertAtFront(L'1'); + break; + } + continue; + } + c++; + _changedPart.ReplaceOneCharAtPos(i, c); + break; + } + } + + _first = false; + return _unchangedPart + _changedPart + _afterPart; + } +}; + +}} + +#endif |