Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/Tar/TarHandler.cpp')
-rw-r--r--[-rwxr-xr-x]CPP/7zip/Archive/Tar/TarHandler.cpp388
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;
+}
+
}}