mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-14 16:11:38 -06:00
Update to 7-Zip Version 22.00
See: https://sourceforge.net/p/sevenzip/discussion/45797/thread/9c2d9061ce/
This commit is contained in:
@@ -36,22 +36,34 @@ static const Byte kProps[] =
|
||||
kpidSize,
|
||||
kpidPackSize,
|
||||
kpidMTime,
|
||||
kpidCTime,
|
||||
kpidATime,
|
||||
kpidPosixAttrib,
|
||||
kpidUser,
|
||||
kpidGroup,
|
||||
kpidUserId,
|
||||
kpidGroupId,
|
||||
kpidSymLink,
|
||||
kpidHardLink,
|
||||
kpidCharacts
|
||||
// kpidLinkType
|
||||
kpidCharacts,
|
||||
kpidComment
|
||||
, kpidDeviceMajor
|
||||
, kpidDeviceMinor
|
||||
// , kpidDevice
|
||||
// , kpidHeadersSize // for debug
|
||||
// , kpidOffset // for debug
|
||||
};
|
||||
|
||||
static const Byte kArcProps[] =
|
||||
{
|
||||
kpidHeadersSize,
|
||||
kpidCodePage,
|
||||
kpidCharacts
|
||||
kpidCharacts,
|
||||
kpidComment
|
||||
};
|
||||
|
||||
static const char *k_Characts_Prefix = "PREFIX";
|
||||
|
||||
IMP_IInArchive_Props
|
||||
IMP_IInArchive_ArcProps
|
||||
|
||||
@@ -60,14 +72,14 @@ 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 kpidPhySize: if (_arc._phySize_Defined) prop = _arc._phySize; break;
|
||||
case kpidHeadersSize: if (_arc._phySize_Defined) prop = _arc._headersSize; break;
|
||||
case kpidErrorFlags:
|
||||
{
|
||||
UInt32 flags = 0;
|
||||
if (!_isArc)
|
||||
flags |= kpv_ErrorFlags_IsNotArc;
|
||||
else switch (_error)
|
||||
else switch (_arc._error)
|
||||
{
|
||||
case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break;
|
||||
case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break;
|
||||
@@ -82,7 +94,7 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
|
||||
case kpidWarningFlags:
|
||||
{
|
||||
if (_warning)
|
||||
if (_arc._is_Warning)
|
||||
prop = kpv_ErrorFlags_HeadersError;
|
||||
break;
|
||||
}
|
||||
@@ -107,7 +119,34 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
|
||||
case kpidCharacts:
|
||||
{
|
||||
prop = _encodingCharacts.GetCharactsString();
|
||||
AString s;
|
||||
if (_arc._are_Gnu) s.Add_OptSpaced("GNU");
|
||||
if (_arc._are_Posix) s.Add_OptSpaced("POSIX");
|
||||
if (_arc._are_Pax_Items) s.Add_OptSpaced("PAX_ITEM");
|
||||
if (_arc._pathPrefix_WasUsed) s.Add_OptSpaced(k_Characts_Prefix);
|
||||
if (_arc._are_LongName) s.Add_OptSpaced("LongName");
|
||||
if (_arc._are_LongLink) s.Add_OptSpaced("LongLink");
|
||||
if (_arc._are_Pax) s.Add_OptSpaced("PAX");
|
||||
if (_arc._are_pax_path) s.Add_OptSpaced("path");
|
||||
if (_arc._are_pax_link) s.Add_OptSpaced("linkpath");
|
||||
if (_arc._are_mtime) s.Add_OptSpaced("mtime");
|
||||
if (_arc._are_atime) s.Add_OptSpaced("atime");
|
||||
if (_arc._are_ctime) s.Add_OptSpaced("ctime");
|
||||
if (_arc._is_PaxGlobal_Error) s.Add_OptSpaced("PAX_GLOBAL_ERROR");
|
||||
s.Add_OptSpaced(_encodingCharacts.GetCharactsString());
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
|
||||
case kpidComment:
|
||||
{
|
||||
if (_arc.PaxGlobal_Defined)
|
||||
{
|
||||
AString s;
|
||||
_arc.PaxGlobal.Print_To_String(s);
|
||||
if (!s.IsEmpty())
|
||||
prop = s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -115,32 +154,6 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item)
|
||||
{
|
||||
item.HeaderPos = _phySize;
|
||||
EErrorType error;
|
||||
HRESULT res = ReadItem(stream, filled, item, error);
|
||||
if (error == k_ErrorType_Warning)
|
||||
_warning = true;
|
||||
else if (error != k_ErrorType_OK)
|
||||
_error = error;
|
||||
RINOK(res);
|
||||
if (filled)
|
||||
{
|
||||
/*
|
||||
if (item.IsSparse())
|
||||
_isSparse = true;
|
||||
*/
|
||||
if (item.IsPaxExtendedHeader())
|
||||
_thereIsPaxExtendedHeader = true;
|
||||
if (item.IsThereWarning())
|
||||
_warning = true;
|
||||
}
|
||||
_phySize += item.HeaderSize;
|
||||
_headersSize += item.HeaderSize;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void CEncodingCharacts::Check(const AString &s)
|
||||
{
|
||||
@@ -199,16 +212,20 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
|
||||
}
|
||||
|
||||
_phySizeDefined = true;
|
||||
_arc._phySize_Defined = true;
|
||||
|
||||
// bool utf8_OK = true;
|
||||
|
||||
_arc.SeqStream = stream;
|
||||
_arc.InStream = stream;
|
||||
_arc.OpenCallback = callback;
|
||||
|
||||
CItemEx item;
|
||||
for (;;)
|
||||
{
|
||||
CItemEx item;
|
||||
bool filled;
|
||||
RINOK(ReadItem2(stream, filled, item));
|
||||
if (!filled)
|
||||
_arc.NumFiles = _items.Size();
|
||||
RINOK(_arc.ReadItem(item));
|
||||
if (!_arc.filled)
|
||||
break;
|
||||
|
||||
_isArc = true;
|
||||
@@ -228,10 +245,10 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
|
||||
_items.Add(item);
|
||||
|
||||
RINOK(stream->Seek((Int64)item.GetPackSizeAligned(), STREAM_SEEK_CUR, &_phySize));
|
||||
if (_phySize > endPos)
|
||||
RINOK(stream->Seek((Int64)item.Get_PackSize_Aligned(), STREAM_SEEK_CUR, &_arc._phySize));
|
||||
if (_arc._phySize > endPos)
|
||||
{
|
||||
_error = k_ErrorType_UnexpectedEnd;
|
||||
_arc._error = k_ErrorType_UnexpectedEnd;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
@@ -241,6 +258,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
break;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (callback)
|
||||
{
|
||||
if (_items.Size() == 1)
|
||||
@@ -249,10 +267,11 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
}
|
||||
if ((_items.Size() & 0x3FF) == 0)
|
||||
{
|
||||
UInt64 numFiles = _items.Size();
|
||||
const UInt64 numFiles = _items.Size();
|
||||
RINOK(callback->SetCompleted(&numFiles, &_phySize));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -266,7 +285,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
|
||||
if (_items.Size() == 0)
|
||||
{
|
||||
if (_error != k_ErrorType_OK)
|
||||
if (_arc._error != k_ErrorType_OK)
|
||||
{
|
||||
_isArc = false;
|
||||
return S_FALSE;
|
||||
@@ -294,6 +313,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||||
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
// for (int i = 0; i < 10; i++) // for debug
|
||||
{
|
||||
Close();
|
||||
RINOK(Open2(stream, openArchiveCallback));
|
||||
@@ -314,16 +334,11 @@ STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
|
||||
STDMETHODIMP CHandler::Close()
|
||||
{
|
||||
_isArc = false;
|
||||
_warning = false;
|
||||
_error = k_ErrorType_OK;
|
||||
|
||||
_phySizeDefined = false;
|
||||
_phySize = 0;
|
||||
_headersSize = 0;
|
||||
_arc.Clear();
|
||||
|
||||
_curIndex = 0;
|
||||
_latestIsRead = false;
|
||||
// _isSparse = false;
|
||||
_thereIsPaxExtendedHeader = false;
|
||||
_encodingCharacts.Clear();
|
||||
_items.Clear();
|
||||
_seqStream.Release();
|
||||
@@ -351,12 +366,12 @@ HRESULT CHandler::SkipTo(UInt32 index)
|
||||
{
|
||||
if (_latestIsRead)
|
||||
{
|
||||
UInt64 packSize = _latestItem.GetPackSizeAligned();
|
||||
const UInt64 packSize = _latestItem.Get_PackSize_Aligned();
|
||||
RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
|
||||
_phySize += copyCoderSpec->TotalSize;
|
||||
_arc._phySize += copyCoderSpec->TotalSize;
|
||||
if (copyCoderSpec->TotalSize != packSize)
|
||||
{
|
||||
_error = k_ErrorType_UnexpectedEnd;
|
||||
_arc._error = k_ErrorType_UnexpectedEnd;
|
||||
return S_FALSE;
|
||||
}
|
||||
_latestIsRead = false;
|
||||
@@ -364,11 +379,12 @@ HRESULT CHandler::SkipTo(UInt32 index)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool filled;
|
||||
RINOK(ReadItem2(_seqStream, filled, _latestItem));
|
||||
if (!filled)
|
||||
_arc.SeqStream = _seqStream;
|
||||
_arc.InStream = NULL;
|
||||
RINOK(_arc.ReadItem(_latestItem));
|
||||
if (!_arc.filled)
|
||||
{
|
||||
_phySizeDefined = true;
|
||||
_arc._phySize_Defined = true;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
_latestIsRead = true;
|
||||
@@ -390,6 +406,69 @@ void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant
|
||||
prop = dest;
|
||||
}
|
||||
|
||||
|
||||
static void PaxTimeToProp(const CPaxTime &pt, NWindows::NCOM::CPropVariant &prop)
|
||||
{
|
||||
UInt64 v;
|
||||
if (!NTime::UnixTime64_To_FileTime64(pt.Sec, v))
|
||||
return;
|
||||
if (pt.Ns != 0)
|
||||
v += pt.Ns / 100;
|
||||
FILETIME ft;
|
||||
ft.dwLowDateTime = (DWORD)v;
|
||||
ft.dwHighDateTime = (DWORD)(v >> 32);
|
||||
prop.SetAsTimeFrom_FT_Prec_Ns100(ft,
|
||||
k_PropVar_TimePrec_Base + pt.NumDigits, pt.Ns % 100);
|
||||
}
|
||||
|
||||
|
||||
#define ValToHex(t) ((char)(((t) < 10) ? ('0' + (t)) : ('a' + ((t) - 10))))
|
||||
|
||||
static void AddByteToHex2(unsigned val, AString &s)
|
||||
{
|
||||
unsigned t;
|
||||
t = val >> 4;
|
||||
s += ValToHex(t);
|
||||
t = val & 0xF;
|
||||
s += ValToHex(t);
|
||||
}
|
||||
|
||||
static void AddSpecCharToString(const char c, AString &s)
|
||||
{
|
||||
if ((Byte)c <= 0x20 || (Byte)c > 127)
|
||||
{
|
||||
s += '[';
|
||||
AddByteToHex2((Byte)(c), s);
|
||||
s += ']';
|
||||
}
|
||||
else
|
||||
s += c;
|
||||
}
|
||||
|
||||
static void AddSpecUInt64(AString &s, const char *name, UInt64 v)
|
||||
{
|
||||
if (v != 0)
|
||||
{
|
||||
s.Add_OptSpaced(name);
|
||||
if (v > 1)
|
||||
{
|
||||
s += ':';
|
||||
s.Add_UInt64(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddSpecBools(AString &s, const char *name, bool b1, bool b2)
|
||||
{
|
||||
if (b1)
|
||||
{
|
||||
s.Add_OptSpaced(name);
|
||||
if (b2)
|
||||
s += '*';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
@@ -413,49 +492,189 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
|
||||
{
|
||||
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 kpidSize: prop = item->Get_UnpackSize(); break;
|
||||
case kpidPackSize: prop = item->Get_PackSize_Aligned(); break;
|
||||
case kpidMTime:
|
||||
if (item->MTime != 0)
|
||||
{
|
||||
FILETIME ft;
|
||||
if (NTime::UnixTime64ToFileTime(item->MTime, ft))
|
||||
prop = ft;
|
||||
}
|
||||
break;
|
||||
case kpidPosixAttrib: prop = item->Get_Combined_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;
|
||||
case kpidCharacts:
|
||||
{
|
||||
AString s = item->EncodingCharacts.GetCharactsString();
|
||||
if (item->IsThereWarning())
|
||||
/*
|
||||
// for debug:
|
||||
PropVariant_SetFrom_UnixTime(prop, 1 << 30);
|
||||
prop.wReserved1 = k_PropVar_TimePrec_Base + 1;
|
||||
prop.wReserved2 = 12;
|
||||
break;
|
||||
*/
|
||||
|
||||
if (item->PaxTimes.MTime.IsDefined())
|
||||
PaxTimeToProp(item->PaxTimes.MTime, prop);
|
||||
else
|
||||
// if (item->MTime != 0)
|
||||
{
|
||||
s.Add_Space_if_NotEmpty();
|
||||
s += "HEADER_ERROR";
|
||||
// we allow (item->MTime == 0)
|
||||
FILETIME ft;
|
||||
if (NTime::UnixTime64_To_FileTime(item->MTime, ft))
|
||||
{
|
||||
unsigned prec = k_PropVar_TimePrec_Unix;
|
||||
if (item->MTime_IsBin)
|
||||
{
|
||||
/* we report here that it's Int64-UnixTime
|
||||
instead of basic UInt32-UnixTime range */
|
||||
prec = k_PropVar_TimePrec_Base;
|
||||
}
|
||||
prop.SetAsTimeFrom_FT_Prec(ft, prec);
|
||||
}
|
||||
}
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
case kpidATime:
|
||||
if (item->PaxTimes.ATime.IsDefined())
|
||||
PaxTimeToProp(item->PaxTimes.ATime, prop);
|
||||
break;
|
||||
case kpidCTime:
|
||||
if (item->PaxTimes.CTime.IsDefined())
|
||||
PaxTimeToProp(item->PaxTimes.CTime, prop);
|
||||
break;
|
||||
case kpidPosixAttrib: prop = item->Get_Combined_Mode(); break;
|
||||
|
||||
case kpidUser:
|
||||
if (!item->User.IsEmpty())
|
||||
TarStringToUnicode(item->User, prop);
|
||||
break;
|
||||
case kpidGroup:
|
||||
if (!item->Group.IsEmpty())
|
||||
TarStringToUnicode(item->Group, prop);
|
||||
break;
|
||||
|
||||
case kpidUserId:
|
||||
// if (item->UID != 0)
|
||||
prop = (UInt32)item->UID;
|
||||
break;
|
||||
case kpidGroupId:
|
||||
// if (item->GID != 0)
|
||||
prop = (UInt32)item->GID;
|
||||
break;
|
||||
|
||||
case kpidDeviceMajor:
|
||||
if (item->DeviceMajor_Defined)
|
||||
// if (item->DeviceMajor != 0)
|
||||
prop = (UInt32)item->DeviceMajor;
|
||||
break;
|
||||
|
||||
case kpidDeviceMinor:
|
||||
if (item->DeviceMinor_Defined)
|
||||
// if (item->DeviceMinor != 0)
|
||||
prop = (UInt32)item->DeviceMinor;
|
||||
break;
|
||||
/*
|
||||
case kpidDevice:
|
||||
if (item->DeviceMajor_Defined)
|
||||
if (item->DeviceMinor_Defined)
|
||||
prop = (UInt64)MY_dev_makedev(item->DeviceMajor, item->DeviceMinor);
|
||||
break;
|
||||
*/
|
||||
|
||||
case kpidSymLink:
|
||||
if (item->Is_SymLink())
|
||||
if (!item->LinkName.IsEmpty())
|
||||
TarStringToUnicode(item->LinkName, prop);
|
||||
break;
|
||||
case kpidHardLink:
|
||||
if (item->Is_HardLink())
|
||||
if (!item->LinkName.IsEmpty())
|
||||
TarStringToUnicode(item->LinkName, prop);
|
||||
break;
|
||||
|
||||
case kpidCharacts:
|
||||
{
|
||||
AString s;
|
||||
{
|
||||
s.Add_Space_if_NotEmpty();
|
||||
AddSpecCharToString(item->LinkFlag, s);
|
||||
}
|
||||
if (item->IsMagic_GNU())
|
||||
s.Add_OptSpaced("GNU");
|
||||
else if (item->IsMagic_Posix_ustar_00())
|
||||
s.Add_OptSpaced("POSIX");
|
||||
else
|
||||
{
|
||||
s.Add_Space_if_NotEmpty();
|
||||
for (unsigned i = 0; i < sizeof(item->Magic); i++)
|
||||
AddSpecCharToString(item->Magic[i], s);
|
||||
}
|
||||
|
||||
if (item->IsSignedChecksum)
|
||||
s.Add_OptSpaced("SignedChecksum");
|
||||
|
||||
if (item->Prefix_WasUsed)
|
||||
s.Add_OptSpaced(k_Characts_Prefix);
|
||||
|
||||
s.Add_OptSpaced(item->EncodingCharacts.GetCharactsString());
|
||||
|
||||
// AddSpecUInt64(s, "LongName", item->Num_LongName_Records);
|
||||
// AddSpecUInt64(s, "LongLink", item->Num_LongLink_Records);
|
||||
AddSpecBools(s, "LongName", item->LongName_WasUsed, item->LongName_WasUsed_2);
|
||||
AddSpecBools(s, "LongLink", item->LongLink_WasUsed, item->LongLink_WasUsed_2);
|
||||
|
||||
if (item->MTime_IsBin)
|
||||
s.Add_OptSpaced("bin_mtime");
|
||||
if (item->PackSize_IsBin)
|
||||
s.Add_OptSpaced("bin_psize");
|
||||
if (item->Size_IsBin)
|
||||
s.Add_OptSpaced("bin_size");
|
||||
|
||||
AddSpecUInt64(s, "PAX", item->Num_Pax_Records);
|
||||
|
||||
if (item->PaxTimes.MTime.IsDefined()) s.Add_OptSpaced("mtime");
|
||||
if (item->PaxTimes.ATime.IsDefined()) s.Add_OptSpaced("atime");
|
||||
if (item->PaxTimes.CTime.IsDefined()) s.Add_OptSpaced("ctime");
|
||||
|
||||
if (item->pax_path_WasUsed)
|
||||
s.Add_OptSpaced("pax_path");
|
||||
if (item->pax_link_WasUsed)
|
||||
s.Add_OptSpaced("pax_linkpath");
|
||||
if (item->pax_size_WasUsed)
|
||||
s.Add_OptSpaced("pax_size");
|
||||
|
||||
if (item->IsThereWarning())
|
||||
s.Add_OptSpaced("WARNING");
|
||||
if (item->HeaderError)
|
||||
s.Add_OptSpaced("ERROR");
|
||||
if (item->Pax_Error)
|
||||
s.Add_OptSpaced("PAX_error");
|
||||
if (!item->PaxExtra.RawLines.IsEmpty())
|
||||
s.Add_OptSpaced("PAX_unsupported_line");
|
||||
if (item->Pax_Overflow)
|
||||
s.Add_OptSpaced("PAX_overflow");
|
||||
if (!s.IsEmpty())
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
case kpidComment:
|
||||
{
|
||||
AString s;
|
||||
item->PaxExtra.Print_To_String(s);
|
||||
if (!s.IsEmpty())
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
// case kpidHeadersSize: prop = item->HeaderSize; break; // for debug
|
||||
// case kpidOffset: prop = item->HeaderPos; break; // for debug
|
||||
}
|
||||
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);
|
||||
const bool seqMode = (_stream == NULL);
|
||||
if (!seqMode)
|
||||
stream = _stream;
|
||||
|
||||
bool allFilesMode = (numItems == (UInt32)(Int32)-1);
|
||||
const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
|
||||
if (allFilesMode)
|
||||
numItems = _items.Size();
|
||||
if (_stream && numItems == 0)
|
||||
@@ -463,7 +682,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]].GetUnpackSize();
|
||||
totalSize += _items[allFilesMode ? i : indices[i]].Get_UnpackSize();
|
||||
extractCallback->SetTotal(totalSize);
|
||||
|
||||
UInt64 totalPackSize;
|
||||
@@ -503,9 +722,9 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||||
item = &_items[index];
|
||||
|
||||
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
||||
UInt64 unpackSize = item->GetUnpackSize();
|
||||
const UInt64 unpackSize = item->Get_UnpackSize();
|
||||
totalSize += unpackSize;
|
||||
totalPackSize += item->GetPackSizeAligned();
|
||||
totalPackSize += item->Get_PackSize_Aligned();
|
||||
if (item->IsDir())
|
||||
{
|
||||
RINOK(extractCallback->PrepareOperation(askMode));
|
||||
@@ -539,7 +758,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||||
|
||||
Int32 opRes = NExtract::NOperationResult::kOK;
|
||||
CMyComPtr<ISequentialInStream> inStream2;
|
||||
if (!item->IsSparse())
|
||||
if (!item->Is_Sparse())
|
||||
inStream2 = inStream;
|
||||
else
|
||||
{
|
||||
@@ -549,7 +768,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||||
}
|
||||
|
||||
{
|
||||
if (item->IsSymLink())
|
||||
if (item->Is_SymLink())
|
||||
{
|
||||
RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len()));
|
||||
}
|
||||
@@ -557,9 +776,9 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||||
{
|
||||
if (!seqMode)
|
||||
{
|
||||
RINOK(_stream->Seek((Int64)item->GetDataPosition(), STREAM_SEEK_SET, NULL));
|
||||
RINOK(_stream->Seek((Int64)item->Get_DataPos(), STREAM_SEEK_SET, NULL));
|
||||
}
|
||||
streamSpec->Init(item->GetPackSizeAligned());
|
||||
streamSpec->Init(item->Get_PackSize_Aligned());
|
||||
RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress));
|
||||
}
|
||||
if (outStreamSpec->GetRem() != 0)
|
||||
@@ -628,7 +847,7 @@ STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize)
|
||||
unsigned left = 0, right = item.SparseBlocks.Size();
|
||||
for (;;)
|
||||
{
|
||||
unsigned mid = (left + right) / 2;
|
||||
const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
|
||||
if (mid == left)
|
||||
break;
|
||||
if (_virtPos < item.SparseBlocks[mid].Offset)
|
||||
@@ -648,7 +867,7 @@ STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize)
|
||||
UInt64 phyPos = PhyOffsets[left] + relat;
|
||||
if (_needStartSeek || _phyPos != phyPos)
|
||||
{
|
||||
RINOK(Handler->_stream->Seek((Int64)(item.GetDataPosition() + phyPos), STREAM_SEEK_SET, NULL));
|
||||
RINOK(Handler->_stream->Seek((Int64)(item.Get_DataPos() + phyPos), STREAM_SEEK_SET, NULL));
|
||||
_needStartSeek = false;
|
||||
_phyPos = phyPos;
|
||||
}
|
||||
@@ -698,7 +917,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
|
||||
|
||||
const CItemEx &item = _items[index];
|
||||
|
||||
if (item.IsSparse())
|
||||
if (item.Is_Sparse())
|
||||
{
|
||||
CSparseStream *streamSpec = new CSparseStream;
|
||||
CMyComPtr<IInStream> streamTemp = streamSpec;
|
||||
@@ -718,24 +937,30 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (item.IsSymLink())
|
||||
if (item.Is_SymLink())
|
||||
{
|
||||
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);
|
||||
return CreateLimitedInStream(_stream, item.Get_DataPos(), item.PackSize, stream);
|
||||
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
|
||||
void CHandler::Init()
|
||||
{
|
||||
_forceCodePage = false;
|
||||
_curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP;
|
||||
_thereIsPaxExtendedHeader = false;
|
||||
_posixMode = false;
|
||||
_posixMode_WasForced = false;
|
||||
// TimeOptions.Clear();
|
||||
_handlerTimeOptions.Init();
|
||||
// _handlerTimeOptions.Write_MTime.Val = true; // it's default already
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
|
||||
{
|
||||
Init();
|
||||
@@ -768,8 +993,54 @@ STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVAR
|
||||
else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
|
||||
{
|
||||
}
|
||||
else if (name.IsEqualTo("m"))
|
||||
{
|
||||
if (prop.vt != VT_BSTR)
|
||||
return E_INVALIDARG;
|
||||
const UString s = prop.bstrVal;
|
||||
if (s.IsEqualTo_Ascii_NoCase("pax") ||
|
||||
s.IsEqualTo_Ascii_NoCase("posix"))
|
||||
_posixMode = true;
|
||||
else if (s.IsEqualTo_Ascii_NoCase("gnu"))
|
||||
_posixMode = false;
|
||||
else
|
||||
return E_INVALIDARG;
|
||||
_posixMode_WasForced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
if (name.IsPrefixedBy_Ascii_NoCase("td"))
|
||||
{
|
||||
name.Delete(0, 3);
|
||||
if (prop.vt == VT_EMPTY)
|
||||
{
|
||||
if (name.IsEqualTo_Ascii_NoCase("n"))
|
||||
{
|
||||
// TimeOptions.UseNativeDigits = true;
|
||||
}
|
||||
else if (name.IsEqualTo_Ascii_NoCase("r"))
|
||||
{
|
||||
// TimeOptions.RemoveZeroDigits = true;
|
||||
}
|
||||
else
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 numTimeDigits = 0;
|
||||
RINOK(ParsePropToUInt32(name, prop, numTimeDigits));
|
||||
TimeOptions.NumDigits_WasForced = true;
|
||||
TimeOptions.NumDigits = numTimeDigits;
|
||||
}
|
||||
}
|
||||
*/
|
||||
bool processed = false;
|
||||
RINOK(_handlerTimeOptions.Parse(name, prop, processed));
|
||||
if (processed)
|
||||
continue;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "../../Compress/CopyCoder.h"
|
||||
|
||||
#include "../IArchive.h"
|
||||
#include "../Common/HandlerOut.h"
|
||||
|
||||
#include "TarIn.h"
|
||||
|
||||
@@ -29,31 +29,26 @@ public:
|
||||
CMyComPtr<IInStream> _stream;
|
||||
CMyComPtr<ISequentialInStream> _seqStream;
|
||||
private:
|
||||
UInt32 _curIndex;
|
||||
bool _latestIsRead;
|
||||
CItemEx _latestItem;
|
||||
|
||||
UInt64 _phySize;
|
||||
UInt64 _headersSize;
|
||||
bool _phySizeDefined;
|
||||
EErrorType _error;
|
||||
bool _warning;
|
||||
bool _isArc;
|
||||
|
||||
// bool _isSparse;
|
||||
bool _thereIsPaxExtendedHeader;
|
||||
|
||||
bool _posixMode_WasForced;
|
||||
bool _posixMode;
|
||||
bool _forceCodePage;
|
||||
UInt32 _specifiedCodePage;
|
||||
UInt32 _curCodePage;
|
||||
UInt32 _openCodePage;
|
||||
|
||||
// CTimeOptions TimeOptions;
|
||||
CHandlerTimeOptions _handlerTimeOptions;
|
||||
CEncodingCharacts _encodingCharacts;
|
||||
|
||||
UInt32 _curIndex;
|
||||
bool _latestIsRead;
|
||||
CItemEx _latestItem;
|
||||
|
||||
CArchive _arc;
|
||||
|
||||
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;
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
// #include <stdio.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 "../Common/ItemNameUtils.h"
|
||||
|
||||
#include "TarHandler.h"
|
||||
#include "TarUpdate.h"
|
||||
|
||||
@@ -21,10 +22,35 @@ namespace NTar {
|
||||
|
||||
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
|
||||
{
|
||||
*type = NFileTimeType::kUnix;
|
||||
UInt32 t = NFileTimeType::kUnix;
|
||||
const UInt32 prec = _handlerTimeOptions.Prec;
|
||||
if (prec != (UInt32)(Int32)-1)
|
||||
{
|
||||
t = NFileTimeType::kWindows;
|
||||
if (prec == k_PropVar_TimePrec_0 ||
|
||||
prec == k_PropVar_TimePrec_100ns)
|
||||
t = NFileTimeType::kWindows;
|
||||
else if (prec == k_PropVar_TimePrec_HighPrec)
|
||||
t = k_PropVar_TimePrec_1ns;
|
||||
else if (prec >= k_PropVar_TimePrec_Base)
|
||||
t = prec;
|
||||
}
|
||||
// 7-Zip before 22.00 fails, if unknown typeType.
|
||||
*type = t;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void Get_AString_From_UString(const UString &s, AString &res,
|
||||
UINT codePage, unsigned utfFlags)
|
||||
{
|
||||
if (codePage == CP_UTF8)
|
||||
ConvertUnicodeToUTF8_Flags(s, res, utfFlags);
|
||||
else
|
||||
UnicodeStringToMultiByte2(res, s, codePage);
|
||||
}
|
||||
|
||||
|
||||
HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
|
||||
UINT codePage, unsigned utfFlags, bool convertSlash)
|
||||
{
|
||||
@@ -36,14 +62,7 @@ HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID pro
|
||||
UString s = prop.bstrVal;
|
||||
if (convertSlash)
|
||||
NItemName::ReplaceSlashes_OsToUnix(s);
|
||||
|
||||
if (codePage == CP_UTF8)
|
||||
{
|
||||
ConvertUnicodeToUTF8_Flags(s, res, utfFlags);
|
||||
// if (!ConvertUnicodeToUTF8(s, res)) // return E_INVALIDARG;
|
||||
}
|
||||
else
|
||||
UnicodeStringToMultiByte2(res, s, codePage);
|
||||
Get_AString_From_UString(s, res, codePage, utfFlags);
|
||||
}
|
||||
else if (prop.vt != VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
@@ -70,12 +89,106 @@ static int CompareUpdateItems(void *const *p1, void *const *p2, void *)
|
||||
}
|
||||
|
||||
|
||||
static HRESULT GetTime(UInt32 i, UInt32 pid, IArchiveUpdateCallback *callback,
|
||||
CPaxTime &pt)
|
||||
{
|
||||
pt.Clear();
|
||||
NCOM::CPropVariant prop;
|
||||
RINOK(callback->GetProperty(i, pid, &prop));
|
||||
return Prop_To_PaxTime(prop, pt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
|
||||
UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined)
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(callback->GetProperty(i, kpidDevice, &prop));
|
||||
if (prop.vt == VT_EMPTY)
|
||||
return S_OK;
|
||||
if (prop.vt != VT_UI8)
|
||||
return E_INVALIDARG;
|
||||
{
|
||||
const UInt64 v = prop.uhVal.QuadPart;
|
||||
majo = MY_dev_major(v);
|
||||
mino = MY_dev_minor(v);
|
||||
majo_defined = true;
|
||||
mino_defined = true;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
*/
|
||||
|
||||
static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
|
||||
UInt32 pid, UInt32 &id, bool &defined)
|
||||
{
|
||||
defined = false;
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(callback->GetProperty(i, pid, &prop));
|
||||
if (prop.vt == VT_EMPTY)
|
||||
return S_OK;
|
||||
if (prop.vt == VT_UI4)
|
||||
{
|
||||
id = prop.ulVal;
|
||||
defined = true;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
|
||||
static HRESULT GetUser(IArchiveUpdateCallback *callback, UInt32 i,
|
||||
UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id,
|
||||
UINT codePage, unsigned utfFlags)
|
||||
{
|
||||
// printf("\ncallback->GetProperty(i, pidId, &prop))\n");
|
||||
|
||||
bool isSet = false;
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(callback->GetProperty(i, pidId, &prop));
|
||||
if (prop.vt == VT_UI4)
|
||||
{
|
||||
isSet = true;
|
||||
id = prop.ulVal;
|
||||
// printf("\ncallback->GetProperty(i, pidId, &prop)); = %d \n", (unsigned)id);
|
||||
name.Empty();
|
||||
}
|
||||
else if (prop.vt != VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(callback->GetProperty(i, pidName, &prop));
|
||||
if (prop.vt == VT_BSTR)
|
||||
{
|
||||
const UString s = prop.bstrVal;
|
||||
Get_AString_From_UString(s, name, codePage, utfFlags);
|
||||
if (!isSet)
|
||||
id = 0;
|
||||
}
|
||||
else if (prop.vt == VT_UI4)
|
||||
{
|
||||
id = prop.ulVal;
|
||||
name.Empty();
|
||||
}
|
||||
else if (prop.vt != VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
|
||||
IArchiveUpdateCallback *callback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
|
||||
if ((_stream && (_error != k_ErrorType_OK || _warning /* || _isSparse */)) || _seqStream)
|
||||
if ((_stream && (_arc._error != k_ErrorType_OK || _arc._is_Warning
|
||||
/* || _isSparse */
|
||||
)) || _seqStream)
|
||||
return E_NOTIMPL;
|
||||
CObjectVector<CUpdateItem> updateItems;
|
||||
const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage);
|
||||
@@ -131,25 +244,30 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt
|
||||
else
|
||||
ui.Mode = prop.ulVal;
|
||||
// 21.07 : we clear high file type bits as GNU TAR.
|
||||
ui.Mode &= ~(UInt32)MY_LIN_S_IFMT;
|
||||
// we will clear it later
|
||||
// ui.Mode &= ~(UInt32)MY_LIN_S_IFMT;
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (_handlerTimeOptions.Write_MTime.Val)
|
||||
RINOK(GetTime(i, kpidMTime, callback, ui.PaxTimes.MTime))
|
||||
if (_handlerTimeOptions.Write_ATime.Val)
|
||||
RINOK(GetTime(i, kpidATime, callback, ui.PaxTimes.ATime))
|
||||
if (_handlerTimeOptions.Write_CTime.Val)
|
||||
RINOK(GetTime(i, kpidCTime, callback, ui.PaxTimes.CTime))
|
||||
|
||||
RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true));
|
||||
if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
|
||||
ui.Name += '/';
|
||||
RINOK(GetPropString(callback, i, kpidUser, ui.User, codePage, utfFlags, false));
|
||||
RINOK(GetPropString(callback, i, kpidGroup, ui.Group, codePage, utfFlags, false));
|
||||
// ui.Name += '/'; // for debug
|
||||
|
||||
if (_posixMode)
|
||||
{
|
||||
RINOK(GetDevice(callback, i, kpidDeviceMajor, ui.DeviceMajor, ui.DeviceMajor_Defined));
|
||||
RINOK(GetDevice(callback, i, kpidDeviceMinor, ui.DeviceMinor, ui.DeviceMinor_Defined));
|
||||
}
|
||||
|
||||
RINOK(GetUser(callback, i, kpidUser, kpidUserId, ui.User, ui.UID, codePage, utfFlags));
|
||||
RINOK(GetUser(callback, i, kpidGroup, kpidGroupId, ui.Group, ui.GID, codePage, utfFlags));
|
||||
}
|
||||
|
||||
if (IntToBool(newData))
|
||||
@@ -169,13 +287,44 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt
|
||||
updateItems.Add(ui);
|
||||
}
|
||||
|
||||
if (_thereIsPaxExtendedHeader)
|
||||
if (_arc._are_Pax_Items)
|
||||
{
|
||||
// we restore original order of files, if there is pax header block
|
||||
// we restore original order of files, if there are pax items
|
||||
updateItems.Sort(CompareUpdateItems, NULL);
|
||||
}
|
||||
|
||||
CUpdateOptions options;
|
||||
|
||||
options.CodePage = codePage;
|
||||
options.UtfFlags = utfFlags;
|
||||
options.PosixMode = _posixMode;
|
||||
|
||||
return UpdateArchive(_stream, outStream, _items, updateItems, codePage, utfFlags, callback);
|
||||
options.Write_MTime = _handlerTimeOptions.Write_MTime;
|
||||
options.Write_ATime = _handlerTimeOptions.Write_ATime;
|
||||
options.Write_CTime = _handlerTimeOptions.Write_CTime;
|
||||
|
||||
// options.TimeOptions = TimeOptions;
|
||||
|
||||
const UInt32 prec = _handlerTimeOptions.Prec;
|
||||
if (prec != (UInt32)(Int32)-1)
|
||||
{
|
||||
unsigned numDigits = 0;
|
||||
if (prec == 0)
|
||||
numDigits = 7;
|
||||
else if (prec == k_PropVar_TimePrec_HighPrec
|
||||
|| prec >= k_PropVar_TimePrec_1ns)
|
||||
numDigits = 9;
|
||||
else if (prec >= k_PropVar_TimePrec_Base)
|
||||
numDigits = prec - k_PropVar_TimePrec_Base;
|
||||
options.TimeOptions.NumDigitsMax = numDigits;
|
||||
// options.TimeOptions.RemoveZeroMode =
|
||||
// k_PaxTimeMode_DontRemoveZero; // pure for debug
|
||||
// k_PaxTimeMode_RemoveZero_if_PureSecondOnly; // optimized code
|
||||
// k_PaxTimeMode_RemoveZero_Always; // original pax code
|
||||
}
|
||||
|
||||
return UpdateArchive(_stream, outStream, _items, updateItems,
|
||||
options, callback);
|
||||
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
@@ -18,9 +18,82 @@ namespace NFileHeader {
|
||||
// const char * const kGNUTar = "GNUtar "; // 7 chars and a null
|
||||
// const char * const kEmpty = "\0\0\0\0\0\0\0\0";
|
||||
// 7-Zip used kUsTar_00 before 21.07:
|
||||
// const char kUsTar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ;
|
||||
const char k_Posix_ustar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ;
|
||||
// GNU TAR uses such header:
|
||||
const char kUsTar_GNU[8] = { 'u', 's', 't', 'a', 'r', ' ', ' ', 0 } ;
|
||||
const char k_GNU_ustar__[8] = { 'u', 's', 't', 'a', 'r', ' ', ' ', 0 } ;
|
||||
}
|
||||
|
||||
/*
|
||||
pre-POSIX.1-1988 (i.e. v7) tar header:
|
||||
-----
|
||||
Link indicator:
|
||||
'0' or 0 : Normal file
|
||||
'1' : Hard link
|
||||
'2' : Symbolic link
|
||||
Some pre-POSIX.1-1988 tar implementations indicated a directory by having
|
||||
a trailing slash (/) in the name.
|
||||
|
||||
Numeric values : octal with leading zeroes.
|
||||
For historical reasons, a final NUL or space character should also be used.
|
||||
Thus only 11 octal digits can be stored from 12 bytes field.
|
||||
|
||||
2001 star : introduced a base-256 coding that is indicated by
|
||||
setting the high-order bit of the leftmost byte of a numeric field.
|
||||
GNU-tar and BSD-tar followed this idea.
|
||||
|
||||
versions of tar from before the first POSIX standard from 1988
|
||||
pad the values with spaces instead of zeroes.
|
||||
|
||||
UStar
|
||||
-----
|
||||
UStar (Unix Standard TAR) : POSIX IEEE P1003.1 : 1988.
|
||||
257 signature: "ustar", 0, "00"
|
||||
265 32 Owner user name
|
||||
297 32 Owner group name
|
||||
329 8 Device major number
|
||||
337 8 Device minor number
|
||||
345 155 Filename prefix
|
||||
|
||||
POSIX.1-2001/pax
|
||||
----
|
||||
format is known as extended tar format or pax format
|
||||
vendor-tagged vendor-specific enhancements.
|
||||
tags Defined by the POSIX standard:
|
||||
atime, mtime, path, linkpath, uname, gname, size, uid, gid, ...
|
||||
|
||||
|
||||
PAX EXTENSION
|
||||
-----------
|
||||
Hard links
|
||||
A further difference from the ustar header block is that data blocks
|
||||
for files of typeflag 1 (hard link) may be included,
|
||||
which means that the size field may be greater than zero.
|
||||
Archives created by pax -o linkdata shall include these data
|
||||
blocks with the hard links.
|
||||
*
|
||||
|
||||
compatiblity
|
||||
------------
|
||||
7-Zip 16.03 supports "PaxHeader/"
|
||||
7-Zip 20.01 supports "PaxHeaders.X/" with optional "./"
|
||||
7-Zip 21.02 supports "@PaxHeader" with optional "./" "./"
|
||||
|
||||
GNU tar --format=posix uses "PaxHeaders/" in folder of file
|
||||
|
||||
|
||||
GNU TAR format
|
||||
==============
|
||||
v7 - Unix V7
|
||||
oldgnu - GNU tar <=1.12 : writes zero in last character in name
|
||||
gnu - GNU tar 1.13 : doesn't write zero in last character in name
|
||||
as 7-zip 21.07
|
||||
ustar - POSIX.1-1988
|
||||
posix (pax) - POSIX.1-2001
|
||||
|
||||
gnu tar:
|
||||
if (S_ISCHR (st->stat.st_mode) || S_ISBLK (st->stat.st_mode)) {
|
||||
major_t devmajor = major (st->stat.st_rdev);
|
||||
minor_t devminor = minor (st->stat.st_rdev); }
|
||||
*/
|
||||
|
||||
}}}
|
||||
|
||||
@@ -59,6 +59,9 @@ namespace NFileHeader
|
||||
const char kGnu_LongName = 'L';
|
||||
const char kSparse = 'S';
|
||||
const char kLabel = 'V';
|
||||
const char kPax = 'x'; // Extended header with meta data for the next file in the archive (POSIX.1-2001)
|
||||
const char kPax_2 = 'X';
|
||||
const char kGlobal = 'g'; // Global extended header with meta data (POSIX.1-2001)
|
||||
const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR.
|
||||
data: list of files created by the --incremental (-G) option
|
||||
Each file name is preceded by either
|
||||
@@ -66,6 +69,7 @@ namespace NFileHeader
|
||||
- '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. */
|
||||
// 'A'-'Z' Vendor specific extensions (POSIX.1-1988)
|
||||
}
|
||||
|
||||
extern const char * const kLongLink; // = "././@LongLink";
|
||||
@@ -76,8 +80,8 @@ namespace NFileHeader
|
||||
// extern const char * const kUsTar; // = "ustar"; // 5 chars
|
||||
// extern const char * const kGNUTar; // = "GNUtar "; // 7 chars and a null
|
||||
// extern const char * const kEmpty; // = "\0\0\0\0\0\0\0\0"
|
||||
// extern const char kUsTar_00[8];
|
||||
extern const char kUsTar_GNU[8];
|
||||
extern const char k_Posix_ustar_00[8];
|
||||
extern const char k_GNU_ustar__[8];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
#ifndef __ARCHIVE_TAR_IN_H
|
||||
#define __ARCHIVE_TAR_IN_H
|
||||
|
||||
#include "../../IStream.h"
|
||||
#include "../IArchive.h"
|
||||
|
||||
#include "TarItem.h"
|
||||
|
||||
@@ -14,11 +14,133 @@ enum EErrorType
|
||||
{
|
||||
k_ErrorType_OK,
|
||||
k_ErrorType_Corrupted,
|
||||
k_ErrorType_UnexpectedEnd,
|
||||
k_ErrorType_Warning
|
||||
k_ErrorType_UnexpectedEnd
|
||||
// , k_ErrorType_Warning
|
||||
};
|
||||
|
||||
|
||||
struct CTempBuffer
|
||||
{
|
||||
CByteBuffer Buffer;
|
||||
size_t StringSize; // num characters before zero Byte (StringSize <= item.PackSize)
|
||||
bool IsNonZeroTail;
|
||||
bool StringSize_IsConfirmed;
|
||||
|
||||
void CopyToString(AString &s)
|
||||
{
|
||||
s.Empty();
|
||||
if (StringSize != 0)
|
||||
s.SetFrom((const char *)(const void *)(const Byte *)Buffer, (unsigned)StringSize);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
StringSize = 0;
|
||||
IsNonZeroTail = false;
|
||||
StringSize_IsConfirmed = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class CArchive
|
||||
{
|
||||
public:
|
||||
bool _phySize_Defined;
|
||||
bool _is_Warning;
|
||||
bool PaxGlobal_Defined;
|
||||
bool _is_PaxGlobal_Error;
|
||||
bool _are_Pax_Items;
|
||||
bool _are_Gnu;
|
||||
bool _are_Posix;
|
||||
bool _are_Pax;
|
||||
bool _are_mtime;
|
||||
bool _are_atime;
|
||||
bool _are_ctime;
|
||||
bool _are_pax_path;
|
||||
bool _are_pax_link;
|
||||
bool _are_LongName;
|
||||
bool _are_LongLink;
|
||||
bool _pathPrefix_WasUsed;
|
||||
// bool _isSparse;
|
||||
|
||||
// temp internal vars for ReadItem():
|
||||
bool filled;
|
||||
private:
|
||||
EErrorType error;
|
||||
|
||||
public:
|
||||
UInt64 _phySize;
|
||||
UInt64 _headersSize;
|
||||
EErrorType _error;
|
||||
|
||||
ISequentialInStream *SeqStream;
|
||||
IInStream *InStream;
|
||||
IArchiveOpenCallback *OpenCallback;
|
||||
UInt64 NumFiles;
|
||||
UInt64 NumFiles_Prev;
|
||||
UInt64 Pos_Prev;
|
||||
// UInt64 NumRecords;
|
||||
// UInt64 NumRecords_Prev;
|
||||
|
||||
CPaxExtra PaxGlobal;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
SeqStream = NULL;
|
||||
InStream = NULL;
|
||||
OpenCallback = NULL;
|
||||
NumFiles = 0;
|
||||
NumFiles_Prev = 0;
|
||||
Pos_Prev = 0;
|
||||
// NumRecords = 0;
|
||||
// NumRecords_Prev = 0;
|
||||
|
||||
PaxGlobal.Clear();
|
||||
PaxGlobal_Defined = false;
|
||||
_is_PaxGlobal_Error = false;
|
||||
_are_Pax_Items = false; // if there are final paxItems
|
||||
_are_Gnu = false;
|
||||
_are_Posix = false;
|
||||
_are_Pax = false;
|
||||
_are_mtime = false;
|
||||
_are_atime = false;
|
||||
_are_ctime = false;
|
||||
_are_pax_path = false;
|
||||
_are_pax_link = false;
|
||||
_are_LongName = false;
|
||||
_are_LongLink = false;
|
||||
_pathPrefix_WasUsed = false;
|
||||
// _isSparse = false;
|
||||
|
||||
_is_Warning = false;
|
||||
_error = k_ErrorType_OK;
|
||||
|
||||
_phySize_Defined = false;
|
||||
_phySize = 0;
|
||||
_headersSize = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
CTempBuffer NameBuf;
|
||||
CTempBuffer LinkBuf;
|
||||
CTempBuffer PaxBuf;
|
||||
CTempBuffer PaxBuf_global;
|
||||
|
||||
CByteBuffer Buffer;
|
||||
|
||||
HRESULT ReadDataToBuffer(const CItemEx &item, CTempBuffer &tb, size_t stringLimit);
|
||||
HRESULT Progress(const CItemEx &item, UInt64 posOffset);
|
||||
HRESULT GetNextItemReal(CItemEx &item);
|
||||
HRESULT ReadItem2(CItemEx &itemInfo);
|
||||
public:
|
||||
CArchive()
|
||||
{
|
||||
// we will call Clear() in CHandler::Close().
|
||||
// Clear(); // it's not required here
|
||||
}
|
||||
HRESULT ReadItem(CItemEx &itemInfo);
|
||||
};
|
||||
|
||||
HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, EErrorType &error);
|
||||
|
||||
API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size);
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "../../../Common/MyLinux.h"
|
||||
#include "../../../Common/UTFConvert.h"
|
||||
|
||||
#include "../Common/ItemNameUtils.h"
|
||||
|
||||
#include "TarHeader.h"
|
||||
|
||||
namespace NArchive {
|
||||
@@ -19,50 +17,149 @@ struct CSparseBlock
|
||||
UInt64 Size;
|
||||
};
|
||||
|
||||
|
||||
enum EPaxTimeRemoveZeroMode
|
||||
{
|
||||
k_PaxTimeMode_DontRemoveZero,
|
||||
k_PaxTimeMode_RemoveZero_if_PureSecondOnly,
|
||||
k_PaxTimeMode_RemoveZero_Always
|
||||
};
|
||||
|
||||
struct CTimeOptions
|
||||
{
|
||||
EPaxTimeRemoveZeroMode RemoveZeroMode;
|
||||
unsigned NumDigitsMax;
|
||||
|
||||
void Init()
|
||||
{
|
||||
RemoveZeroMode = k_PaxTimeMode_RemoveZero_if_PureSecondOnly;
|
||||
NumDigitsMax = 0;
|
||||
}
|
||||
CTimeOptions() { Init(); }
|
||||
};
|
||||
|
||||
|
||||
struct CPaxTime
|
||||
{
|
||||
Int32 NumDigits; // -1 means undefined
|
||||
UInt32 Ns; // it's smaller than 1G. Even if (Sec < 0), larger (Ns) value means newer files.
|
||||
Int64 Sec; // can be negative
|
||||
|
||||
Int64 GetSec() const { return NumDigits != -1 ? Sec : 0; }
|
||||
|
||||
bool IsDefined() const { return NumDigits != -1; }
|
||||
// bool IsDefined_And_nonZero() const { return NumDigits != -1 && (Sec != 0 || Ns != 0); }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
NumDigits = -1;
|
||||
Ns = 0;
|
||||
Sec = 0;
|
||||
}
|
||||
CPaxTime() { Clear(); }
|
||||
|
||||
/*
|
||||
void ReducePrecison(int numDigits)
|
||||
{
|
||||
// we don't use this->NumDigits here
|
||||
if (numDigits > 0)
|
||||
{
|
||||
if (numDigits >= 9)
|
||||
return;
|
||||
UInt32 r = 1;
|
||||
for (unsigned i = numDigits; i < 9; i++)
|
||||
r *= 10;
|
||||
Ns /= r;
|
||||
Ns *= r;
|
||||
return;
|
||||
}
|
||||
Ns = 0;
|
||||
if (numDigits == 0)
|
||||
return;
|
||||
UInt32 r;
|
||||
if (numDigits == -1) r = 60;
|
||||
else if (numDigits == -2) r = 60 * 60;
|
||||
else if (numDigits == -3) r = 60 * 60 * 24;
|
||||
else return;
|
||||
Sec /= r;
|
||||
Sec *= r;
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
struct CPaxTimes
|
||||
{
|
||||
CPaxTime MTime;
|
||||
CPaxTime ATime;
|
||||
CPaxTime CTime;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
MTime.Clear();
|
||||
ATime.Clear();
|
||||
CTime.Clear();
|
||||
}
|
||||
|
||||
/*
|
||||
void ReducePrecison(int numDigits)
|
||||
{
|
||||
MTime.ReducePrecison(numDigits);
|
||||
CTime.ReducePrecison(numDigits);
|
||||
ATime.ReducePrecison(numDigits);
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
struct CItem
|
||||
{
|
||||
AString Name;
|
||||
UInt64 PackSize;
|
||||
UInt64 Size;
|
||||
Int64 MTime;
|
||||
|
||||
char LinkFlag;
|
||||
bool DeviceMajor_Defined;
|
||||
bool DeviceMinor_Defined;
|
||||
|
||||
UInt32 Mode;
|
||||
UInt32 UID;
|
||||
UInt32 GID;
|
||||
UInt32 DeviceMajor;
|
||||
UInt32 DeviceMinor;
|
||||
|
||||
AString Name;
|
||||
AString LinkName;
|
||||
AString User;
|
||||
AString Group;
|
||||
|
||||
char Magic[8];
|
||||
char LinkFlag;
|
||||
bool DeviceMajorDefined;
|
||||
bool DeviceMinorDefined;
|
||||
|
||||
CPaxTimes PaxTimes;
|
||||
|
||||
CRecordVector<CSparseBlock> SparseBlocks;
|
||||
|
||||
void SetDefaultWriteFields()
|
||||
void SetMagic_Posix(bool posixMode)
|
||||
{
|
||||
DeviceMajorDefined = false;
|
||||
DeviceMinorDefined = false;
|
||||
UID = 0;
|
||||
GID = 0;
|
||||
memcpy(Magic, NFileHeader::NMagic::kUsTar_GNU, 8);
|
||||
memcpy(Magic, posixMode ?
|
||||
NFileHeader::NMagic::k_Posix_ustar_00 :
|
||||
NFileHeader::NMagic::k_GNU_ustar__,
|
||||
8);
|
||||
}
|
||||
|
||||
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
|
||||
bool Is_SymLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymLink && (Size == 0); }
|
||||
bool Is_HardLink() const { return LinkFlag == NFileHeader::NLinkFlag::kHardLink; }
|
||||
bool Is_Sparse() const { return LinkFlag == NFileHeader::NLinkFlag::kSparse; }
|
||||
|
||||
UInt64 Get_UnpackSize() const { return Is_SymLink() ? LinkName.Len() : Size; }
|
||||
|
||||
bool Is_PaxExtendedHeader() const
|
||||
{
|
||||
switch (LinkFlag)
|
||||
{
|
||||
case 'g':
|
||||
case 'x':
|
||||
case 'X': // Check it
|
||||
case NFileHeader::NLinkFlag::kPax:
|
||||
case NFileHeader::NLinkFlag::kPax_2:
|
||||
case NFileHeader::NLinkFlag::kGlobal:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -72,6 +169,17 @@ struct CItem
|
||||
{
|
||||
return (Mode & ~(UInt32)MY_LIN_S_IFMT) | Get_FileTypeMode_from_LinkFlag();
|
||||
}
|
||||
|
||||
void Set_LinkFlag_for_File(UInt32 mode)
|
||||
{
|
||||
Byte lf = NFileHeader::NLinkFlag::kNormal;
|
||||
if (MY_LIN_S_ISCHR(mode)) lf = NFileHeader::NLinkFlag::kCharacter;
|
||||
else if (MY_LIN_S_ISBLK(mode)) lf = NFileHeader::NLinkFlag::kBlock;
|
||||
else if (MY_LIN_S_ISFIFO(mode)) lf = NFileHeader::NLinkFlag::kFIFO;
|
||||
// else if (MY_LIN_S_ISDIR(mode)) lf = NFileHeader::NLinkFlag::kDirectory;
|
||||
// else if (MY_LIN_S_ISLNK(mode)) lf = NFileHeader::NLinkFlag::kSymLink;
|
||||
LinkFlag = lf;
|
||||
}
|
||||
|
||||
UInt32 Get_FileTypeMode_from_LinkFlag() const
|
||||
{
|
||||
@@ -82,10 +190,10 @@ struct CItem
|
||||
case NFileHeader::NLinkFlag::kDumpDir:
|
||||
return MY_LIN_S_IFDIR;
|
||||
*/
|
||||
case NFileHeader::NLinkFlag::kSymLink: return MY_LIN_S_IFLNK;
|
||||
case NFileHeader::NLinkFlag::kBlock: return MY_LIN_S_IFBLK;
|
||||
case NFileHeader::NLinkFlag::kCharacter: return MY_LIN_S_IFCHR;
|
||||
case NFileHeader::NLinkFlag::kFIFO: return MY_LIN_S_IFIFO;
|
||||
case NFileHeader::NLinkFlag::kSymLink: return MY_LIN_S_IFLNK;
|
||||
case NFileHeader::NLinkFlag::kBlock: return MY_LIN_S_IFBLK;
|
||||
case NFileHeader::NLinkFlag::kCharacter: return MY_LIN_S_IFCHR;
|
||||
case NFileHeader::NLinkFlag::kFIFO: return MY_LIN_S_IFIFO;
|
||||
// case return MY_LIN_S_IFSOCK;
|
||||
}
|
||||
|
||||
@@ -104,20 +212,41 @@ struct CItem
|
||||
case NFileHeader::NLinkFlag::kOldNormal:
|
||||
case NFileHeader::NLinkFlag::kNormal:
|
||||
case NFileHeader::NLinkFlag::kSymLink:
|
||||
return NItemName::HasTailSlash(Name, CP_OEMCP);
|
||||
if (Name.IsEmpty())
|
||||
return false;
|
||||
// GNU TAR uses last character as directory marker
|
||||
// we also do it
|
||||
return Name.Back() == '/';
|
||||
// return NItemName::HasTailSlash(Name, CP_OEMCP);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsUstarMagic() const
|
||||
bool IsMagic_ustar_5chars() const
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
if (Magic[i] != NFileHeader::NMagic::kUsTar_GNU[i])
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
if (Magic[i] != NFileHeader::NMagic::k_GNU_ustar__[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt64 GetPackSizeAligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); }
|
||||
bool IsMagic_Posix_ustar_00() const
|
||||
{
|
||||
for (unsigned i = 0; i < 8; i++)
|
||||
if (Magic[i] != NFileHeader::NMagic::k_Posix_ustar_00[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsMagic_GNU() const
|
||||
{
|
||||
for (unsigned i = 0; i < 8; i++)
|
||||
if (Magic[i] != NFileHeader::NMagic::k_GNU_ustar__[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt64 Get_PackSize_Aligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); }
|
||||
|
||||
bool IsThereWarning() const
|
||||
{
|
||||
@@ -163,18 +292,67 @@ struct CEncodingCharacts
|
||||
};
|
||||
|
||||
|
||||
struct CPaxExtra
|
||||
{
|
||||
AString RecordPath;
|
||||
AString RawLines;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
RecordPath.Empty();
|
||||
RawLines.Empty();
|
||||
}
|
||||
|
||||
void Print_To_String(AString &s) const
|
||||
{
|
||||
if (!RecordPath.IsEmpty())
|
||||
{
|
||||
s += RecordPath;
|
||||
s.Add_LF();
|
||||
}
|
||||
if (!RawLines.IsEmpty())
|
||||
s += RawLines;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CItemEx: public CItem
|
||||
{
|
||||
bool HeaderError;
|
||||
|
||||
bool IsSignedChecksum;
|
||||
bool Prefix_WasUsed;
|
||||
|
||||
bool Pax_Error;
|
||||
bool Pax_Overflow;
|
||||
bool pax_path_WasUsed;
|
||||
bool pax_link_WasUsed;
|
||||
bool pax_size_WasUsed;
|
||||
|
||||
bool MTime_IsBin;
|
||||
bool PackSize_IsBin;
|
||||
bool Size_IsBin;
|
||||
|
||||
bool LongName_WasUsed;
|
||||
bool LongName_WasUsed_2;
|
||||
|
||||
bool LongLink_WasUsed;
|
||||
bool LongLink_WasUsed_2;
|
||||
|
||||
// bool Name_CouldBeReduced;
|
||||
// bool LinkName_CouldBeReduced;
|
||||
|
||||
UInt64 HeaderPos;
|
||||
unsigned HeaderSize;
|
||||
bool NameCouldBeReduced;
|
||||
bool LinkNameCouldBeReduced;
|
||||
UInt64 HeaderSize;
|
||||
|
||||
UInt64 Num_Pax_Records;
|
||||
CPaxExtra PaxExtra;
|
||||
|
||||
CEncodingCharacts EncodingCharacts;
|
||||
|
||||
UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
|
||||
UInt64 GetFullSize() const { return HeaderSize + PackSize; }
|
||||
UInt64 Get_DataPos() const { return HeaderPos + HeaderSize; }
|
||||
// UInt64 GetFullSize() const { return HeaderSize + PackSize; }
|
||||
UInt64 Get_FullSize_Aligned() const { return HeaderSize + Get_PackSize_Aligned(); }
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "../../../../C/7zCrc.h"
|
||||
|
||||
#include "../../../Common/IntToString.h"
|
||||
|
||||
#include "../../Common/StreamUtils.h"
|
||||
|
||||
#include "TarOut.h"
|
||||
@@ -9,23 +13,27 @@
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
HRESULT COutArchive::WriteBytes(const void *data, unsigned size)
|
||||
{
|
||||
Pos += size;
|
||||
return WriteStream(m_Stream, data, size);
|
||||
}
|
||||
using namespace NFileHeader;
|
||||
|
||||
static bool WriteOctal_8(char *s, UInt32 val)
|
||||
// it's path prefix assigned by 7-Zip to show that file path was cut
|
||||
#define K_PREFIX_PATH_CUT "@PathCut"
|
||||
|
||||
static const UInt32 k_7_oct_digits_Val_Max = ((UInt32)1 << (7 * 3)) - 1;
|
||||
|
||||
static void WriteOctal_8(char *s, UInt32 val)
|
||||
{
|
||||
const unsigned kNumDigits = 8 - 1;
|
||||
if (val >= ((UInt32)1 << (kNumDigits * 3)))
|
||||
return false;
|
||||
{
|
||||
val = 0;
|
||||
// return false;
|
||||
}
|
||||
for (unsigned i = 0; i < kNumDigits; i++)
|
||||
{
|
||||
s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
|
||||
val >>= 3;
|
||||
}
|
||||
return true;
|
||||
// return true;
|
||||
}
|
||||
|
||||
static void WriteBin_64bit(char *s, UInt64 val)
|
||||
@@ -68,61 +76,93 @@ static void CopyString(char *dest, const AString &src, unsigned maxSize)
|
||||
unsigned len = src.Len();
|
||||
if (len == 0)
|
||||
return;
|
||||
// 21.07: we don't require additional 0 character at the end
|
||||
// 21.07: new gnu : we don't require additional 0 character at the end
|
||||
// if (len >= maxSize)
|
||||
if (len > maxSize)
|
||||
{
|
||||
len = maxSize;
|
||||
// return false;
|
||||
/*
|
||||
// oldgnu needs 0 character at the end
|
||||
len = maxSize - 1;
|
||||
dest[len] = 0;
|
||||
*/
|
||||
}
|
||||
memcpy(dest, src.Ptr(), len);
|
||||
// return true;
|
||||
}
|
||||
|
||||
#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; }
|
||||
// #define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_INVALIDARG; }
|
||||
#define RETURN_IF_NOT_TRUE(x) { x; }
|
||||
|
||||
#define COPY_STRING_CHECK(dest, src, size) \
|
||||
CopyString(dest, src, size); dest += (size);
|
||||
|
||||
#define WRITE_OCTAL_8_CHECK(dest, src) \
|
||||
RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src));
|
||||
RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src))
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
HRESULT COutArchive::WriteHeaderReal(const CItem &item, bool isPax
|
||||
// , bool zero_PackSize
|
||||
// , bool zero_MTime
|
||||
)
|
||||
{
|
||||
char record[NFileHeader::kRecordSize];
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
/*
|
||||
if (isPax) { we don't use Glob_Name and Prefix }
|
||||
if (!isPax)
|
||||
{
|
||||
we use Glob_Name if it's not empty
|
||||
we use Prefix if it's not empty
|
||||
}
|
||||
*/
|
||||
char record[kRecordSize];
|
||||
memset(record, 0, kRecordSize);
|
||||
char *cur = record;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.Name, NFileHeader::kNameSize);
|
||||
COPY_STRING_CHECK (cur,
|
||||
(!isPax && !Glob_Name.IsEmpty()) ? Glob_Name : item.Name,
|
||||
kNameSize);
|
||||
|
||||
WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8;
|
||||
WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8; // & k_7_oct_digits_Val_Max
|
||||
WRITE_OCTAL_8_CHECK (cur, item.UID); cur += 8;
|
||||
WRITE_OCTAL_8_CHECK (cur, item.GID); cur += 8;
|
||||
|
||||
WriteOctal_12(cur, item.PackSize); cur += 12;
|
||||
WriteOctal_12_Signed(cur, item.MTime); cur += 12;
|
||||
WriteOctal_12 (cur, /* zero_PackSize ? 0 : */ item.PackSize); cur += 12;
|
||||
WriteOctal_12_Signed (cur, /* zero_MTime ? 0 : */ item.MTime); cur += 12;
|
||||
|
||||
memset(cur, ' ', 8); // checksum field
|
||||
// we will use binary init for checksum instead of memset
|
||||
// checksum field:
|
||||
// memset(cur, ' ', 8);
|
||||
cur += 8;
|
||||
|
||||
*cur++ = item.LinkFlag;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.LinkName, NFileHeader::kNameSize);
|
||||
COPY_STRING_CHECK (cur, item.LinkName, kNameSize);
|
||||
|
||||
memcpy(cur, item.Magic, 8);
|
||||
cur += 8;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.User, NFileHeader::kUserNameSize);
|
||||
COPY_STRING_CHECK (cur, item.Group, NFileHeader::kGroupNameSize);
|
||||
COPY_STRING_CHECK (cur, item.User, kUserNameSize);
|
||||
COPY_STRING_CHECK (cur, item.Group, kGroupNameSize);
|
||||
|
||||
if (item.DeviceMajorDefined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor);
|
||||
const bool needDevice = (IsPosixMode && !isPax);
|
||||
|
||||
if (item.DeviceMajor_Defined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor)
|
||||
else if (needDevice)
|
||||
WRITE_OCTAL_8_CHECK (cur, 0)
|
||||
cur += 8;
|
||||
if (item.DeviceMinorDefined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor);
|
||||
|
||||
if (item.DeviceMinor_Defined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor)
|
||||
else if (needDevice)
|
||||
WRITE_OCTAL_8_CHECK (cur, 0)
|
||||
cur += 8;
|
||||
|
||||
if (item.IsSparse())
|
||||
if (!isPax && !Prefix.IsEmpty())
|
||||
{
|
||||
COPY_STRING_CHECK (cur, Prefix, kPrefixSize);
|
||||
}
|
||||
|
||||
if (item.Is_Sparse())
|
||||
{
|
||||
record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0);
|
||||
WriteOctal_12(record + 483, item.Size);
|
||||
@@ -136,31 +176,31 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
}
|
||||
|
||||
{
|
||||
UInt32 checkSum = 0;
|
||||
UInt32 sum = (unsigned)(' ') * 8; // we use binary init
|
||||
{
|
||||
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
checkSum += (Byte)record[i];
|
||||
for (unsigned i = 0; i < kRecordSize; i++)
|
||||
sum += (Byte)record[i];
|
||||
}
|
||||
/* we use GNU TAR scheme:
|
||||
checksum field is formatted differently from the
|
||||
/* checksum field is formatted differently from the
|
||||
other fields: it has [6] digits, a null, then a space. */
|
||||
// WRITE_OCTAL_8_CHECK(record + 148, checkSum);
|
||||
// WRITE_OCTAL_8_CHECK(record + 148, sum);
|
||||
const unsigned kNumDigits = 6;
|
||||
for (unsigned i = 0; i < kNumDigits; i++)
|
||||
{
|
||||
record[148 + kNumDigits - 1 - i] = (char)('0' + (checkSum & 7));
|
||||
checkSum >>= 3;
|
||||
record[148 + kNumDigits - 1 - i] = (char)('0' + (sum & 7));
|
||||
sum >>= 3;
|
||||
}
|
||||
record[148 + 6] = 0;
|
||||
// record[148 + 6] = 0; // we need it, if we use memset(' ') init
|
||||
record[148 + 7] = ' '; // we need it, if we use binary init
|
||||
}
|
||||
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
|
||||
if (item.IsSparse())
|
||||
if (item.Is_Sparse())
|
||||
{
|
||||
for (unsigned i = 4; i < item.SparseBlocks.Size();)
|
||||
{
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
memset(record, 0, kRecordSize);
|
||||
for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++)
|
||||
{
|
||||
const CSparseBlock &sb = item.SparseBlocks[i];
|
||||
@@ -169,7 +209,7 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
WriteOctal_12(p + 12, sb.Size);
|
||||
}
|
||||
record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0);
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,101 +217,426 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
}
|
||||
|
||||
|
||||
/* OLD_GNU_TAR: writes short name with zero at the end
|
||||
NEW_GNU_TAR: writes short name without zero at the end */
|
||||
static void AddPaxLine(AString &s, const char *name, const AString &val)
|
||||
{
|
||||
// s.Add_LF(); // for debug
|
||||
const unsigned len = 3 + (unsigned)strlen(name) + val.Len();
|
||||
AString n;
|
||||
for (unsigned numDigits = 1;; numDigits++)
|
||||
{
|
||||
n.Empty();
|
||||
n.Add_UInt32(numDigits + len);
|
||||
if (numDigits == n.Len())
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
s.Add_Space();
|
||||
s += name;
|
||||
s += '=';
|
||||
s += val;
|
||||
s.Add_LF();
|
||||
}
|
||||
|
||||
|
||||
static void AddPaxTime(AString &s, const char *name, const CPaxTime &pt,
|
||||
const CTimeOptions &options)
|
||||
{
|
||||
unsigned numDigits = pt.NumDigits;
|
||||
if (numDigits > options.NumDigitsMax)
|
||||
numDigits = options.NumDigitsMax;
|
||||
|
||||
bool needNs = false;
|
||||
UInt32 ns = 0;
|
||||
if (numDigits != 0)
|
||||
{
|
||||
ns = pt.Ns;
|
||||
// if (ns != 0) before reduction, we show all digits after digits reduction
|
||||
needNs = (ns != 0 || options.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero);
|
||||
UInt32 d = 1;
|
||||
for (unsigned k = numDigits; k < 9; k++)
|
||||
d *= 10;
|
||||
ns /= d;
|
||||
ns *= d;
|
||||
}
|
||||
|
||||
AString v;
|
||||
{
|
||||
Int64 sec = pt.Sec;
|
||||
if (pt.Sec < 0)
|
||||
{
|
||||
sec = -sec;
|
||||
v += '-';
|
||||
if (ns != 0)
|
||||
{
|
||||
ns = 1000*1000*1000 - ns;
|
||||
sec--;
|
||||
}
|
||||
}
|
||||
v.Add_UInt64(sec);
|
||||
}
|
||||
|
||||
if (needNs)
|
||||
{
|
||||
AString d;
|
||||
d.Add_UInt32(ns);
|
||||
while (d.Len() < 9)
|
||||
d.InsertAtFront('0');
|
||||
// here we have precision
|
||||
while (d.Len() > (unsigned)numDigits)
|
||||
d.DeleteBack();
|
||||
// GNU TAR reduces '0' digits.
|
||||
if (options.RemoveZeroMode == k_PaxTimeMode_RemoveZero_Always)
|
||||
while (!d.IsEmpty() && d.Back() == '0')
|
||||
d.DeleteBack();
|
||||
|
||||
if (!d.IsEmpty())
|
||||
{
|
||||
v += '.';
|
||||
v += d;
|
||||
// v += "1234567009999"; // for debug
|
||||
// for (int y = 0; y < 1000; y++) v += '8'; // for debug
|
||||
}
|
||||
}
|
||||
|
||||
AddPaxLine(s, name, v);
|
||||
}
|
||||
|
||||
|
||||
static void AddPax_UInt32_ifBig(AString &s, const char *name, const UInt32 &v)
|
||||
{
|
||||
if (v > k_7_oct_digits_Val_Max)
|
||||
{
|
||||
AString s2;
|
||||
s2.Add_UInt32(v);
|
||||
AddPaxLine(s, name, s2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* OLD_GNU_TAR: writes name with zero at the end
|
||||
NEW_GNU_TAR: can write name filled with all kNameSize characters */
|
||||
|
||||
static const unsigned kNameSize_Max =
|
||||
NFileHeader::kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
|
||||
// NFileHeader::kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
|
||||
kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
|
||||
// kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
|
||||
|
||||
#define DOES_NAME_FIT_IN_FIELD(name) ((name).Len() <= kNameSize_Max)
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteHeader(const CItem &item)
|
||||
{
|
||||
if (DOES_NAME_FIT_IN_FIELD(item.Name) &&
|
||||
DOES_NAME_FIT_IN_FIELD(item.LinkName))
|
||||
return WriteHeaderReal(item);
|
||||
Glob_Name.Empty();
|
||||
Prefix.Empty();
|
||||
|
||||
// here we can get all fields from main (item) or create new empty item
|
||||
/*
|
||||
CItem mi;
|
||||
mi.SetDefaultWriteFields();
|
||||
*/
|
||||
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by IsSparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
unsigned namePos = 0;
|
||||
bool needPathCut = false;
|
||||
bool allowPrefix = false;
|
||||
|
||||
mi.Name = NFileHeader::kLongLink;
|
||||
// 21.07 : we set Mode and MTime props as in GNU TAR:
|
||||
mi.Mode = 0644; // octal
|
||||
mi.MTime = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name))
|
||||
{
|
||||
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 (DOES_NAME_FIT_IN_FIELD(*name))
|
||||
continue;
|
||||
// GNU TAR writes null character after NAME to file. We do same here:
|
||||
const unsigned nameStreamSize = name->Len() + 1;
|
||||
mi.PackSize = nameStreamSize;
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(WriteBytes(name->Ptr(), nameStreamSize));
|
||||
RINOK(FillDataResidual(nameStreamSize));
|
||||
const char *s = item.Name;
|
||||
const char *p = s + item.Name.Len() - 1;
|
||||
for (; *p == '/' && p != s; p--)
|
||||
{}
|
||||
for (; p != s && p[-1] != '/'; p--)
|
||||
{}
|
||||
namePos = (unsigned)(p - s);
|
||||
needPathCut = true;
|
||||
}
|
||||
|
||||
if (IsPosixMode)
|
||||
{
|
||||
AString s;
|
||||
|
||||
if (needPathCut)
|
||||
{
|
||||
const unsigned nameLen = item.Name.Len() - namePos;
|
||||
if ( item.LinkFlag >= NLinkFlag::kNormal
|
||||
&& item.LinkFlag <= NLinkFlag::kDirectory
|
||||
&& namePos > 1
|
||||
&& nameLen != 0
|
||||
// && IsPrefixAllowed
|
||||
&& item.IsMagic_Posix_ustar_00())
|
||||
{
|
||||
/* GNU TAR decoder supports prefix field, only if (magic)
|
||||
signature matches 6-bytes "ustar\0".
|
||||
so here we use prefix field only in posix mode with posix signature */
|
||||
|
||||
allowPrefix = true;
|
||||
// allowPrefix = false; // for debug
|
||||
if (namePos <= kPrefixSize + 1 && nameLen <= kNameSize_Max)
|
||||
{
|
||||
needPathCut = false;
|
||||
/* we will set Prefix and Glob_Name later, for such conditions:
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name) && !needPathCut) */
|
||||
}
|
||||
}
|
||||
|
||||
if (needPathCut)
|
||||
AddPaxLine(s, "path", item.Name);
|
||||
}
|
||||
|
||||
// AddPaxLine(s, "testname", AString("testval")); // for debug
|
||||
|
||||
if (item.LinkName.Len() > kNameSize_Max)
|
||||
AddPaxLine(s, "linkpath", item.LinkName);
|
||||
|
||||
const UInt64 kPaxSize_Limit = ((UInt64)1 << 33);
|
||||
// const UInt64 kPaxSize_Limit = ((UInt64)1 << 1); // for debug
|
||||
// bool zero_PackSize = false;
|
||||
if (item.PackSize >= kPaxSize_Limit)
|
||||
{
|
||||
/* GNU TAR in pax mode sets PackSize = 0 in main record, if pack_size >= 8 GiB
|
||||
But old 7-Zip doesn't detect "size" property from pax header.
|
||||
So we write real size (>= 8 GiB) to main record in binary format,
|
||||
and old 7-Zip can decode size correctly */
|
||||
// zero_PackSize = true;
|
||||
AString v;
|
||||
v.Add_UInt64(item.PackSize);
|
||||
AddPaxLine(s, "size", v);
|
||||
}
|
||||
|
||||
/* GNU TAR encoder can set "devmajor" / "devminor" attributes,
|
||||
but GNU TAR decoder doesn't parse "devmajor" / "devminor" */
|
||||
if (item.DeviceMajor_Defined)
|
||||
AddPax_UInt32_ifBig(s, "devmajor", item.DeviceMajor);
|
||||
if (item.DeviceMinor_Defined)
|
||||
AddPax_UInt32_ifBig(s, "devminor", item.DeviceMinor);
|
||||
|
||||
AddPax_UInt32_ifBig(s, "uid", item.UID);
|
||||
AddPax_UInt32_ifBig(s, "gid", item.GID);
|
||||
|
||||
const UInt64 kPax_MTime_Limit = ((UInt64)1 << 33);
|
||||
const bool zero_MTime = (
|
||||
item.MTime < 0 ||
|
||||
item.MTime >= (Int64)kPax_MTime_Limit);
|
||||
|
||||
const CPaxTime &mtime = item.PaxTimes.MTime;
|
||||
if (mtime.IsDefined())
|
||||
{
|
||||
bool needPax = false;
|
||||
if (zero_MTime)
|
||||
needPax = true;
|
||||
else if (TimeOptions.NumDigitsMax > 0)
|
||||
if (mtime.Ns != 0 ||
|
||||
(mtime.NumDigits != 0 &&
|
||||
TimeOptions.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero))
|
||||
needPax = true;
|
||||
if (needPax)
|
||||
AddPaxTime(s, "mtime", mtime, TimeOptions);
|
||||
}
|
||||
|
||||
if (item.PaxTimes.ATime.IsDefined())
|
||||
AddPaxTime(s, "atime", item.PaxTimes.ATime, TimeOptions);
|
||||
if (item.PaxTimes.CTime.IsDefined())
|
||||
AddPaxTime(s, "ctime", item.PaxTimes.CTime, TimeOptions);
|
||||
|
||||
if (item.User.Len() > kUserNameSize)
|
||||
AddPaxLine(s, "uname", item.User);
|
||||
if (item.Group.Len() > kGroupNameSize)
|
||||
AddPaxLine(s, "gname", item.Group);
|
||||
|
||||
/*
|
||||
// for debug
|
||||
AString a ("11"); for (int y = 0; y < (1 << 24); y++) AddPaxLine(s, "temp", a);
|
||||
*/
|
||||
|
||||
const unsigned paxSize = s.Len();
|
||||
if (paxSize != 0)
|
||||
{
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by Is_Sparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
// we use "PaxHeader/*" for compatibility with previous 7-Zip decoder
|
||||
|
||||
// GNU TAR writes empty for these fields;
|
||||
mi.User.Empty();
|
||||
mi.Group.Empty();
|
||||
mi.UID = 0;
|
||||
mi.GID = 0;
|
||||
|
||||
mi.DeviceMajor_Defined = false;
|
||||
mi.DeviceMinor_Defined = false;
|
||||
|
||||
mi.Name = "PaxHeader/@PaxHeader";
|
||||
mi.Mode = 0644; // octal
|
||||
if (zero_MTime)
|
||||
mi.MTime = 0;
|
||||
mi.LinkFlag = NLinkFlag::kPax;
|
||||
// mi.LinkFlag = 'Z'; // for debug
|
||||
mi.PackSize = paxSize;
|
||||
// for (unsigned y = 0; y < 1; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi, true)); // isPax
|
||||
RINOK(Write_Data_And_Residual(s, paxSize));
|
||||
// } // for debug
|
||||
/*
|
||||
we can send (zero_MTime) for compatibility with gnu tar output.
|
||||
we can send (zero_MTime = false) for better compatibility with old 7-Zip
|
||||
*/
|
||||
// return WriteHeaderReal(item);
|
||||
/*
|
||||
false, // isPax
|
||||
false, // zero_PackSize
|
||||
false); // zero_MTime
|
||||
*/
|
||||
}
|
||||
}
|
||||
else // !PosixMode
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name) ||
|
||||
!DOES_NAME_FIT_IN_FIELD(item.LinkName))
|
||||
{
|
||||
// here we can get all fields from main (item) or create new empty item
|
||||
/*
|
||||
CItem mi;
|
||||
mi.SetDefaultWriteFields();
|
||||
*/
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by Is_Sparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
mi.Name = kLongLink;
|
||||
// mi.Name = "././@BAD_LONG_LINK_TEST"; // for debug
|
||||
// 21.07 : we set Mode and MTime props as in GNU TAR:
|
||||
mi.Mode = 0644; // octal
|
||||
mi.MTime = 0;
|
||||
|
||||
mi.User.Empty();
|
||||
mi.Group.Empty();
|
||||
/*
|
||||
gnu tar sets "root" for such items:
|
||||
uid_to_uname (0, &uname);
|
||||
gid_to_gname (0, &gname);
|
||||
*/
|
||||
/*
|
||||
mi.User = "root";
|
||||
mi.Group = "root";
|
||||
*/
|
||||
mi.UID = 0;
|
||||
mi.GID = 0;
|
||||
mi.DeviceMajor_Defined = false;
|
||||
mi.DeviceMinor_Defined = false;
|
||||
|
||||
|
||||
for (unsigned 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 = NLinkFlag::kGnu_LongLink;
|
||||
name = &item.LinkName;
|
||||
}
|
||||
else
|
||||
{
|
||||
mi.LinkFlag = NLinkFlag::kGnu_LongName;
|
||||
name = &item.Name;
|
||||
}
|
||||
if (DOES_NAME_FIT_IN_FIELD(*name))
|
||||
continue;
|
||||
// GNU TAR writes null character after NAME to file. We do same here:
|
||||
const unsigned nameStreamSize = name->Len() + 1;
|
||||
mi.PackSize = nameStreamSize;
|
||||
// for (unsigned y = 0; y < 3; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(Write_Data_And_Residual(name->Ptr(), nameStreamSize));
|
||||
// }
|
||||
|
||||
// for debug
|
||||
/*
|
||||
const unsigned kSize = (1 << 29) + 16;
|
||||
CByteBuffer buf;
|
||||
buf.Alloc(kSize);
|
||||
memset(buf, 0, kSize);
|
||||
memcpy(buf, name->Ptr(), name->Len());
|
||||
const unsigned nameStreamSize = kSize;
|
||||
mi.PackSize = nameStreamSize;
|
||||
// for (unsigned y = 0; y < 3; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(WriteBytes(buf, nameStreamSize));
|
||||
RINOK(FillDataResidual(nameStreamSize));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// bool fals = false; if (fals) // for debug: for bit-to-bit output compatibility with GNU TAR
|
||||
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name))
|
||||
{
|
||||
const unsigned nameLen = item.Name.Len() - namePos;
|
||||
if (!needPathCut)
|
||||
Prefix.SetFrom(item.Name, namePos - 1);
|
||||
else
|
||||
{
|
||||
Glob_Name = K_PREFIX_PATH_CUT "/_pc_";
|
||||
|
||||
if (namePos == 0)
|
||||
Glob_Name += "root";
|
||||
else
|
||||
{
|
||||
Glob_Name += "crc32/";
|
||||
char temp[12];
|
||||
ConvertUInt32ToHex8Digits(CrcCalc(item.Name, namePos - 1), temp);
|
||||
Glob_Name += temp;
|
||||
}
|
||||
|
||||
if (!allowPrefix || Glob_Name.Len() + 1 + nameLen <= kNameSize_Max)
|
||||
Glob_Name.Add_Slash();
|
||||
else
|
||||
{
|
||||
Prefix = Glob_Name;
|
||||
Glob_Name.Empty();
|
||||
}
|
||||
}
|
||||
Glob_Name.AddFrom(item.Name.Ptr(namePos), nameLen);
|
||||
}
|
||||
|
||||
// 21.07: WriteHeaderReal() writes short part of (Name) and (LinkName).
|
||||
return WriteHeaderReal(item);
|
||||
/*
|
||||
mi = item;
|
||||
if (!DOES_NAME_FIT_IN_FIELD(mi.Name))
|
||||
mi.Name.SetFrom(item.Name, kNameSize_Max);
|
||||
if (!DOES_NAME_FIT_IN_FIELD(mi.LinkName))
|
||||
mi.LinkName.SetFrom(item.LinkName, kNameSize_Max);
|
||||
return WriteHeaderReal(mi);
|
||||
*/
|
||||
}
|
||||
|
||||
HRESULT COutArchive::FillDataResidual(UInt64 dataSize)
|
||||
|
||||
HRESULT COutArchive::Write_Data(const void *data, unsigned size)
|
||||
{
|
||||
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);
|
||||
Pos += size;
|
||||
return WriteStream(Stream, data, size);
|
||||
}
|
||||
|
||||
HRESULT COutArchive::Write_AfterDataResidual(UInt64 dataSize)
|
||||
{
|
||||
const unsigned v = ((unsigned)dataSize & (kRecordSize - 1));
|
||||
if (v == 0)
|
||||
return S_OK;
|
||||
const unsigned rem = kRecordSize - v;
|
||||
Byte buf[kRecordSize];
|
||||
memset(buf, 0, rem);
|
||||
return Write_Data(buf, rem);
|
||||
}
|
||||
|
||||
|
||||
HRESULT COutArchive::Write_Data_And_Residual(const void *data, unsigned size)
|
||||
{
|
||||
RINOK(Write_Data(data, size));
|
||||
return Write_AfterDataResidual(size);
|
||||
}
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteFinishHeader()
|
||||
{
|
||||
Byte record[NFileHeader::kRecordSize];
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
Byte record[kRecordSize];
|
||||
memset(record, 0, kRecordSize);
|
||||
|
||||
const unsigned kNumFinishRecords = 2;
|
||||
|
||||
/* GNU TAR by default uses --blocking-factor=20 (512 * 20 = 10 KiB)
|
||||
we also can use cluster alignment:
|
||||
const unsigned numBlocks = (unsigned)(Pos / NFileHeader::kRecordSize) + kNumFinishRecords;
|
||||
const unsigned numBlocks = (unsigned)(Pos / kRecordSize) + kNumFinishRecords;
|
||||
const unsigned kNumClusterBlocks = (1 << 3); // 8 blocks = 4 KiB
|
||||
const unsigned numFinishRecords = kNumFinishRecords + ((kNumClusterBlocks - numBlocks) & (kNumClusterBlocks - 1));
|
||||
*/
|
||||
|
||||
for (unsigned i = 0; i < kNumFinishRecords; i++)
|
||||
{
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -14,21 +14,38 @@ namespace NTar {
|
||||
|
||||
class COutArchive
|
||||
{
|
||||
CMyComPtr<ISequentialOutStream> m_Stream;
|
||||
CMyComPtr<ISequentialOutStream> Stream;
|
||||
|
||||
AString Glob_Name;
|
||||
AString Prefix;
|
||||
|
||||
HRESULT WriteHeaderReal(const CItem &item, bool isPax = false
|
||||
// , bool zero_PackSize = false
|
||||
// , bool zero_MTime = false
|
||||
);
|
||||
|
||||
HRESULT Write_Data(const void *data, unsigned size);
|
||||
HRESULT Write_Data_And_Residual(const void *data, unsigned size);
|
||||
|
||||
HRESULT WriteBytes(const void *data, unsigned size);
|
||||
HRESULT WriteHeaderReal(const CItem &item);
|
||||
public:
|
||||
UInt64 Pos;
|
||||
|
||||
bool IsPosixMode;
|
||||
// bool IsPrefixAllowed; // it's used only if (IsPosixMode == true)
|
||||
CTimeOptions TimeOptions;
|
||||
|
||||
void Create(ISequentialOutStream *outStream)
|
||||
{
|
||||
m_Stream = outStream;
|
||||
Stream = outStream;
|
||||
}
|
||||
|
||||
HRESULT WriteHeader(const CItem &item);
|
||||
HRESULT FillDataResidual(UInt64 dataSize);
|
||||
HRESULT Write_AfterDataResidual(UInt64 dataSize);
|
||||
HRESULT WriteFinishHeader();
|
||||
|
||||
COutArchive():
|
||||
Pos(0),
|
||||
IsPosixMode(false)
|
||||
// , IsPrefixAllowed(true)
|
||||
{}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -15,9 +15,17 @@ REGISTER_ARC_IO(
|
||||
"tar", "tar ova", 0, 0xEE,
|
||||
k_Signature,
|
||||
NFileHeader::kUstarMagic_Offset,
|
||||
NArcInfoFlags::kStartOpen |
|
||||
NArcInfoFlags::kSymLinks |
|
||||
NArcInfoFlags::kHardLinks,
|
||||
IsArc_Tar)
|
||||
NArcInfoFlags::kStartOpen
|
||||
| NArcInfoFlags::kSymLinks
|
||||
| NArcInfoFlags::kHardLinks
|
||||
| NArcInfoFlags::kMTime
|
||||
| NArcInfoFlags::kMTime_Default
|
||||
// | NArcInfoTimeFlags::kCTime
|
||||
// | NArcInfoTimeFlags::kATime
|
||||
, TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::kWindows)
|
||||
| TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::kUnix)
|
||||
| TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::k1ns)
|
||||
| TIME_PREC_TO_ARC_FLAGS_TIME_DEFAULT (NFileTimeType::kUnix)
|
||||
, IsArc_Tar)
|
||||
|
||||
}}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
// #include <stdio.h>
|
||||
|
||||
#include "../../../Windows/TimeUtils.h"
|
||||
|
||||
#include "../../Common/LimitedStreams.h"
|
||||
@@ -15,18 +17,161 @@
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
static void FILETIME_To_PaxTime(const FILETIME &ft, CPaxTime &pt)
|
||||
{
|
||||
UInt32 ns;
|
||||
pt.Sec = NWindows::NTime::FileTime_To_UnixTime64_and_Quantums(ft, ns);
|
||||
pt.Ns = ns * 100;
|
||||
pt.NumDigits = 7;
|
||||
}
|
||||
|
||||
|
||||
HRESULT Prop_To_PaxTime(const NWindows::NCOM::CPropVariant &prop, CPaxTime &pt)
|
||||
{
|
||||
pt.Clear();
|
||||
if (prop.vt == VT_EMPTY)
|
||||
{
|
||||
// pt.Sec = 0;
|
||||
return S_OK;
|
||||
}
|
||||
if (prop.vt != VT_FILETIME)
|
||||
return E_INVALIDARG;
|
||||
{
|
||||
UInt32 ns;
|
||||
pt.Sec = NWindows::NTime::FileTime_To_UnixTime64_and_Quantums(prop.filetime, ns);
|
||||
ns *= 100;
|
||||
pt.NumDigits = 7;
|
||||
const unsigned prec = prop.wReserved1;
|
||||
if (prec >= k_PropVar_TimePrec_Base)
|
||||
{
|
||||
pt.NumDigits = prec - k_PropVar_TimePrec_Base;
|
||||
if (prop.wReserved2 < 100)
|
||||
ns += prop.wReserved2;
|
||||
}
|
||||
pt.Ns = ns;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static HRESULT GetTime(IStreamGetProp *getProp, UInt32 pid, CPaxTime &pt)
|
||||
{
|
||||
pt.Clear();
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(pid, &prop));
|
||||
return Prop_To_PaxTime(prop, pt);
|
||||
}
|
||||
|
||||
|
||||
static HRESULT GetUser(IStreamGetProp *getProp,
|
||||
UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id,
|
||||
UINT codePage, unsigned utfFlags)
|
||||
{
|
||||
// printf("\nGetUser\n");
|
||||
// we keep old values, if both GetProperty() return VT_EMPTY
|
||||
// we clear old values, if any of GetProperty() returns non-VT_EMPTY;
|
||||
bool isSet = false;
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(pidId, &prop));
|
||||
if (prop.vt == VT_UI4)
|
||||
{
|
||||
isSet = true;
|
||||
id = prop.ulVal;
|
||||
name.Empty();
|
||||
}
|
||||
else if (prop.vt != VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(pidName, &prop));
|
||||
if (prop.vt == VT_BSTR)
|
||||
{
|
||||
const UString s = prop.bstrVal;
|
||||
Get_AString_From_UString(s, name, codePage, utfFlags);
|
||||
// printf("\ngetProp->GetProperty(pidName, &prop) : %s" , name.Ptr());
|
||||
if (!isSet)
|
||||
id = 0;
|
||||
}
|
||||
else if (prop.vt == VT_UI4)
|
||||
{
|
||||
id = prop.ulVal;
|
||||
name.Empty();
|
||||
}
|
||||
else if (prop.vt != VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static HRESULT GetDevice(IStreamGetProp *getProp,
|
||||
UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined)
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(kpidDevice, &prop));
|
||||
if (prop.vt == VT_EMPTY)
|
||||
return S_OK;
|
||||
if (prop.vt != VT_UI8)
|
||||
return E_INVALIDARG;
|
||||
{
|
||||
printf("\nTarUpdate.cpp :: GetDevice()\n");
|
||||
const UInt64 v = prop.uhVal.QuadPart;
|
||||
majo = MY_dev_major(v);
|
||||
mino = MY_dev_minor(v);
|
||||
majo_defined = true;
|
||||
mino_defined = true;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
*/
|
||||
|
||||
static HRESULT GetDevice(IStreamGetProp *getProp,
|
||||
UInt32 pid, UInt32 &id, bool &defined)
|
||||
{
|
||||
defined = false;
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(pid, &prop));
|
||||
if (prop.vt == VT_EMPTY)
|
||||
return S_OK;
|
||||
if (prop.vt == VT_UI4)
|
||||
{
|
||||
id = prop.ulVal;
|
||||
defined = true;
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
|
||||
HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
const CObjectVector<NArchive::NTar::CItemEx> &inputItems,
|
||||
const CObjectVector<CUpdateItem> &updateItems,
|
||||
UINT codePage, unsigned utfFlags,
|
||||
const CUpdateOptions &options,
|
||||
IArchiveUpdateCallback *updateCallback)
|
||||
{
|
||||
COutArchive outArchive;
|
||||
outArchive.Create(outStream);
|
||||
outArchive.Pos = 0;
|
||||
outArchive.IsPosixMode = options.PosixMode;
|
||||
outArchive.TimeOptions = options.TimeOptions;
|
||||
|
||||
CMyComPtr<IOutStream> outSeekStream;
|
||||
outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
|
||||
if (outSeekStream)
|
||||
{
|
||||
/*
|
||||
// for debug
|
||||
Byte buf[1 << 14];
|
||||
memset (buf, 0, sizeof(buf));
|
||||
RINOK(outStream->Write(buf, sizeof(buf), NULL));
|
||||
*/
|
||||
// we need real outArchive.Pos, if outSeekStream->SetSize() will be used.
|
||||
RINOK(outSeekStream->Seek(0, STREAM_SEEK_CUR, &outArchive.Pos));
|
||||
}
|
||||
|
||||
|
||||
CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
|
||||
updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
|
||||
@@ -40,7 +185,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
if (ui.NewData)
|
||||
complexity += ui.Size;
|
||||
else
|
||||
complexity += inputItems[(unsigned)ui.IndexInArc].GetFullSize();
|
||||
complexity += inputItems[(unsigned)ui.IndexInArc].Get_FullSize_Aligned();
|
||||
}
|
||||
|
||||
RINOK(updateCallback->SetTotal(complexity));
|
||||
@@ -58,21 +203,31 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
|
||||
complexity = 0;
|
||||
|
||||
for (i = 0; i < updateItems.Size(); i++)
|
||||
// const int kNumReduceDigits = -1; // for debug
|
||||
|
||||
for (i = 0;; i++)
|
||||
{
|
||||
lps->InSize = lps->OutSize = complexity;
|
||||
RINOK(lps->SetCur());
|
||||
|
||||
if (i == updateItems.Size())
|
||||
return outArchive.WriteFinishHeader();
|
||||
|
||||
const CUpdateItem &ui = updateItems[i];
|
||||
CItem item;
|
||||
|
||||
if (ui.NewProps)
|
||||
{
|
||||
item.SetDefaultWriteFields();
|
||||
item.Mode = ui.Mode;
|
||||
item.SetMagic_Posix(options.PosixMode);
|
||||
item.Name = ui.Name;
|
||||
item.User = ui.User;
|
||||
item.Group = ui.Group;
|
||||
item.UID = ui.UID;
|
||||
item.GID = ui.GID;
|
||||
item.DeviceMajor = ui.DeviceMajor;
|
||||
item.DeviceMinor = ui.DeviceMinor;
|
||||
item.DeviceMajor_Defined = ui.DeviceMajor_Defined;
|
||||
item.DeviceMinor_Defined = ui.DeviceMinor_Defined;
|
||||
|
||||
if (ui.IsDir)
|
||||
{
|
||||
@@ -81,11 +236,15 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
}
|
||||
else
|
||||
{
|
||||
item.LinkFlag = NFileHeader::NLinkFlag::kNormal;
|
||||
item.PackSize = ui.Size;
|
||||
item.Set_LinkFlag_for_File(ui.Mode);
|
||||
}
|
||||
|
||||
item.MTime = ui.MTime;
|
||||
|
||||
// 22.00
|
||||
item.Mode = ui.Mode & ~(UInt32)MY_LIN_S_IFMT;
|
||||
item.PaxTimes = ui.PaxTimes;
|
||||
// item.PaxTimes.ReducePrecison(kNumReduceDigits); // for debug
|
||||
item.MTime = ui.PaxTimes.MTime.GetSec();
|
||||
}
|
||||
else
|
||||
item = inputItems[(unsigned)ui.IndexInArc];
|
||||
@@ -93,7 +252,8 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
AString symLink;
|
||||
if (ui.NewData || ui.NewProps)
|
||||
{
|
||||
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink, codePage, utfFlags, true));
|
||||
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink,
|
||||
options.CodePage, options.UtfFlags, true));
|
||||
if (!symLink.IsEmpty())
|
||||
{
|
||||
item.LinkFlag = NFileHeader::NLinkFlag::kSymLink;
|
||||
@@ -120,7 +280,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
}
|
||||
else
|
||||
{
|
||||
HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
|
||||
const HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
|
||||
|
||||
if (res == S_FALSE)
|
||||
needWrite = false;
|
||||
@@ -128,31 +288,105 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
{
|
||||
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
|
||||
if (!fileInStream)
|
||||
{
|
||||
item.PackSize = 0;
|
||||
item.Size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CMyComPtr<IStreamGetProps> getProps;
|
||||
CMyComPtr<IStreamGetProp> getProp;
|
||||
fileInStream->QueryInterface(IID_IStreamGetProp, (void **)&getProp);
|
||||
if (getProp)
|
||||
{
|
||||
if (options.Write_MTime.Val) RINOK(GetTime(getProp, kpidMTime, item.PaxTimes.MTime))
|
||||
if (options.Write_ATime.Val) RINOK(GetTime(getProp, kpidATime, item.PaxTimes.ATime))
|
||||
if (options.Write_CTime.Val) RINOK(GetTime(getProp, kpidCTime, item.PaxTimes.CTime))
|
||||
|
||||
if (options.PosixMode)
|
||||
{
|
||||
/*
|
||||
RINOK(GetDevice(getProp, item.DeviceMajor, item.DeviceMinor,
|
||||
item.DeviceMajor_Defined, item.DeviceMinor_Defined));
|
||||
*/
|
||||
bool defined = false;
|
||||
UInt32 val = 0;
|
||||
RINOK(GetDevice(getProp, kpidDeviceMajor, val, defined));
|
||||
if (defined)
|
||||
{
|
||||
item.DeviceMajor = val;
|
||||
item.DeviceMajor_Defined = true;
|
||||
item.DeviceMinor = 0;
|
||||
item.DeviceMinor_Defined = false;
|
||||
RINOK(GetDevice(getProp, kpidDeviceMinor, item.DeviceMinor, item.DeviceMinor_Defined));
|
||||
}
|
||||
}
|
||||
|
||||
RINOK(GetUser(getProp, kpidUser, kpidUserId, item.User, item.UID, options.CodePage, options.UtfFlags));
|
||||
RINOK(GetUser(getProp, kpidGroup, kpidGroupId, item.Group, item.GID, options.CodePage, options.UtfFlags));
|
||||
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(kpidPosixAttrib, &prop));
|
||||
if (prop.vt == VT_EMPTY)
|
||||
item.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
|
||||
item.Mode = prop.ulVal;
|
||||
// 21.07 : we clear high file type bits as GNU TAR.
|
||||
item.Set_LinkFlag_for_File(item.Mode);
|
||||
item.Mode &= ~(UInt32)MY_LIN_S_IFMT;
|
||||
}
|
||||
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
RINOK(getProp->GetProperty(kpidSize, &prop));
|
||||
if (prop.vt != VT_UI8)
|
||||
return E_INVALIDARG;
|
||||
const UInt64 size = prop.uhVal.QuadPart;
|
||||
item.PackSize = size;
|
||||
item.Size = size;
|
||||
}
|
||||
/*
|
||||
printf("\nNum digits = %d %d\n",
|
||||
(int)item.PaxTimes.MTime.NumDigits,
|
||||
(int)item.PaxTimes.MTime.Ns);
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
|
||||
if (getProps)
|
||||
{
|
||||
FILETIME mTime, aTime, cTime;
|
||||
UInt64 size2;
|
||||
if (getProps->GetProps(&size2,
|
||||
options.Write_CTime.Val ? &cTime : NULL,
|
||||
options.Write_ATime.Val ? &aTime : NULL,
|
||||
options.Write_MTime.Val ? &mTime : NULL,
|
||||
NULL) == S_OK)
|
||||
{
|
||||
item.PackSize = size2;
|
||||
item.Size = size2;
|
||||
if (options.Write_MTime.Val) FILETIME_To_PaxTime(mTime, item.PaxTimes.MTime);
|
||||
if (options.Write_ATime.Val) FILETIME_To_PaxTime(aTime, item.PaxTimes.ATime);
|
||||
if (options.Write_CTime.Val) FILETIME_To_PaxTime(cTime, item.PaxTimes.CTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// we must request kpidHardLink after updateCallback->GetStream()
|
||||
AString hardLink;
|
||||
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink, codePage, utfFlags, true));
|
||||
RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink,
|
||||
options.CodePage, options.UtfFlags, true));
|
||||
if (!hardLink.IsEmpty())
|
||||
{
|
||||
item.LinkFlag = NFileHeader::NLinkFlag::kHardLink;
|
||||
@@ -165,37 +399,98 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
}
|
||||
}
|
||||
|
||||
// item.PaxTimes.ReducePrecison(kNumReduceDigits); // for debug
|
||||
|
||||
if (ui.NewProps)
|
||||
item.MTime = item.PaxTimes.MTime.GetSec();
|
||||
|
||||
if (needWrite)
|
||||
{
|
||||
UInt64 fileHeaderStartPos = outArchive.Pos;
|
||||
const UInt64 headerPos = outArchive.Pos;
|
||||
// item.PackSize = ((UInt64)1 << 33); // for debug
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
if (fileInStream)
|
||||
{
|
||||
RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
|
||||
outArchive.Pos += copyCoderSpec->TotalSize;
|
||||
if (copyCoderSpec->TotalSize != item.PackSize)
|
||||
for (unsigned numPasses = 0;; numPasses++)
|
||||
{
|
||||
/* we support 2 attempts to write header:
|
||||
pass-0: main pass:
|
||||
pass-1: additional pass, if size_of_file and size_of_header are changed */
|
||||
if (numPasses >= 2)
|
||||
{
|
||||
// opRes = NArchive::NUpdate::NOperationResult::kError_FileChanged;
|
||||
// break;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const UInt64 dataPos = outArchive.Pos;
|
||||
RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
|
||||
outArchive.Pos += copyCoderSpec->TotalSize;
|
||||
RINOK(outArchive.Write_AfterDataResidual(copyCoderSpec->TotalSize));
|
||||
|
||||
// if (numPasses >= 10) // for debug
|
||||
if (copyCoderSpec->TotalSize == item.PackSize)
|
||||
break;
|
||||
|
||||
if (opCallback)
|
||||
{
|
||||
RINOK(opCallback->ReportOperation(
|
||||
NEventIndexType::kOutArcIndex, (UInt32)ui.IndexInClient,
|
||||
NUpdateNotifyOp::kInFileChanged))
|
||||
}
|
||||
|
||||
if (!outSeekStream)
|
||||
return E_FAIL;
|
||||
UInt64 backOffset = outArchive.Pos - fileHeaderStartPos;
|
||||
RINOK(outSeekStream->Seek(-(Int64)backOffset, STREAM_SEEK_CUR, NULL));
|
||||
outArchive.Pos = fileHeaderStartPos;
|
||||
const UInt64 nextPos = outArchive.Pos;
|
||||
RINOK(outSeekStream->Seek(-(Int64)(nextPos - headerPos), STREAM_SEEK_CUR, NULL));
|
||||
outArchive.Pos = headerPos;
|
||||
item.PackSize = copyCoderSpec->TotalSize;
|
||||
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
RINOK(outSeekStream->Seek((Int64)item.PackSize, STREAM_SEEK_CUR, NULL));
|
||||
outArchive.Pos += item.PackSize;
|
||||
|
||||
// if (numPasses >= 10) // for debug
|
||||
if (outArchive.Pos == dataPos)
|
||||
{
|
||||
const UInt64 alignedSize = nextPos - dataPos;
|
||||
if (alignedSize != 0)
|
||||
{
|
||||
RINOK(outSeekStream->Seek(alignedSize, STREAM_SEEK_CUR, NULL));
|
||||
outArchive.Pos += alignedSize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// size of header was changed.
|
||||
// we remove data after header and try new attempt, if required
|
||||
CMyComPtr<IInStream> fileSeekStream;
|
||||
fileInStream->QueryInterface(IID_IInStream, (void **)&fileSeekStream);
|
||||
if (!fileSeekStream)
|
||||
return E_FAIL;
|
||||
RINOK(fileSeekStream->Seek(0, STREAM_SEEK_SET, NULL));
|
||||
RINOK(outSeekStream->SetSize(outArchive.Pos));
|
||||
if (item.PackSize == 0)
|
||||
break;
|
||||
}
|
||||
RINOK(outArchive.FillDataResidual(item.PackSize));
|
||||
}
|
||||
}
|
||||
|
||||
complexity += item.PackSize;
|
||||
fileInStream.Release();
|
||||
RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
|
||||
}
|
||||
else
|
||||
{
|
||||
// (ui.NewData == false)
|
||||
|
||||
if (opCallback)
|
||||
{
|
||||
RINOK(opCallback->ReportOperation(
|
||||
NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
|
||||
NUpdateNotifyOp::kReplicate))
|
||||
}
|
||||
|
||||
const CItemEx &existItem = inputItems[(unsigned)ui.IndexInArc];
|
||||
UInt64 size;
|
||||
UInt64 size, pos;
|
||||
|
||||
if (ui.NewProps)
|
||||
{
|
||||
@@ -216,44 +511,37 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
item.PackSize = existItem.PackSize;
|
||||
}
|
||||
|
||||
item.DeviceMajorDefined = existItem.DeviceMajorDefined;
|
||||
item.DeviceMinorDefined = existItem.DeviceMinorDefined;
|
||||
item.DeviceMajor_Defined = existItem.DeviceMajor_Defined;
|
||||
item.DeviceMinor_Defined = existItem.DeviceMinor_Defined;
|
||||
item.DeviceMajor = existItem.DeviceMajor;
|
||||
item.DeviceMinor = existItem.DeviceMinor;
|
||||
item.UID = existItem.UID;
|
||||
item.GID = existItem.GID;
|
||||
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
RINOK(inStream->Seek((Int64)existItem.GetDataPosition(), STREAM_SEEK_SET, NULL));
|
||||
size = existItem.PackSize;
|
||||
size = existItem.Get_PackSize_Aligned();
|
||||
pos = existItem.Get_DataPos();
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(inStream->Seek((Int64)existItem.HeaderPos, STREAM_SEEK_SET, NULL));
|
||||
size = existItem.GetFullSize();
|
||||
size = existItem.Get_FullSize_Aligned();
|
||||
pos = existItem.HeaderPos;
|
||||
}
|
||||
|
||||
streamSpec->Init(size);
|
||||
|
||||
if (opCallback)
|
||||
if (size != 0)
|
||||
{
|
||||
RINOK(opCallback->ReportOperation(
|
||||
NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
|
||||
NUpdateNotifyOp::kReplicate))
|
||||
RINOK(inStream->Seek((Int64)pos, STREAM_SEEK_SET, NULL));
|
||||
streamSpec->Init(size);
|
||||
// 22.00 : we copy Residual data from old archive to new archive instead of zeroing
|
||||
RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
|
||||
if (copyCoderSpec->TotalSize != size)
|
||||
return E_FAIL;
|
||||
outArchive.Pos += size;
|
||||
// RINOK(outArchive.Write_AfterDataResidual(existItem.PackSize));
|
||||
complexity += size;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -15,27 +15,60 @@ struct CUpdateItem
|
||||
int IndexInArc;
|
||||
unsigned IndexInClient;
|
||||
UInt64 Size;
|
||||
Int64 MTime;
|
||||
// Int64 MTime;
|
||||
UInt32 Mode;
|
||||
bool NewData;
|
||||
bool NewProps;
|
||||
bool IsDir;
|
||||
bool DeviceMajor_Defined;
|
||||
bool DeviceMinor_Defined;
|
||||
UInt32 UID;
|
||||
UInt32 GID;
|
||||
UInt32 DeviceMajor;
|
||||
UInt32 DeviceMinor;
|
||||
AString Name;
|
||||
AString User;
|
||||
AString Group;
|
||||
|
||||
CUpdateItem(): Size(0), IsDir(false) {}
|
||||
CPaxTimes PaxTimes;
|
||||
|
||||
CUpdateItem():
|
||||
Size(0),
|
||||
IsDir(false),
|
||||
DeviceMajor_Defined(false),
|
||||
DeviceMinor_Defined(false),
|
||||
UID(0),
|
||||
GID(0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct CUpdateOptions
|
||||
{
|
||||
UINT CodePage;
|
||||
unsigned UtfFlags;
|
||||
bool PosixMode;
|
||||
CBoolPair Write_MTime;
|
||||
CBoolPair Write_ATime;
|
||||
CBoolPair Write_CTime;
|
||||
CTimeOptions TimeOptions;
|
||||
};
|
||||
|
||||
|
||||
HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
const CObjectVector<CItemEx> &inputItems,
|
||||
const CObjectVector<CUpdateItem> &updateItems,
|
||||
UINT codePage, unsigned utfFlags,
|
||||
const CUpdateOptions &options,
|
||||
IArchiveUpdateCallback *updateCallback);
|
||||
|
||||
HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
|
||||
UINT codePage, unsigned utfFlags, bool convertSlash);
|
||||
|
||||
HRESULT Prop_To_PaxTime(const NWindows::NCOM::CPropVariant &prop, CPaxTime &pt);
|
||||
|
||||
void Get_AString_From_UString(const UString &s, AString &res,
|
||||
UINT codePage, unsigned utfFlags);
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user