267 lines
7.4 KiB
C++
267 lines
7.4 KiB
C++
// 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();
|
|
}
|
|
|
|
}}
|