diff options
Diffstat (limited to 'CPP/7zip/Archive/Tar/TarHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/Tar/TarHandler.cpp | 388 |
1 files changed, 338 insertions, 50 deletions
diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp index 251afdb7..fc7de5ae 100755..100644 --- a/CPP/7zip/Archive/Tar/TarHandler.cpp +++ b/CPP/7zip/Archive/Tar/TarHandler.cpp @@ -2,13 +2,15 @@ #include "StdAfx.h" -#include "Common/ComTry.h" -#include "Common/StringConvert.h" +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/UTFConvert.h" -#include "Windows/PropVariant.h" -#include "Windows/Time.h" +#include "../../../Windows/TimeUtils.h" #include "../../Common/LimitedStreams.h" +#include "../../Common/MethodProps.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" @@ -16,32 +18,34 @@ #include "../Common/ItemNameUtils.h" #include "TarHandler.h" -#include "TarIn.h" using namespace NWindows; namespace NArchive { namespace NTar { -static const char *kUnexpectedEnd = "Unexpected end of archive"; +static const UINT k_DefaultCodePage = CP_OEMCP; // it uses it if UTF8 check in names shows error -static const STATPROPSTG kProps[] = + +static const Byte kProps[] = { - { NULL, kpidPath, VT_BSTR}, - { NULL, kpidIsDir, VT_BOOL}, - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, - { NULL, kpidMTime, VT_FILETIME}, - { NULL, kpidPosixAttrib, VT_UI4}, - { NULL, kpidUser, VT_BSTR}, - { NULL, kpidGroup, VT_BSTR}, - { NULL, kpidLink, VT_BSTR} + kpidPath, + kpidIsDir, + kpidSize, + kpidPackSize, + kpidMTime, + kpidPosixAttrib, + kpidUser, + kpidGroup, + kpidSymLink, + kpidHardLink, + // kpidLinkType }; -static const STATPROPSTG kArcProps[] = +static const Byte kArcProps[] = { - { NULL, kpidPhySize, VT_UI8}, - { NULL, kpidHeadersSize, VT_UI8} + kpidHeadersSize, + kpidCodePage }; IMP_IInArchive_Props @@ -50,11 +54,42 @@ IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; - switch(propID) + switch (propID) { case kpidPhySize: if (_phySizeDefined) prop = _phySize; break; case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break; - case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break; + case kpidErrorFlags: + { + UInt32 flags = 0; + if (!_isArc) + flags |= kpv_ErrorFlags_IsNotArc; + else switch (_error) + { + case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break; + case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break; + } + prop = flags; + break; + } + + case kpidCodePage: + { + const char *name = NULL; + switch (_openCodePage) + { + case CP_OEMCP: name = "OEM"; break; + case CP_UTF8: name = "UTF-8"; break; + } + if (name != NULL) + prop = name; + else + { + char sz[16]; + ConvertUInt32ToString(_openCodePage, sz); + prop = sz; + }; + break; + } } prop.Detach(value); return S_OK; @@ -63,9 +98,16 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item) { item.HeaderPos = _phySize; - RINOK(ReadItem(stream, filled, item, _errorMessage)); - if (filled && item.IsSparse()) - _isSparse = true; + RINOK(ReadItem(stream, filled, item, _error)); + if (filled) + { + /* + if (item.IsSparse()) + _isSparse = true; + */ + if (item.IsPaxExtendedHeader()) + _thereIsPaxExtendedHeader = true; + } _phySize += item.HeaderSize; _headersSize += item.HeaderSize; return S_OK; @@ -80,6 +122,14 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) } _phySizeDefined = true; + + bool utf8_OK = true; + if (!_forceCodePage) + { + if (!utf8_OK) + _curCodePage = k_DefaultCodePage; + } + for (;;) { CItemEx item; @@ -87,12 +137,22 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) RINOK(ReadItem2(stream, filled, item)); if (!filled) break; + + _isArc = true; _items.Add(item); + + if (!_forceCodePage) + { + if (utf8_OK) utf8_OK = CheckUTF8(item.Name); + if (utf8_OK) utf8_OK = CheckUTF8(item.User); + if (utf8_OK) utf8_OK = CheckUTF8(item.Group); + if (utf8_OK) utf8_OK = CheckUTF8(item.LinkName); + } RINOK(stream->Seek(item.GetPackSizeAligned(), STREAM_SEEK_CUR, &_phySize)); if (_phySize > endPos) { - _errorMessage = kUnexpectedEnd; + _error = k_ErrorType_UnexpectedEnd; break; } /* @@ -102,13 +162,13 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) break; } */ - if (callback != NULL) + if (callback) { if (_items.Size() == 1) { RINOK(callback->SetTotal(NULL, &endPos)); } - if (_items.Size() % 100 == 0) + if ((_items.Size() & 0x3FF) == 0) { UInt64 numFiles = _items.Size(); RINOK(callback->SetCompleted(&numFiles, &_phySize)); @@ -116,8 +176,20 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) } } + if (!_forceCodePage) + { + if (!utf8_OK) + _curCodePage = k_DefaultCodePage; + } + _openCodePage = _curCodePage; + if (_items.Size() == 0) { + if (_error != k_ErrorType_OK) + { + _isArc = false; + return S_FALSE; + } CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; if (!callback) return S_FALSE; @@ -129,11 +201,12 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) return S_FALSE; if (prop.vt != VT_BSTR) return S_FALSE; - UString baseName = prop.bstrVal; - baseName = baseName.Right(4); - if (baseName.CompareNoCase(L".tar") != 0) + unsigned len = MyStringLen(prop.bstrVal); + if (len < 4 || MyStringCompareNoCase(prop.bstrVal + len - 4, L".tar") != 0) return S_FALSE; } + + _isArc = true; return S_OK; } @@ -158,13 +231,16 @@ STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) STDMETHODIMP CHandler::Close() { - _errorMessage.Empty(); + _isArc = false; + _error = k_ErrorType_OK; + _phySizeDefined = false; _phySize = 0; _headersSize = 0; _curIndex = 0; _latestIsRead = false; - _isSparse = false; + // _isSparse = false; + _thereIsPaxExtendedHeader = false; _items.Clear(); _seqStream.Release(); _stream.Release(); @@ -181,6 +257,8 @@ CHandler::CHandler() { copyCoderSpec = new NCompress::CCopyCoder(); copyCoder = copyCoderSpec; + _openCodePage = CP_UTF8; + Init(); } HRESULT CHandler::SkipTo(UInt32 index) @@ -194,7 +272,7 @@ HRESULT CHandler::SkipTo(UInt32 index) _phySize += copyCoderSpec->TotalSize; if (copyCoderSpec->TotalSize != packSize) { - _errorMessage = kUnexpectedEnd; + _error = k_ErrorType_UnexpectedEnd; return S_FALSE; } _latestIsRead = false; @@ -215,15 +293,29 @@ HRESULT CHandler::SkipTo(UInt32 index) return S_OK; } -static UString TarStringToUnicode(const AString &s) +void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs) const { - return MultiByteToUnicodeString(s, CP_OEMCP); + UString dest; + if (_curCodePage == CP_UTF8) + { + if (!ConvertUTF8ToUnicode(s, dest)) + { + prop = "[ERROR-NAME]"; + return; + } + } + else + dest = MultiByteToUnicodeString(s, _curCodePage); + if (toOs) + prop = NItemName::GetOSName2(dest); + else + prop = dest; } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN - NWindows::NCOM::CPropVariant prop; + NCOM::CPropVariant prop; const CItemEx *item; if (_stream) @@ -239,9 +331,9 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val } } - switch(propID) + switch (propID) { - case kpidPath: prop = NItemName::GetOSName2(TarStringToUnicode(item->Name)); break; + case kpidPath: TarStringToUnicode(item->Name, prop, true); break; case kpidIsDir: prop = item->IsDir(); break; case kpidSize: prop = item->GetUnpackSize(); break; case kpidPackSize: prop = item->GetPackSizeAligned(); break; @@ -254,9 +346,11 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val } break; case kpidPosixAttrib: prop = item->Mode; break; - case kpidUser: prop = TarStringToUnicode(item->User); break; - case kpidGroup: prop = TarStringToUnicode(item->Group); break; - case kpidLink: prop = TarStringToUnicode(item->LinkName); break; + case kpidUser: TarStringToUnicode(item->User, prop); break; + case kpidGroup: TarStringToUnicode(item->Group, prop); break; + case kpidSymLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kSymLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break; + case kpidHardLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kHardLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break; + // case kpidLinkType: prop = (int)item->LinkFlag; break; } prop.Detach(value); return S_OK; @@ -272,7 +366,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (!seqMode) stream = _stream; - bool allFilesMode = (numItems == (UInt32)-1); + bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _items.Size(); if (_stream && numItems == 0) @@ -333,7 +427,18 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (!testMode && !realOutStream) { if (!seqMode) + { + /* + // probably we must show extracting info it callback handler instead + if (item->IsHardLink() || + item->IsSymLink()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + */ continue; + } skipMode = true; askMode = NExtract::NAskMode::kSkip; } @@ -344,13 +449,20 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, outStreamSpec->Init(skipMode ? 0 : unpackSize, true); Int32 opRes = NExtract::NOperationResult::kOK; - if (item->IsSparse()) - opRes = NExtract::NOperationResult::kUnSupportedMethod; + CMyComPtr<ISequentialInStream> inStream2; + if (!item->IsSparse()) + inStream2 = inStream; else { - if (item->IsLink()) + GetStream(index, &inStream2); + if (!inStream2) + return E_FAIL; + } + + { + if (item->IsSymLink()) { - RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Length())); + RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len())); } else { @@ -359,7 +471,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL)); } streamSpec->Init(item->GetPackSizeAligned()); - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress)); } if (outStreamSpec->GetRem() != 0) opRes = NExtract::NOperationResult::kDataError; @@ -376,22 +488,198 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, COM_TRY_END } +class CSparseStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _phyPos; + UInt64 _virtPos; + bool _needStartSeek; + +public: + CHandler *Handler; + CMyComPtr<IUnknown> HandlerRef; + unsigned ItemIndex; + CRecordVector<UInt64> PhyOffsets; + + MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + + void Init() + { + _virtPos = 0; + _phyPos = 0; + _needStartSeek = true; + } +}; + + +STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + const CItemEx &item = Handler->_items[ItemIndex]; + if (_virtPos >= item.Size) + return S_OK; + { + UInt64 rem = item.Size - _virtPos; + if (size > rem) + size = (UInt32)rem; + } + + HRESULT res = S_OK; + + if (item.SparseBlocks.IsEmpty()) + memset(data, 0, size); + else + { + unsigned left = 0, right = item.SparseBlocks.Size(); + for (;;) + { + unsigned mid = (left + right) / 2; + if (mid == left) + break; + if (_virtPos < item.SparseBlocks[mid].Offset) + right = mid; + else + left = mid; + } + + const CSparseBlock &sb = item.SparseBlocks[left]; + UInt64 relat = _virtPos - sb.Offset; + + if (_virtPos >= sb.Offset && relat < sb.Size) + { + UInt64 rem = sb.Size - relat; + if (size > rem) + size = (UInt32)rem; + UInt64 phyPos = PhyOffsets[left] + relat; + if (_needStartSeek || _phyPos != phyPos) + { + RINOK(Handler->_stream->Seek(item.GetDataPosition() + phyPos, STREAM_SEEK_SET, NULL)); + _needStartSeek = false; + _phyPos = phyPos; + } + res = Handler->_stream->Read(data, size, &size); + _phyPos += size; + } + else + { + UInt64 next = item.Size; + if (_virtPos < sb.Offset) + next = sb.Offset; + else if (left + 1 < item.SparseBlocks.Size()) + next = item.SparseBlocks[left + 1].Offset; + UInt64 rem = next - _virtPos; + if (size > rem) + size = (UInt32)rem; + memset(data, 0, size); + } + } + + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; +} + +STDMETHODIMP CSparseStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch (seekOrigin) + { + case STREAM_SEEK_SET: break; + case STREAM_SEEK_CUR: offset += _virtPos; break; + case STREAM_SEEK_END: offset += Handler->_items[ItemIndex].Size; break; + default: return STG_E_INVALIDFUNCTION; + } + if (offset < 0) + return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; + _virtPos = offset; + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN + const CItemEx &item = _items[index]; + if (item.IsSparse()) - return E_NOTIMPL; - if (item.IsLink()) + { + CSparseStream *streamSpec = new CSparseStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + streamSpec->Init(); + streamSpec->Handler = this; + streamSpec->HandlerRef = (IInArchive *)this; + streamSpec->ItemIndex = index; + streamSpec->PhyOffsets.Reserve(item.SparseBlocks.Size()); + UInt64 offs = 0; + FOR_VECTOR(i, item.SparseBlocks) + { + const CSparseBlock &sb = item.SparseBlocks[i]; + streamSpec->PhyOffsets.AddInReserved(offs); + offs += sb.Size; + } + *stream = streamTemp.Detach(); + return S_OK; + } + + if (item.IsSymLink()) { CBufInStream *streamSpec = new CBufInStream; CMyComPtr<IInStream> streamTemp = streamSpec; - streamSpec->Init((const Byte *)(const char *)item.LinkName, item.LinkName.Length(), (IInArchive *)this); + streamSpec->Init((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this); *stream = streamTemp.Detach(); return S_OK; } + return CreateLimitedInStream(_stream, item.GetDataPosition(), item.PackSize, stream); + COM_TRY_END } +void CHandler::Init() +{ + _forceCodePage = false; + // _codePage = CP_OEMCP; + _curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP; +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) +{ + Init(); + + for (UInt32 i = 0; i < numProps; i++) + { + UString name = names[i]; + name.MakeLower_Ascii(); + if (name.IsEmpty()) + return E_INVALIDARG; + + const PROPVARIANT &prop = values[i]; + + if (name[0] == L'x') + { + // some clients write 'x' property. So we support it + UInt32 level = 0; + RINOK(ParsePropToUInt32(name.Ptr(1), prop, level)); + } + else if (name.IsEqualTo("cp")) + { + UInt32 cp = CP_OEMCP; + RINOK(ParsePropToUInt32(L"", prop, cp)); + _forceCodePage = true; + _curCodePage = _specifiedCodePage = cp; + } + else + return E_INVALIDARG; + } + return S_OK; +} + }} |