Initialer Commit

This commit is contained in:
Tino Reichardt
2016-06-25 21:15:50 +02:00
commit c3967fe27a
1199 changed files with 290375 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
// StdAfx.h
#ifndef __STDAFX_H
#define __STDAFX_H
#include "../../../Common/Common.h"
#endif

View File

@@ -0,0 +1,677 @@
// TarHandler.cpp
#include "StdAfx.h"
#include "../../../Common/ComTry.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/UTFConvert.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"
#include "../Common/ItemNameUtils.h"
#include "TarHandler.h"
using namespace NWindows;
namespace NArchive {
namespace NTar {
static const UINT k_DefaultCodePage = CP_OEMCP; // it uses it if UTF8 check in names shows error
static const Byte kProps[] =
{
kpidPath,
kpidIsDir,
kpidSize,
kpidPackSize,
kpidMTime,
kpidPosixAttrib,
kpidUser,
kpidGroup,
kpidSymLink,
kpidHardLink,
// kpidLinkType
};
static const Byte kArcProps[] =
{
kpidHeadersSize,
kpidCodePage
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
NCOM::CPropVariant prop;
switch (propID)
{
case kpidPhySize: if (_phySizeDefined) prop = _phySize; break;
case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; 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;
}
HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item)
{
item.HeaderPos = _phySize;
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;
}
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
{
UInt64 endPos = 0;
{
RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
}
_phySizeDefined = true;
bool utf8_OK = true;
if (!_forceCodePage)
{
if (!utf8_OK)
_curCodePage = k_DefaultCodePage;
}
for (;;)
{
CItemEx item;
bool filled;
RINOK(ReadItem2(stream, filled, item));
if (!filled)
break;
_isArc = true;
_items.Add(item);
if (!_forceCodePage)
{
if (utf8_OK) utf8_OK = CheckUTF8(item.Name, item.NameCouldBeReduced);
if (utf8_OK) utf8_OK = CheckUTF8(item.LinkName, item.LinkNameCouldBeReduced);
if (utf8_OK) utf8_OK = CheckUTF8(item.User);
if (utf8_OK) utf8_OK = CheckUTF8(item.Group);
}
RINOK(stream->Seek(item.GetPackSizeAligned(), STREAM_SEEK_CUR, &_phySize));
if (_phySize > endPos)
{
_error = k_ErrorType_UnexpectedEnd;
break;
}
/*
if (_phySize == endPos)
{
_errorMessage = "There are no trailing zero-filled records";
break;
}
*/
if (callback)
{
if (_items.Size() == 1)
{
RINOK(callback->SetTotal(NULL, &endPos));
}
if ((_items.Size() & 0x3FF) == 0)
{
UInt64 numFiles = _items.Size();
RINOK(callback->SetCompleted(&numFiles, &_phySize));
}
}
}
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;
callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
if (!openVolumeCallback)
return S_FALSE;
NCOM::CPropVariant prop;
if (openVolumeCallback->GetProperty(kpidName, &prop) != S_OK)
return S_FALSE;
if (prop.vt != VT_BSTR)
return S_FALSE;
unsigned len = MyStringLen(prop.bstrVal);
if (len < 4 || MyStringCompareNoCase(prop.bstrVal + len - 4, L".tar") != 0)
return S_FALSE;
}
_isArc = true;
return S_OK;
}
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback)
{
COM_TRY_BEGIN
{
Close();
RINOK(Open2(stream, openArchiveCallback));
_stream = stream;
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
{
Close();
_seqStream = stream;
_isArc = true;
return S_OK;
}
STDMETHODIMP CHandler::Close()
{
_isArc = false;
_error = k_ErrorType_OK;
_phySizeDefined = false;
_phySize = 0;
_headersSize = 0;
_curIndex = 0;
_latestIsRead = false;
// _isSparse = false;
_thereIsPaxExtendedHeader = false;
_items.Clear();
_seqStream.Release();
_stream.Release();
return S_OK;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = (_stream ? _items.Size() : (UInt32)(Int32)-1);
return S_OK;
}
CHandler::CHandler()
{
copyCoderSpec = new NCompress::CCopyCoder();
copyCoder = copyCoderSpec;
_openCodePage = CP_UTF8;
Init();
}
HRESULT CHandler::SkipTo(UInt32 index)
{
while (_curIndex < index || !_latestIsRead)
{
if (_latestIsRead)
{
UInt64 packSize = _latestItem.GetPackSizeAligned();
RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
_phySize += copyCoderSpec->TotalSize;
if (copyCoderSpec->TotalSize != packSize)
{
_error = k_ErrorType_UnexpectedEnd;
return S_FALSE;
}
_latestIsRead = false;
_curIndex++;
}
else
{
bool filled;
RINOK(ReadItem2(_seqStream, filled, _latestItem));
if (!filled)
{
_phySizeDefined = true;
return E_INVALIDARG;
}
_latestIsRead = true;
}
}
return S_OK;
}
void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs) const
{
UString dest;
if (_curCodePage == CP_UTF8)
ConvertUTF8ToUnicode(s, dest);
else
MultiByteToUnicodeString2(dest, s, _curCodePage);
if (toOs)
NItemName::ConvertToOSName2(dest);
prop = dest;
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
const CItemEx *item;
if (_stream)
item = &_items[index];
else
{
if (index < _curIndex)
return E_INVALIDARG;
else
{
RINOK(SkipTo(index));
item = &_latestItem;
}
}
switch (propID)
{
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;
case kpidMTime:
if (item->MTime != 0)
{
FILETIME ft;
if (NTime::UnixTime64ToFileTime(item->MTime, ft))
prop = ft;
}
break;
case kpidPosixAttrib: prop = item->Mode; 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;
COM_TRY_END
}
HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
ISequentialInStream *stream = _seqStream;
bool seqMode = (_stream == NULL);
if (!seqMode)
stream = _stream;
bool allFilesMode = (numItems == (UInt32)(Int32)-1);
if (allFilesMode)
numItems = _items.Size();
if (_stream && numItems == 0)
return S_OK;
UInt64 totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize();
extractCallback->SetTotal(totalSize);
UInt64 totalPackSize;
totalSize = totalPackSize = 0;
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(extractCallback, false);
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
CMyComPtr<ISequentialInStream> inStream(streamSpec);
streamSpec->SetStream(stream);
CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
for (i = 0; i < numItems || seqMode; i++)
{
lps->InSize = totalPackSize;
lps->OutSize = totalSize;
RINOK(lps->SetCur());
CMyComPtr<ISequentialOutStream> realOutStream;
Int32 askMode = testMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract;
Int32 index = allFilesMode ? i : indices[i];
const CItemEx *item;
if (seqMode)
{
HRESULT res = SkipTo(index);
if (res == E_INVALIDARG)
break;
RINOK(res);
item = &_latestItem;
}
else
item = &_items[index];
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
UInt64 unpackSize = item->GetUnpackSize();
totalSize += unpackSize;
totalPackSize += item->GetPackSizeAligned();
if (item->IsDir())
{
RINOK(extractCallback->PrepareOperation(askMode));
RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
continue;
}
bool skipMode = false;
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;
}
RINOK(extractCallback->PrepareOperation(askMode));
outStreamSpec->SetStream(realOutStream);
realOutStream.Release();
outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
Int32 opRes = NExtract::NOperationResult::kOK;
CMyComPtr<ISequentialInStream> inStream2;
if (!item->IsSparse())
inStream2 = inStream;
else
{
GetStream(index, &inStream2);
if (!inStream2)
return E_FAIL;
}
{
if (item->IsSymLink())
{
RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len()));
}
else
{
if (!seqMode)
{
RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL));
}
streamSpec->Init(item->GetPackSizeAligned());
RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress));
}
if (outStreamSpec->GetRem() != 0)
opRes = NExtract::NOperationResult::kDataError;
}
if (seqMode)
{
_latestIsRead = false;
_curIndex++;
}
outStreamSpec->ReleaseStream();
RINOK(extractCallback->SetOperationResult(opRes));
}
return S_OK;
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())
{
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())
{
Create_BufInStream_WithReference((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this, stream);
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;
_thereIsPaxExtendedHeader = false;
}
STDMETHODIMP CHandler::SetProperties(const wchar_t * const *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;
}
}}

View File

@@ -0,0 +1,78 @@
// TarHandler.h
#ifndef __TAR_HANDLER_H
#define __TAR_HANDLER_H
#include "../../../Common/MyCom.h"
#include "../../../Windows/PropVariant.h"
#include "../../Compress/CopyCoder.h"
#include "../IArchive.h"
#include "TarIn.h"
namespace NArchive {
namespace NTar {
class CHandler:
public IInArchive,
public IArchiveOpenSeq,
public IInArchiveGetStream,
public ISetProperties,
public IOutArchive,
public CMyUnknownImp
{
public:
CObjectVector<CItemEx> _items;
CMyComPtr<IInStream> _stream;
CMyComPtr<ISequentialInStream> _seqStream;
private:
UInt32 _curIndex;
bool _latestIsRead;
CItemEx _latestItem;
UInt64 _phySize;
UInt64 _headersSize;
bool _phySizeDefined;
EErrorType _error;
bool _isArc;
// bool _isSparse;
bool _thereIsPaxExtendedHeader;
bool _forceCodePage;
UInt32 _specifiedCodePage;
UInt32 _curCodePage;
UInt32 _openCodePage;
NCompress::CCopyCoder *copyCoderSpec;
CMyComPtr<ICompressCoder> copyCoder;
HRESULT ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo);
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
HRESULT SkipTo(UInt32 index);
void TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs = false) const;
public:
MY_UNKNOWN_IMP5(
IInArchive,
IArchiveOpenSeq,
IInArchiveGetStream,
ISetProperties,
IOutArchive
)
INTERFACE_IInArchive(;)
INTERFACE_IOutArchive(;)
STDMETHOD(OpenSeq)(ISequentialInStream *stream);
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
void Init();
CHandler();
};
}}
#endif

View File

@@ -0,0 +1,174 @@
// TarHandlerOut.cpp
#include "StdAfx.h"
#include "../../../Common/ComTry.h"
#include "../../../Common/Defs.h"
#include "../../../Common/MyLinux.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/UTFConvert.h"
#include "../../../Windows/PropVariant.h"
#include "../../../Windows/TimeUtils.h"
#include "TarHandler.h"
#include "TarUpdate.h"
using namespace NWindows;
namespace NArchive {
namespace NTar {
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
{
*type = NFileTimeType::kUnix;
return S_OK;
}
HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId,
AString &res, UINT codePage, bool convertSlash = false)
{
NCOM::CPropVariant prop;
RINOK(callback->GetProperty(index, propId, &prop));
if (prop.vt == VT_BSTR)
{
UString s = prop.bstrVal;
if (convertSlash)
s = NItemName::MakeLegalName(s);
if (codePage == CP_UTF8)
{
ConvertUnicodeToUTF8(s, res);
// if (!ConvertUnicodeToUTF8(s, res)) // return E_INVALIDARG;
}
else
UnicodeStringToMultiByte2(res, s, codePage);
}
else if (prop.vt != VT_EMPTY)
return E_INVALIDARG;
return S_OK;
}
// sort old files with original order.
static int CompareUpdateItems(void *const *p1, void *const *p2, void *)
{
const CUpdateItem &u1 = *(*((const CUpdateItem **)p1));
const CUpdateItem &u2 = *(*((const CUpdateItem **)p2));
if (!u1.NewProps)
{
if (u2.NewProps)
return -1;
return MyCompare(u1.IndexInArc, u2.IndexInArc);
}
if (!u2.NewProps)
return 1;
return MyCompare(u1.IndexInClient, u2.IndexInClient);
}
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
IArchiveUpdateCallback *callback)
{
COM_TRY_BEGIN
if ((_stream && (_error != k_ErrorType_OK /* || _isSparse */)) || _seqStream)
return E_NOTIMPL;
CObjectVector<CUpdateItem> updateItems;
UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage);
for (UInt32 i = 0; i < numItems; i++)
{
CUpdateItem ui;
Int32 newData;
Int32 newProps;
UInt32 indexInArc;
if (!callback)
return E_FAIL;
RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArc));
ui.NewProps = IntToBool(newProps);
ui.NewData = IntToBool(newData);
ui.IndexInArc = indexInArc;
ui.IndexInClient = i;
if (IntToBool(newProps))
{
{
NCOM::CPropVariant prop;
RINOK(callback->GetProperty(i, kpidIsDir, &prop));
if (prop.vt == VT_EMPTY)
ui.IsDir = false;
else if (prop.vt != VT_BOOL)
return E_INVALIDARG;
else
ui.IsDir = (prop.boolVal != VARIANT_FALSE);
}
{
NCOM::CPropVariant prop;
RINOK(callback->GetProperty(i, kpidPosixAttrib, &prop));
if (prop.vt == VT_EMPTY)
ui.Mode =
MY_LIN_S_IRWXO
| MY_LIN_S_IRWXG
| MY_LIN_S_IRWXU
| (ui.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG);
else if (prop.vt != VT_UI4)
return E_INVALIDARG;
else
ui.Mode = prop.ulVal;
}
{
NCOM::CPropVariant prop;
RINOK(callback->GetProperty(i, kpidMTime, &prop));
if (prop.vt == VT_EMPTY)
ui.MTime = 0;
else if (prop.vt != VT_FILETIME)
return E_INVALIDARG;
else
ui.MTime = NTime::FileTimeToUnixTime64(prop.filetime);
}
RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, true));
if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
ui.Name += '/';
RINOK(GetPropString(callback, i, kpidUser, ui.User, codePage));
RINOK(GetPropString(callback, i, kpidGroup, ui.Group, codePage));
}
if (IntToBool(newData))
{
NCOM::CPropVariant prop;
RINOK(callback->GetProperty(i, kpidSize, &prop));
if (prop.vt != VT_UI8)
return E_INVALIDARG;
ui.Size = prop.uhVal.QuadPart;
/*
// now we support GNU extension for big files
if (ui.Size >= ((UInt64)1 << 33))
return E_INVALIDARG;
*/
}
updateItems.Add(ui);
}
if (_thereIsPaxExtendedHeader)
{
// we restore original order of files, if there is pax header block
updateItems.Sort(CompareUpdateItems, NULL);
}
return UpdateArchive(_stream, outStream, _items, updateItems, codePage, callback);
COM_TRY_END
}
}}

View File

@@ -0,0 +1,23 @@
// Archive/TarHeader.cpp
#include "StdAfx.h"
#include "TarHeader.h"
namespace NArchive {
namespace NTar {
namespace NFileHeader {
const char *kLongLink = "././@LongLink";
const char *kLongLink2 = "@LongLink";
// The magic field is filled with this if uname and gname are valid.
namespace NMagic
{
// const char *kUsTar = "ustar"; // 5 chars
// const char *kGNUTar = "GNUtar "; // 7 chars and a null
// const char *kEmpty = "\0\0\0\0\0\0\0\0";
const char kUsTar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ;
}
}}}

View File

@@ -0,0 +1,84 @@
// Archive/TarHeader.h
#ifndef __ARCHIVE_TAR_HEADER_H
#define __ARCHIVE_TAR_HEADER_H
#include "../../../Common/MyTypes.h"
namespace NArchive {
namespace NTar {
namespace NFileHeader
{
const unsigned kRecordSize = 512;
const unsigned kNameSize = 100;
const unsigned kUserNameSize = 32;
const unsigned kGroupNameSize = 32;
const unsigned kPrefixSize = 155;
const unsigned kUstarMagic_Offset = 257;
/*
struct CHeader
{
char Name[kNameSize];
char Mode[8];
char UID[8];
char GID[8];
char Size[12];
char ModificationTime[12];
char CheckSum[8];
char LinkFlag;
char LinkName[kNameSize];
char Magic[8];
char UserName[kUserNameSize];
char GroupName[kGroupNameSize];
char DeviceMajor[8];
char DeviceMinor[8];
char Prefix[155];
};
union CRecord
{
CHeader Header;
Byte Padding[kRecordSize];
};
*/
namespace NLinkFlag
{
const char kOldNormal = 0; // Normal disk file, Unix compatible
const char kNormal = '0'; // Normal disk file
const char kHardLink = '1'; // Link to previously dumped file
const char kSymLink = '2'; // Symbolic link
const char kCharacter = '3'; // Character special file
const char kBlock = '4'; // Block special file
const char kDirectory = '5'; // Directory
const char kFIFO = '6'; // FIFO special file
const char kContiguous = '7'; // Contiguous file
const char kGnu_LongLink = 'K';
const char kGnu_LongName = 'L';
const char kSparse = 'S';
const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR.
data: list of files created by the --incremental (-G) option
Each file name is preceded by either
- 'Y' (file should be in this archive)
- 'N' (file is a directory, or is not stored in the archive.)
Each file name is terminated by a null + an additional null after
the last file name. */
}
extern const char *kLongLink; // = "././@LongLink";
extern const char *kLongLink2; // = "@LongLink";
namespace NMagic
{
// extern const char *kUsTar; // = "ustar"; // 5 chars
// extern const char *kGNUTar; // = "GNUtar "; // 7 chars and a null
// extern const char *kEmpty; // = "\0\0\0\0\0\0\0\0"
extern const char kUsTar_00[];
}
}
}}
#endif

View File

@@ -0,0 +1,422 @@
// TarIn.cpp
#include "StdAfx.h"
#include "../../../../C/CpuArch.h"
#include "../../../Common/StringToInt.h"
#include "../../Common/StreamUtils.h"
#include "../IArchive.h"
#include "TarIn.h"
namespace NArchive {
namespace NTar {
static void MyStrNCpy(char *dest, const char *src, unsigned size)
{
for (unsigned i = 0; i < size; i++)
{
char c = src[i];
dest[i] = c;
if (c == 0)
break;
}
}
static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res)
{
char sz[32];
MyStrNCpy(sz, srcString, size);
sz[size] = 0;
const char *end;
unsigned i;
for (i = 0; sz[i] == ' '; i++);
res = ConvertOctStringToUInt64(sz + i, &end);
if (end == sz + i)
return false;
return (*end == ' ' || *end == 0);
}
static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res)
{
UInt64 res64;
if (!OctalToNumber(srcString, size, res64))
return false;
res = (UInt32)res64;
return (res64 <= 0xFFFFFFFF);
}
#define RIF(x) { if (!(x)) return S_OK; }
/*
static bool IsEmptyData(const char *buf, size_t size)
{
for (unsigned i = 0; i < size; i++)
if (buf[i] != 0)
return false;
return true;
}
*/
static bool IsRecordLast(const char *buf)
{
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
if (buf[i] != 0)
return false;
return true;
}
static void ReadString(const char *s, unsigned size, AString &result)
{
char temp[NFileHeader::kRecordSize + 1];
MyStrNCpy(temp, s, size);
temp[size] = '\0';
result = temp;
}
static bool ParseInt64(const char *p, Int64 &val)
{
UInt32 h = GetBe32(p);
val = GetBe64(p + 4);
if (h == (UInt32)1 << 31)
return ((val >> 63) & 1) == 0;
if (h == (UInt32)(Int32)-1)
return ((val >> 63) & 1) != 0;
UInt64 uv;
bool res = OctalToNumber(p, 12, uv);
val = uv;
return res;
}
static bool ParseSize(const char *p, UInt64 &val)
{
if (GetBe32(p) == (UInt32)1 << 31)
{
// GNU extension
val = GetBe64(p + 4);
return ((val >> 63) & 1) == 0;
}
return OctalToNumber(p, 12, val);
}
#define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
{
if (size < NFileHeader::kRecordSize)
return k_IsArc_Res_NEED_MORE;
const char *p = (const char *)p2;
p += NFileHeader::kNameSize;
UInt32 mode;
CHECK(OctalToNumber32(p, 8, mode)); p += 8;
// if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0;
p += 8;
// if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0;
p += 8;
UInt64 packSize;
Int64 time;
UInt32 checkSum;
CHECK(ParseSize(p, packSize)); p += 12;
CHECK(ParseInt64(p, time)); p += 12;
CHECK(OctalToNumber32(p, 8, checkSum));
return k_IsArc_Res_YES;
}
static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
{
char buf[NFileHeader::kRecordSize];
char *p = buf;
error = k_ErrorType_OK;
filled = false;
bool thereAreEmptyRecords = false;
for (;;)
{
size_t processedSize = NFileHeader::kRecordSize;
RINOK(ReadStream(stream, buf, &processedSize));
if (processedSize == 0)
{
if (!thereAreEmptyRecords)
error = k_ErrorType_UnexpectedEnd; // "There are no trailing zero-filled records";
return S_OK;
}
if (processedSize != NFileHeader::kRecordSize)
{
if (!thereAreEmptyRecords)
error = k_ErrorType_UnexpectedEnd; // error = "There is no correct record at the end of archive";
else
{
/*
if (IsEmptyData(buf, processedSize))
error = k_ErrorType_UnexpectedEnd;
else
{
// extraReadSize = processedSize;
// error = k_ErrorType_Corrupted; // some data after the end tail zeros
}
*/
}
return S_OK;
}
if (!IsRecordLast(buf))
break;
item.HeaderSize += NFileHeader::kRecordSize;
thereAreEmptyRecords = true;
}
if (thereAreEmptyRecords)
{
// error = "There are data after end of archive";
return S_OK;
}
error = k_ErrorType_Corrupted;
ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize;
item.NameCouldBeReduced =
(item.Name.Len() == NFileHeader::kNameSize ||
item.Name.Len() == NFileHeader::kNameSize - 1);
RIF(OctalToNumber32(p, 8, item.Mode)); p += 8;
if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0; p += 8;
if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0; p += 8;
RIF(ParseSize(p, item.PackSize));
item.Size = item.PackSize;
p += 12;
RIF(ParseInt64(p, item.MTime)); p += 12;
UInt32 checkSum;
RIF(OctalToNumber32(p, 8, checkSum));
memset(p, ' ', 8); p += 8;
item.LinkFlag = *p++;
ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
item.LinkNameCouldBeReduced =
(item.LinkName.Len() == NFileHeader::kNameSize ||
item.LinkName.Len() == NFileHeader::kNameSize - 1);
memcpy(item.Magic, p, 8); p += 8;
ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
item.DeviceMajorDefined = (p[0] != 0); if (item.DeviceMajorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMajor)); } p += 8;
item.DeviceMinorDefined = (p[0] != 0); if (item.DeviceMinorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMinor)); } p += 8;
if (p[0] != 0)
{
AString prefix;
ReadString(p, NFileHeader::kPrefixSize, prefix);
if (!prefix.IsEmpty()
&& item.IsUstarMagic()
&& (item.LinkFlag != 'L' /* || prefix != "00000000000" */ ))
item.Name = prefix + '/' + item.Name;
}
p += NFileHeader::kPrefixSize;
if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
{
item.PackSize = 0;
item.Size = 0;
}
/*
TAR standard requires sum of unsigned byte values.
But some TAR programs use sum of signed byte values.
So we check both values.
*/
UInt32 checkSumReal = 0;
Int32 checkSumReal_Signed = 0;
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
{
char c = buf[i];
checkSumReal_Signed += (signed char)c;
checkSumReal += (Byte)buf[i];
}
if (checkSumReal != checkSum)
{
if ((UInt32)checkSumReal_Signed != checkSum)
return S_OK;
}
item.HeaderSize += NFileHeader::kRecordSize;
if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse)
{
Byte isExtended = buf[482];
if (isExtended != 0 && isExtended != 1)
return S_OK;
RIF(ParseSize(buf + 483, item.Size));
UInt64 min = 0;
for (unsigned i = 0; i < 4; i++)
{
p = buf + 386 + 24 * i;
if (GetBe32(p) == 0)
{
if (isExtended != 0)
return S_OK;
break;
}
CSparseBlock sb;
RIF(ParseSize(p, sb.Offset));
RIF(ParseSize(p + 12, sb.Size));
item.SparseBlocks.Add(sb);
if (sb.Offset < min || sb.Offset > item.Size)
return S_OK;
if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
return S_OK;
min = sb.Offset + sb.Size;
if (min < sb.Offset)
return S_OK;
}
if (min > item.Size)
return S_OK;
while (isExtended != 0)
{
size_t processedSize = NFileHeader::kRecordSize;
RINOK(ReadStream(stream, buf, &processedSize));
if (processedSize != NFileHeader::kRecordSize)
{
error = k_ErrorType_UnexpectedEnd;
return S_OK;
}
item.HeaderSize += NFileHeader::kRecordSize;
isExtended = buf[21 * 24];
if (isExtended != 0 && isExtended != 1)
return S_OK;
for (unsigned i = 0; i < 21; i++)
{
p = buf + 24 * i;
if (GetBe32(p) == 0)
{
if (isExtended != 0)
return S_OK;
break;
}
CSparseBlock sb;
RIF(ParseSize(p, sb.Offset));
RIF(ParseSize(p + 12, sb.Size));
item.SparseBlocks.Add(sb);
if (sb.Offset < min || sb.Offset > item.Size)
return S_OK;
if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
return S_OK;
min = sb.Offset + sb.Size;
if (min < sb.Offset)
return S_OK;
}
}
if (min > item.Size)
return S_OK;
}
filled = true;
error = k_ErrorType_OK;
return S_OK;
}
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
{
item.HeaderSize = 0;
bool flagL = false;
bool flagK = false;
AString nameL;
AString nameK;
for (;;)
{
RINOK(GetNextItemReal(stream, filled, item, error));
if (!filled)
{
if (error == k_ErrorType_OK && (flagL || flagK))
error = k_ErrorType_Corrupted;
return S_OK;
}
if (error != k_ErrorType_OK)
return S_OK;
if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName || // file contains a long name
item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongLink) // file contains a long linkname
{
AString *name;
if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName)
{ if (flagL) return S_OK; flagL = true; name = &nameL; }
else
{ if (flagK) return S_OK; flagK = true; name = &nameK; }
if (item.Name != NFileHeader::kLongLink &&
item.Name != NFileHeader::kLongLink2)
return S_OK;
if (item.PackSize > (1 << 14))
return S_OK;
unsigned packSize = (unsigned)item.GetPackSizeAligned();
char *buf = name->GetBuf(packSize);
size_t processedSize = packSize;
HRESULT res = ReadStream(stream, buf, &processedSize);
item.HeaderSize += (unsigned)processedSize;
name->ReleaseBuf_CalcLen((unsigned)item.PackSize);
RINOK(res);
if (processedSize != packSize)
{
error = k_ErrorType_UnexpectedEnd;
return S_OK;
}
continue;
}
switch (item.LinkFlag)
{
case 'g':
case 'x':
case 'X':
{
// pax Extended Header
break;
}
case NFileHeader::NLinkFlag::kDumpDir:
{
break;
// GNU Extensions to the Archive Format
}
case NFileHeader::NLinkFlag::kSparse:
{
break;
// GNU Extensions to the Archive Format
}
default:
if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
return S_OK;
}
if (flagL)
{
item.Name = nameL;
item.NameCouldBeReduced = false;
}
if (flagK)
{
item.LinkName = nameK;
item.LinkNameCouldBeReduced = false;
}
error = k_ErrorType_OK;
return S_OK;
}
}
}}

View File

@@ -0,0 +1,26 @@
// TarIn.h
#ifndef __ARCHIVE_TAR_IN_H
#define __ARCHIVE_TAR_IN_H
#include "../../IStream.h"
#include "TarItem.h"
namespace NArchive {
namespace NTar {
enum EErrorType
{
k_ErrorType_OK,
k_ErrorType_Corrupted,
k_ErrorType_UnexpectedEnd,
};
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, EErrorType &error);
API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size);
}}
#endif

View File

@@ -0,0 +1,98 @@
// TarItem.h
#ifndef __ARCHIVE_TAR_ITEM_H
#define __ARCHIVE_TAR_ITEM_H
#include "../Common/ItemNameUtils.h"
#include "TarHeader.h"
namespace NArchive {
namespace NTar {
struct CSparseBlock
{
UInt64 Offset;
UInt64 Size;
};
struct CItem
{
AString Name;
UInt64 PackSize;
UInt64 Size;
Int64 MTime;
UInt32 Mode;
UInt32 UID;
UInt32 GID;
UInt32 DeviceMajor;
UInt32 DeviceMinor;
AString LinkName;
AString User;
AString Group;
char Magic[8];
char LinkFlag;
bool DeviceMajorDefined;
bool DeviceMinorDefined;
CRecordVector<CSparseBlock> SparseBlocks;
bool IsSymLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymLink && (Size == 0); }
bool IsHardLink() const { return LinkFlag == NFileHeader::NLinkFlag::kHardLink; }
bool IsSparse() const { return LinkFlag == NFileHeader::NLinkFlag::kSparse; }
UInt64 GetUnpackSize() const { return IsSymLink() ? LinkName.Len() : Size; }
bool IsPaxExtendedHeader() const
{
switch (LinkFlag)
{
case 'g':
case 'x':
case 'X': // Check it
return true;
}
return false;
}
bool IsDir() const
{
switch (LinkFlag)
{
case NFileHeader::NLinkFlag::kDirectory:
case NFileHeader::NLinkFlag::kDumpDir:
return true;
case NFileHeader::NLinkFlag::kOldNormal:
case NFileHeader::NLinkFlag::kNormal:
case NFileHeader::NLinkFlag::kSymLink:
return NItemName::HasTailSlash(Name, CP_OEMCP);
}
return false;
}
bool IsUstarMagic() const
{
for (int i = 0; i < 5; i++)
if (Magic[i] != NFileHeader::NMagic::kUsTar_00[i])
return false;
return true;
}
UInt64 GetPackSizeAligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); }
};
struct CItemEx: public CItem
{
UInt64 HeaderPos;
unsigned HeaderSize;
bool NameCouldBeReduced;
bool LinkNameCouldBeReduced;
UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
UInt64 GetFullSize() const { return HeaderSize + PackSize; }
};
}}
#endif

View File

@@ -0,0 +1,245 @@
// TarOut.cpp
#include "StdAfx.h"
#include "../../Common/StreamUtils.h"
#include "TarOut.h"
namespace NArchive {
namespace NTar {
HRESULT COutArchive::WriteBytes(const void *data, unsigned size)
{
Pos += size;
return WriteStream(m_Stream, data, size);
}
static void MyStrNCpy(char *dest, const char *src, unsigned size)
{
for (unsigned i = 0; i < size; i++)
{
char c = src[i];
dest[i] = c;
if (c == 0)
break;
}
}
static bool WriteOctal_8(char *s, UInt32 val)
{
const unsigned kNumDigits = 8 - 1;
if (val >= ((UInt32)1 << (kNumDigits * 3)))
return false;
for (unsigned i = 0; i < kNumDigits; i++)
{
s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
val >>= 3;
}
return true;
}
static void WriteOctal_12(char *s, UInt64 val)
{
const unsigned kNumDigits = 12 - 1;
if (val >= ((UInt64)1 << (kNumDigits * 3)))
{
// GNU extension;
s[0] = (char)(Byte)0x80;
s[1] = s[2] = s[3] = 0;
for (unsigned i = 0; i < 8; i++, val <<= 8)
s[4 + i] = (char)(val >> 56);
return;
}
for (unsigned i = 0; i < kNumDigits; i++)
{
s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
val >>= 3;
}
}
static void WriteOctal_12_Signed(char *s, Int64 val)
{
if (val >= 0)
{
WriteOctal_12(s, val);
return;
}
s[0] = s[1] = s[2] = s[3] = (char)(Byte)0xFF;
for (unsigned i = 0; i < 8; i++, val <<= 8)
s[4 + i] = (char)(val >> 56);
}
static bool CopyString(char *dest, const AString &src, unsigned maxSize)
{
if (src.Len() >= maxSize)
return false;
MyStringCopy(dest, (const char *)src);
return true;
}
#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; }
HRESULT COutArchive::WriteHeaderReal(const CItem &item)
{
char record[NFileHeader::kRecordSize];
memset(record, 0, NFileHeader::kRecordSize);
char *cur = record;
if (item.Name.Len() > NFileHeader::kNameSize)
return E_FAIL;
MyStrNCpy(cur, item.Name, NFileHeader::kNameSize);
cur += NFileHeader::kNameSize;
RETURN_IF_NOT_TRUE(WriteOctal_8(cur, item.Mode)); cur += 8;
RETURN_IF_NOT_TRUE(WriteOctal_8(cur, item.UID)); cur += 8;
RETURN_IF_NOT_TRUE(WriteOctal_8(cur, item.GID)); cur += 8;
WriteOctal_12(cur, item.PackSize); cur += 12;
WriteOctal_12_Signed(cur, item.MTime); cur += 12;
memset(cur, ' ', 8);
cur += 8;
*cur++ = item.LinkFlag;
RETURN_IF_NOT_TRUE(CopyString(cur, item.LinkName, NFileHeader::kNameSize));
cur += NFileHeader::kNameSize;
memcpy(cur, item.Magic, 8);
cur += 8;
RETURN_IF_NOT_TRUE(CopyString(cur, item.User, NFileHeader::kUserNameSize));
cur += NFileHeader::kUserNameSize;
RETURN_IF_NOT_TRUE(CopyString(cur, item.Group, NFileHeader::kGroupNameSize));
cur += NFileHeader::kGroupNameSize;
if (item.DeviceMajorDefined) RETURN_IF_NOT_TRUE(WriteOctal_8(cur, item.DeviceMajor)); cur += 8;
if (item.DeviceMinorDefined) RETURN_IF_NOT_TRUE(WriteOctal_8(cur, item.DeviceMinor)); cur += 8;
if (item.IsSparse())
{
record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0);
WriteOctal_12(record + 483, item.Size);
for (unsigned i = 0; i < item.SparseBlocks.Size() && i < 4; i++)
{
const CSparseBlock &sb = item.SparseBlocks[i];
char *p = record + 386 + 24 * i;
WriteOctal_12(p, sb.Offset);
WriteOctal_12(p + 12, sb.Size);
}
}
{
UInt32 checkSum = 0;
{
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
checkSum += (Byte)record[i];
}
/* we use GNU TAR scheme:
checksum field is formatted differently from the
other fields: it has [6] digits, a null, then a space. */
// RETURN_IF_NOT_TRUE(WriteOctal_8(record + 148, checkSum));
const unsigned kNumDigits = 6;
for (unsigned i = 0; i < kNumDigits; i++)
{
record[148 + kNumDigits - 1 - i] = (char)('0' + (checkSum & 7));
checkSum >>= 3;
}
record[148 + 6] = 0;
}
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
if (item.IsSparse())
{
for (unsigned i = 4; i < item.SparseBlocks.Size();)
{
memset(record, 0, NFileHeader::kRecordSize);
for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++)
{
const CSparseBlock &sb = item.SparseBlocks[i];
char *p = record + 24 * t;
WriteOctal_12(p, sb.Offset);
WriteOctal_12(p + 12, sb.Size);
}
record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0);
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
}
}
return S_OK;
}
HRESULT COutArchive::WriteHeader(const CItem &item)
{
unsigned nameSize = item.Name.Len();
unsigned linkSize = item.LinkName.Len();
/* There two versions of GNU tar:
OLDGNU_FORMAT: it writes short name and zero at the end
GNU_FORMAT: it writes only short name without zero at the end
we write it as OLDGNU_FORMAT with zero at the end */
if (nameSize < NFileHeader::kNameSize &&
linkSize < NFileHeader::kNameSize)
return WriteHeaderReal(item);
CItem mi = item;
mi.Name = NFileHeader::kLongLink;
mi.LinkName.Empty();
for (int i = 0; i < 2; i++)
{
const AString *name;
// We suppose that GNU tar also writes item for long link before item for LongName?
if (i == 0)
{
mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongLink;
name = &item.LinkName;
}
else
{
mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongName;
name = &item.Name;
}
if (name->Len() < NFileHeader::kNameSize)
continue;
unsigned nameStreamSize = name->Len() + 1;
mi.PackSize = nameStreamSize;
RINOK(WriteHeaderReal(mi));
RINOK(WriteBytes((const char *)*name, nameStreamSize));
RINOK(FillDataResidual(nameStreamSize));
}
mi = item;
if (mi.Name.Len() >= NFileHeader::kNameSize)
mi.Name.SetFrom(item.Name, NFileHeader::kNameSize - 1);
if (mi.LinkName.Len() >= NFileHeader::kNameSize)
mi.LinkName.SetFrom(item.LinkName, NFileHeader::kNameSize - 1);
return WriteHeaderReal(mi);
}
HRESULT COutArchive::FillDataResidual(UInt64 dataSize)
{
unsigned lastRecordSize = ((unsigned)dataSize & (NFileHeader::kRecordSize - 1));
if (lastRecordSize == 0)
return S_OK;
unsigned rem = NFileHeader::kRecordSize - lastRecordSize;
Byte buf[NFileHeader::kRecordSize];
memset(buf, 0, rem);
return WriteBytes(buf, rem);
}
HRESULT COutArchive::WriteFinishHeader()
{
Byte record[NFileHeader::kRecordSize];
memset(record, 0, NFileHeader::kRecordSize);
for (unsigned i = 0; i < 2; i++)
{
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
}
return S_OK;
}
}}

View File

@@ -0,0 +1,36 @@
// Archive/TarOut.h
#ifndef __ARCHIVE_TAR_OUT_H
#define __ARCHIVE_TAR_OUT_H
#include "../../../Common/MyCom.h"
#include "../../IStream.h"
#include "TarItem.h"
namespace NArchive {
namespace NTar {
class COutArchive
{
CMyComPtr<ISequentialOutStream> m_Stream;
HRESULT WriteBytes(const void *data, unsigned size);
HRESULT WriteHeaderReal(const CItem &item);
public:
UInt64 Pos;
void Create(ISequentialOutStream *outStream)
{
m_Stream = outStream;
}
HRESULT WriteHeader(const CItem &item);
HRESULT FillDataResidual(UInt64 dataSize);
HRESULT WriteFinishHeader();
};
}}
#endif

View File

@@ -0,0 +1,23 @@
// TarRegister.cpp
#include "StdAfx.h"
#include "../../Common/RegisterArc.h"
#include "TarHandler.h"
namespace NArchive {
namespace NTar {
static const Byte k_Signature[] = { 'u', 's', 't', 'a', 'r' };
REGISTER_ARC_IO(
"tar", "tar ova", 0, 0xEE,
k_Signature,
NFileHeader::kUstarMagic_Offset,
NArcInfoFlags::kStartOpen |
NArcInfoFlags::kSymLinks |
NArcInfoFlags::kHardLinks,
IsArc_Tar)
}}

View File

@@ -0,0 +1,266 @@
// TarUpdate.cpp
#include "StdAfx.h"
#include "../../../Windows/TimeUtils.h"
#include "../../Common/LimitedStreams.h"
#include "../../Common/ProgressUtils.h"
#include "../../Compress/CopyCoder.h"
#include "TarOut.h"
#include "TarUpdate.h"
namespace NArchive {
namespace NTar {
HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId,
AString &res, UINT codePage, bool convertSlash = false);
HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
const CObjectVector<NArchive::NTar::CItemEx> &inputItems,
const CObjectVector<CUpdateItem> &updateItems,
UINT codePage,
IArchiveUpdateCallback *updateCallback)
{
COutArchive outArchive;
outArchive.Create(outStream);
outArchive.Pos = 0;
CMyComPtr<IOutStream> outSeekStream;
outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
UInt64 complexity = 0;
unsigned i;
for (i = 0; i < updateItems.Size(); i++)
{
const CUpdateItem &ui = updateItems[i];
if (ui.NewData)
complexity += ui.Size;
else
complexity += inputItems[ui.IndexInArc].GetFullSize();
}
RINOK(updateCallback->SetTotal(complexity));
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(updateCallback, true);
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
streamSpec->SetStream(inStream);
complexity = 0;
for (i = 0; i < updateItems.Size(); i++)
{
lps->InSize = lps->OutSize = complexity;
RINOK(lps->SetCur());
const CUpdateItem &ui = updateItems[i];
CItem item;
if (ui.NewProps)
{
item.Mode = ui.Mode;
item.Name = ui.Name;
item.User = ui.User;
item.Group = ui.Group;
if (ui.IsDir)
{
item.LinkFlag = NFileHeader::NLinkFlag::kDirectory;
item.PackSize = 0;
}
else
{
item.LinkFlag = NFileHeader::NLinkFlag::kNormal;
item.PackSize = ui.Size;
}
item.MTime = ui.MTime;
item.DeviceMajorDefined = false;
item.DeviceMinorDefined = false;
item.UID = 0;
item.GID = 0;
memcpy(item.Magic, NFileHeader::NMagic::kUsTar_00, 8);
}
else
item = inputItems[ui.IndexInArc];
AString symLink;
if (ui.NewData || ui.NewProps)
{
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink, codePage, true));
if (!symLink.IsEmpty())
{
item.LinkFlag = NFileHeader::NLinkFlag::kSymLink;
item.LinkName = symLink;
}
}
if (ui.NewData)
{
item.SparseBlocks.Clear();
item.PackSize = ui.Size;
item.Size = ui.Size;
if (ui.Size == (UInt64)(Int64)-1)
return E_INVALIDARG;
CMyComPtr<ISequentialInStream> fileInStream;
bool needWrite = true;
if (!symLink.IsEmpty())
{
item.PackSize = 0;
item.Size = 0;
}
else
{
HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
if (res == S_FALSE)
needWrite = false;
else
{
RINOK(res);
if (fileInStream)
{
CMyComPtr<IStreamGetProps> getProps;
fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
if (getProps)
{
FILETIME mTime;
UInt64 size2;
if (getProps->GetProps(&size2, NULL, NULL, &mTime, NULL) == S_OK)
{
item.PackSize = size2;
item.Size = size2;
item.MTime = NWindows::NTime::FileTimeToUnixTime64(mTime);;
}
}
}
else
{
item.PackSize = 0;
item.Size = 0;
}
{
AString hardLink;
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink, codePage, true));
if (!hardLink.IsEmpty())
{
item.LinkFlag = NFileHeader::NLinkFlag::kHardLink;
item.LinkName = hardLink;
item.PackSize = 0;
item.Size = 0;
fileInStream.Release();
}
}
}
}
if (needWrite)
{
UInt64 fileHeaderStartPos = outArchive.Pos;
RINOK(outArchive.WriteHeader(item));
if (fileInStream)
{
RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
outArchive.Pos += copyCoderSpec->TotalSize;
if (copyCoderSpec->TotalSize != item.PackSize)
{
if (!outSeekStream)
return E_FAIL;
UInt64 backOffset = outArchive.Pos - fileHeaderStartPos;
RINOK(outSeekStream->Seek(-(Int64)backOffset, STREAM_SEEK_CUR, NULL));
outArchive.Pos = fileHeaderStartPos;
item.PackSize = copyCoderSpec->TotalSize;
RINOK(outArchive.WriteHeader(item));
RINOK(outSeekStream->Seek(item.PackSize, STREAM_SEEK_CUR, NULL));
outArchive.Pos += item.PackSize;
}
RINOK(outArchive.FillDataResidual(item.PackSize));
}
}
complexity += item.PackSize;
RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
}
else
{
const CItemEx &existItem = inputItems[ui.IndexInArc];
UInt64 size;
if (ui.NewProps)
{
// memcpy(item.Magic, NFileHeader::NMagic::kEmpty, 8);
if (!symLink.IsEmpty())
{
item.PackSize = 0;
item.Size = 0;
}
else
{
if (ui.IsDir == existItem.IsDir())
item.LinkFlag = existItem.LinkFlag;
item.SparseBlocks = existItem.SparseBlocks;
item.Size = existItem.Size;
item.PackSize = existItem.PackSize;
}
item.DeviceMajorDefined = existItem.DeviceMajorDefined;
item.DeviceMinorDefined = existItem.DeviceMinorDefined;
item.DeviceMajor = existItem.DeviceMajor;
item.DeviceMinor = existItem.DeviceMinor;
item.UID = existItem.UID;
item.GID = existItem.GID;
RINOK(outArchive.WriteHeader(item));
RINOK(inStream->Seek(existItem.GetDataPosition(), STREAM_SEEK_SET, NULL));
size = existItem.PackSize;
}
else
{
RINOK(inStream->Seek(existItem.HeaderPos, STREAM_SEEK_SET, NULL));
size = existItem.GetFullSize();
}
streamSpec->Init(size);
if (opCallback)
{
RINOK(opCallback->ReportOperation(
NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
NUpdateNotifyOp::kReplicate))
}
RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
if (copyCoderSpec->TotalSize != size)
return E_FAIL;
outArchive.Pos += size;
RINOK(outArchive.FillDataResidual(existItem.PackSize));
complexity += size;
}
}
lps->InSize = lps->OutSize = complexity;
RINOK(lps->SetCur());
return outArchive.WriteFinishHeader();
}
}}

View File

@@ -0,0 +1,38 @@
// TarUpdate.h
#ifndef __TAR_UPDATE_H
#define __TAR_UPDATE_H
#include "../IArchive.h"
#include "TarItem.h"
namespace NArchive {
namespace NTar {
struct CUpdateItem
{
int IndexInArc;
int IndexInClient;
UInt64 Size;
Int64 MTime;
UInt32 Mode;
bool NewData;
bool NewProps;
bool IsDir;
AString Name;
AString User;
AString Group;
CUpdateItem(): Size(0), IsDir(false) {}
};
HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
const CObjectVector<CItemEx> &inputItems,
const CObjectVector<CUpdateItem> &updateItems,
UINT codePage,
IArchiveUpdateCallback *updateCallback);
}}
#endif