2747 lines
63 KiB
C++
2747 lines
63 KiB
C++
// 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 "../HandlerCont.h"
|
|
|
|
#include "RarVol.h"
|
|
#include "Rar5Handler.h"
|
|
|
|
using namespace NWindows;
|
|
|
|
#define Get32(p) GetUi32(p)
|
|
|
|
namespace NArchive {
|
|
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 kpidError:
|
|
{
|
|
if (/* &_missingVol || */ !_missingVolName.IsEmpty())
|
|
{
|
|
UString s;
|
|
s.SetFromAscii("Missing volume : ");
|
|
s += _missingVolName;
|
|
prop = s;
|
|
}
|
|
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;
|
|
|
|
bool nextVol_is_Required = false;
|
|
|
|
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;
|
|
}
|
|
|
|
const UString volName = seqName.GetNextName();
|
|
|
|
HRESULT result = openVolumeCallback->GetStream(volName, &inStream);
|
|
|
|
if (result != S_OK && result != S_FALSE)
|
|
return result;
|
|
|
|
if (!inStream || result != S_OK)
|
|
{
|
|
if (nextVol_is_Required)
|
|
_missingVolName = volName;
|
|
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(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 &ref2 = _refs[prevSplitFile];
|
|
CItem &prevItem = _items[ref2.Last];
|
|
if (item.IsNextForItem(prevItem))
|
|
{
|
|
ref2.Last = _items.Size();
|
|
prevItem.NextItem = ref2.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;
|
|
|
|
nextVol_is_Required = false;
|
|
|
|
if (!arcInfo.IsVolume())
|
|
break;
|
|
|
|
if (arcInfo.EndOfArchive_was_Read)
|
|
{
|
|
if (!arcInfo.AreMoreVolumes())
|
|
break;
|
|
nextVol_is_Required = true;
|
|
}
|
|
}
|
|
|
|
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
|
|
_missingVolName.Empty();
|
|
_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 isThereUndefinedSize = false;
|
|
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];
|
|
const CItem &lastItem = _items[ref.Last];
|
|
|
|
extractStatuses[index] |= kStatus_Extract;
|
|
|
|
if (!lastItem.Is_UnknownSize())
|
|
total += lastItem.Size;
|
|
else
|
|
isThereUndefinedSize = true;
|
|
|
|
if (ref.Link >= 0)
|
|
{
|
|
if (!testMode)
|
|
{
|
|
if ((unsigned)ref.Link < index)
|
|
{
|
|
const CRefItem &linkRef = _refs[(unsigned)ref.Link];
|
|
const CItem &linkItem = _items[linkRef.Item];
|
|
if (linkItem.IsSolid() && linkItem.Size <= k_CopyLinkFile_MaxSize)
|
|
{
|
|
if (extractStatuses[(unsigned)ref.Link] == 0)
|
|
{
|
|
const CItem &lastLinkItem = _items[linkRef.Last];
|
|
if (!lastLinkItem.Is_UnknownSize())
|
|
total += lastLinkItem.Size;
|
|
else
|
|
isThereUndefinedSize = true;
|
|
}
|
|
extractStatuses[(unsigned)ref.Link] |= kStatus_Link;
|
|
thereAreLinks = true;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (item.IsService())
|
|
continue;
|
|
|
|
if (item.IsSolid())
|
|
{
|
|
unsigned j = index;
|
|
|
|
while (j > solidLimit)
|
|
{
|
|
j--;
|
|
const CRefItem &ref2 = _refs[j];
|
|
const CItem &item2 = _items[ref2.Item];
|
|
if (!item2.IsService())
|
|
{
|
|
if (extractStatuses[j] == 0)
|
|
{
|
|
const CItem &lastItem2 = _items[ref2.Last];
|
|
if (!lastItem2.Is_UnknownSize())
|
|
total += lastItem2.Size;
|
|
else
|
|
isThereUndefinedSize = true;
|
|
}
|
|
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 CRefItem &ref2 = _refs[j];
|
|
const CItem &item2 = _items[ref2.Item];
|
|
if (!item2.IsService())
|
|
{
|
|
if (extractStatuses[j] != 0)
|
|
break;
|
|
extractStatuses[j] = kStatus_Skip;
|
|
{
|
|
const CItem &lastItem2 = _items[ref2.Last];
|
|
if (!lastItem2.Is_UnknownSize())
|
|
total += lastItem2.Size;
|
|
else
|
|
isThereUndefinedSize = true;
|
|
}
|
|
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++;
|
|
}
|
|
}
|
|
|
|
if (total != 0 || !isThereUndefinedSize)
|
|
{
|
|
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];
|
|
const CItem &lastItem = _items[ref->Last];
|
|
|
|
curUnpackSize = 0;
|
|
if (!lastItem.Is_UnknownSize())
|
|
curUnpackSize = lastItem.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];
|
|
const CItem &lastItem2 = _items[ref2.Last];
|
|
if (!item2.IsSolid())
|
|
{
|
|
item = &item2;
|
|
ref = &ref2;
|
|
if (!lastItem2.Is_UnknownSize())
|
|
curUnpackSize = lastItem2.Size;
|
|
else
|
|
curUnpackSize = 0;
|
|
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)
|