This commit is contained in:
Igor Pavlov
2010-11-02 00:00:00 +00:00
committed by Kornel Lesiński
parent 2eb60a0598
commit c65230d858
101 changed files with 4557 additions and 541 deletions

View File

@@ -10,6 +10,8 @@
#include "../../Common/LimitedStreams.h"
#include "../../Common/ProgressUtils.h"
#include "../../Common/StreamObjects.h"
#include "../../Common/StreamUtils.h"
#include "../Common/ItemNameUtils.h"
@@ -21,7 +23,9 @@ using namespace NWindows;
namespace NArchive {
namespace NTar {
static STATPROPSTG kProps[] =
static const char *kUnexpectedEnd = "Unexpected end of archive";
static const STATPROPSTG kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidIsDir, VT_BOOL},
@@ -34,8 +38,14 @@ static STATPROPSTG kProps[] =
{ NULL, kpidLink, VT_BSTR}
};
static const STATPROPSTG kArcProps[] =
{
{ NULL, kpidPhySize, VT_UI8},
{ NULL, kpidHeadersSize, VT_UI8}
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps_NO_Table
IMP_IInArchive_ArcProps
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
@@ -43,11 +53,22 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
switch(propID)
{
case kpidPhySize: if (_phySizeDefined) prop = _phySize; break;
case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break;
case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
}
prop.Detach(value);
return S_OK;
}
HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item)
{
item.HeaderPos = _phySize;
RINOK(ReadItem(stream, filled, item, _errorMessage));
_phySize += item.HeaderSize;
_headersSize += item.HeaderSize;
return S_OK;
}
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
{
UInt64 endPos = 0;
@@ -56,26 +77,29 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
}
_isGood = true;
UInt64 pos = 0;
_phySizeDefined = true;
for (;;)
{
CItemEx item;
bool filled;
item.HeaderPosition = pos;
RINOK(ReadItem(stream, filled, item));
RINOK(ReadItem2(stream, filled, item));
if (!filled)
break;
_items.Add(item);
RINOK(stream->Seek(item.GetPackSize(), STREAM_SEEK_CUR, &pos));
if (pos > endPos)
return S_FALSE;
if (pos == endPos)
RINOK(stream->Seek(item.GetPackSize(), STREAM_SEEK_CUR, &_phySize));
if (_phySize > endPos)
{
_isGood = false;
_errorMessage = kUnexpectedEnd;
break;
}
/*
if (_phySize == endPos)
{
_errorMessage = "There are no trailing zero-filled records";
break;
}
*/
if (callback != NULL)
{
if (_items.Size() == 1)
@@ -85,7 +109,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
if (_items.Size() % 100 == 0)
{
UInt64 numFiles = _items.Size();
RINOK(callback->SetCompleted(&numFiles, &pos));
RINOK(callback->SetCompleted(&numFiles, &_phySize));
}
}
}
@@ -132,7 +156,10 @@ STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
STDMETHODIMP CHandler::Close()
{
_errorMessage.Empty();
_phySizeDefined = false;
_phySize = 0;
_headersSize = 0;
_curIndex = 0;
_latestIsRead = false;
_items.Clear();
@@ -161,16 +188,24 @@ HRESULT CHandler::SkipTo(UInt32 index)
{
UInt64 packSize = _latestItem.GetPackSize();
RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
_phySize += copyCoderSpec->TotalSize;
if (copyCoderSpec->TotalSize != packSize)
{
_errorMessage = kUnexpectedEnd;
return S_FALSE;
}
_latestIsRead = false;
_curIndex++;
}
else
{
bool filled;
// item.HeaderPosition = pos;
RINOK(ReadItem(_seqStream, filled, _latestItem));
RINOK(ReadItem2(_seqStream, filled, _latestItem));
if (!filled)
{
_phySizeDefined = true;
return E_INVALIDARG;
}
_latestIsRead = true;
}
}
@@ -203,10 +238,10 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
switch(propID)
{
case kpidPath: prop = NItemName::GetOSName2(TarStringToUnicode(item->Name)); break;
case kpidIsDir: prop = item->IsDir(); break;
case kpidSize: prop = item->Size; break;
case kpidPackSize: prop = item->GetPackSize(); break;
case kpidPath: prop = NItemName::GetOSName2(TarStringToUnicode(item->Name)); break;
case kpidIsDir: prop = item->IsDir(); break;
case kpidSize: prop = item->GetUnpackSize(); break;
case kpidPackSize: prop = item->GetPackSize(); break;
case kpidMTime:
if (item->MTime != 0)
{
@@ -216,9 +251,9 @@ 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: prop = TarStringToUnicode(item->User); break;
case kpidGroup: prop = TarStringToUnicode(item->Group); break;
case kpidLink: prop = TarStringToUnicode(item->LinkName); break;
}
prop.Detach(value);
return S_OK;
@@ -242,7 +277,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
UInt64 totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
totalSize += _items[allFilesMode ? i : indices[i]].Size;
totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize();
extractCallback->SetTotal(totalSize);
UInt64 totalPackSize;
@@ -282,7 +317,8 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
item = &_items[index];
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
totalSize += item->Size;
UInt64 unpackSize = item->GetUnpackSize();
totalSize += unpackSize;
totalPackSize += item->GetPackSize();
if (item->IsDir())
{
@@ -302,14 +338,21 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
outStreamSpec->SetStream(realOutStream);
realOutStream.Release();
outStreamSpec->Init(skipMode ? 0 : item->Size, true);
outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
if (!seqMode)
if (item->IsLink())
{
RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL));
RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Length()));
}
else
{
if (!seqMode)
{
RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL));
}
streamSpec->Init(item->GetPackSize());
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
}
streamSpec->Init(item->GetPackSize());
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
if (seqMode)
{
_latestIsRead = false;
@@ -328,6 +371,14 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
{
COM_TRY_BEGIN
const CItemEx &item = _items[index];
if (item.IsLink())
{
CBufInStream *streamSpec = new CBufInStream;
CMyComPtr<IInStream> streamTemp = streamSpec;
streamSpec->Init((const Byte *)(const char *)item.LinkName, item.LinkName.Length(), (IInArchive *)this);
*stream = streamTemp.Detach();
return S_OK;
}
return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream);
COM_TRY_END
}

View File

@@ -23,18 +23,20 @@ class CHandler:
CObjectVector<CItemEx> _items;
CMyComPtr<IInStream> _stream;
CMyComPtr<ISequentialInStream> _seqStream;
bool _isGood;
UInt32 _curIndex;
bool _latestIsRead;
CItemEx _latestItem;
UInt64 _phySize;
UInt64 _headersSize;
bool _phySizeDefined;
AString _errorMessage;
NCompress::CCopyCoder *copyCoderSpec;
CMyComPtr<ICompressCoder> copyCoder;
HRESULT ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo);
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
HRESULT SkipTo(UInt32 index);

View File

@@ -37,7 +37,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt
IArchiveUpdateCallback *callback)
{
COM_TRY_BEGIN
if ((_stream && !_isGood) || _seqStream)
if ((_stream && !_errorMessage.IsEmpty()) || _seqStream)
return E_NOTIMPL;
CObjectVector<CUpdateItem> updateItems;
for (UInt32 i = 0; i < numItems; i++)

View File

@@ -63,29 +63,40 @@ static void ReadString(const char *s, int size, AString &result)
result = temp;
}
static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, size_t &processedSize)
static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, AString &error)
{
item.LongLinkSize = 0;
char buf[NFileHeader::kRecordSize];
char *p = buf;
error.Empty();
filled = false;
bool thereAreEmptyRecords = false;
for (;;)
{
processedSize = NFileHeader::kRecordSize;
size_t processedSize = NFileHeader::kRecordSize;
RINOK(ReadStream(stream, buf, &processedSize));
if (processedSize == 0)
{
if (!thereAreEmptyRecords )
error = "There are no trailing zero-filled records";
return S_OK;
}
if (processedSize != NFileHeader::kRecordSize)
return S_FALSE;
{
error = "There is no correct record at the end of archive";
return S_OK;
}
item.HeaderSize += NFileHeader::kRecordSize;
if (!IsRecordLast(buf))
break;
thereAreEmptyRecords = true;
}
if (thereAreEmptyRecords)
return S_FALSE;
{
error = "There are data after end of archive";
return S_OK;
}
ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize;
@@ -143,59 +154,54 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
return S_OK;
}
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item)
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, AString &error)
{
size_t processedSize;
RINOK(GetNextItemReal(stream, filled, item, processedSize));
if (!filled)
return S_OK;
// GNUtar extension
if (item.LinkFlag == 'L' || // NEXT file has a long name
item.LinkFlag == 'K') // NEXT file has a long linkname
item.HeaderSize = 0;
bool flagL = false;
bool flagK = false;
AString nameL;
AString nameK;
for (;;)
{
if (item.Name.Compare(NFileHeader::kLongLink) != 0)
if (item.Name.Compare(NFileHeader::kLongLink2) != 0)
RINOK(GetNextItemReal(stream, filled, item, error));
if (!filled)
return S_OK;
if (item.LinkFlag == 'L' || // NEXT file has a long name
item.LinkFlag == 'K') // NEXT file has a long linkname
{
AString *name;
if (item.LinkFlag == 'L')
{ if (flagL) return S_FALSE; flagL = true; name = &nameL; }
else
{ if (flagK) return S_FALSE; flagK = true; name = &nameK; }
if (item.Name.Compare(NFileHeader::kLongLink) != 0 &&
item.Name.Compare(NFileHeader::kLongLink2) != 0)
return S_FALSE;
AString fullName;
if (item.Size > (1 << 15))
if (item.Size > (1 << 14))
return S_FALSE;
int packSize = (int)item.GetPackSize();
char *buf = name->GetBuffer(packSize);
RINOK(ReadStream_FALSE(stream, buf, packSize));
item.HeaderSize += packSize;
buf[(size_t)item.Size] = '\0';
name->ReleaseBuffer();
continue;
}
if (item.LinkFlag == 'g' || item.LinkFlag == 'x' || item.LinkFlag == 'X')
{
// pax Extended Header
}
else if (item.LinkFlag == NFileHeader::NLinkFlag::kDumpDir)
{
// GNU Extensions to the Archive Format
}
else if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
return S_FALSE;
int packSize = (int)item.GetPackSize();
char *buffer = fullName.GetBuffer(packSize + 1);
RINOK(ReadStream_FALSE(stream, buffer, packSize));
processedSize += packSize;
buffer[item.Size] = '\0';
fullName.ReleaseBuffer();
UInt64 headerPosition = item.HeaderPosition;
if (item.LinkFlag == 'L')
{
size_t processedSize2;
RINOK(GetNextItemReal(stream, filled, item, processedSize2));
item.LongLinkSize = (unsigned)processedSize;
}
else
{
item.LongLinkSize = (unsigned)processedSize - NFileHeader::kRecordSize;
item.Size = 0;
}
item.Name = fullName;
item.HeaderPosition = headerPosition;
}
else if (item.LinkFlag == 'g' || item.LinkFlag == 'x' || item.LinkFlag == 'X')
{
// pax Extended Header
if (flagL) item.Name = nameL;
if (flagK) item.LinkName = nameK;
return S_OK;
}
else if (item.LinkFlag == NFileHeader::NLinkFlag::kDumpDir)
{
// GNU Extensions to the Archive Format
return S_OK;
}
else if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
return S_FALSE;
return S_OK;
}
}}

View File

@@ -1,9 +1,8 @@
// Archive/TarIn.h
// TarIn.h
#ifndef __ARCHIVE_TAR_IN_H
#define __ARCHIVE_TAR_IN_H
#include "Common/MyCom.h"
#include "../../IStream.h"
#include "TarItem.h"
@@ -11,7 +10,7 @@
namespace NArchive {
namespace NTar {
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo);
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, AString &error);
}}

View File

@@ -31,6 +31,9 @@ struct CItem
bool DeviceMajorDefined;
bool DeviceMinorDefined;
bool IsLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymbolicLink && (Size == 0); }
UInt64 GetUnpackSize() const { return IsLink() ? LinkName.Length() : Size; }
bool IsDir() const
{
switch(LinkFlag)
@@ -58,10 +61,10 @@ struct CItem
struct CItemEx: public CItem
{
UInt64 HeaderPosition;
unsigned LongLinkSize;
UInt64 GetDataPosition() const { return HeaderPosition + LongLinkSize + NFileHeader::kRecordSize; }
UInt64 GetFullSize() const { return LongLinkSize + NFileHeader::kRecordSize + Size; }
UInt64 HeaderPos;
unsigned HeaderSize;
UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
UInt64 GetFullSize() const { return HeaderSize + Size; }
};
}}

View File

@@ -111,25 +111,25 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
}
else
{
const CItemEx &existItemInfo = inputItems[ui.IndexInArchive];
const CItemEx &existItem = inputItems[ui.IndexInArchive];
UInt64 size;
if (ui.NewProps)
{
RINOK(outArchive.WriteHeader(item));
RINOK(inStream->Seek(existItemInfo.GetDataPosition(), STREAM_SEEK_SET, NULL));
size = existItemInfo.Size;
RINOK(inStream->Seek(existItem.GetDataPosition(), STREAM_SEEK_SET, NULL));
size = existItem.Size;
}
else
{
RINOK(inStream->Seek(existItemInfo.HeaderPosition, STREAM_SEEK_SET, NULL));
size = existItemInfo.GetFullSize();
RINOK(inStream->Seek(existItem.HeaderPos, STREAM_SEEK_SET, NULL));
size = existItem.GetFullSize();
}
streamSpec->Init(size);
RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
if (copyCoderSpec->TotalSize != size)
return E_FAIL;
RINOK(outArchive.FillDataResidual(existItemInfo.Size));
RINOK(outArchive.FillDataResidual(existItem.Size));
complexity += size;
}
}