blob: 90b6f8438d70ab38cca09fd4d459f6c8bfaa2f59 [file] [log] [blame]
// TarIn.cpp
#include "StdAfx.h"
#include "../../../../C/CpuArch.h"
#include "../../../Common/StringToInt.h"
#include "../../Common/StreamUtils.h"
#include "../IArchive.h"
#include "TarIn.h"
#define NUM_UNROLL_BYTES (8 * 4)
Z7_NO_INLINE static bool IsBufNonZero(const void *data, size_t size);
Z7_NO_INLINE static bool IsBufNonZero(const void *data, size_t size)
{
const Byte *p = (const Byte *)data;
for (; size != 0 && ((unsigned)(ptrdiff_t)p & (NUM_UNROLL_BYTES - 1)) != 0; size--)
if (*p++ != 0)
return true;
if (size >= NUM_UNROLL_BYTES)
{
const Byte *lim = p + size;
size &= (NUM_UNROLL_BYTES - 1);
lim -= size;
do
{
if (*(const UInt64 *)(const void *)(p ) != 0) return true;
if (*(const UInt64 *)(const void *)(p + 8 * 1) != 0) return true;
if (*(const UInt64 *)(const void *)(p + 8 * 2) != 0) return true;
if (*(const UInt64 *)(const void *)(p + 8 * 3) != 0) return true;
// if (*(const UInt32 *)(const void *)(p ) != 0) return true;
// if (*(const UInt32 *)(const void *)(p + 4 * 1) != 0) return true;
// if (*(const UInt32 *)(const void *)(p + 4 * 2) != 0) return true;
// if (*(const UInt32 *)(const void *)(p + 4 * 3) != 0) return true;
p += NUM_UNROLL_BYTES;
}
while (p != lim);
}
for (; size != 0; size--)
if (*p++ != 0)
return true;
return false;
}
namespace NArchive {
namespace NTar {
static void MyStrNCpy(char *dest, const char *src, unsigned size)
{
for (unsigned i = 0; i < size; i++)
{
char c = src[i];
dest[i] = c;
if (c == 0)
break;
}
}
static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res, bool allowEmpty = false)
{
res = 0;
char sz[32];
MyStrNCpy(sz, srcString, size);
sz[size] = 0;
const char *end;
unsigned i;
for (i = 0; sz[i] == ' '; i++);
if (sz[i] == 0)
return allowEmpty;
res = ConvertOctStringToUInt64(sz + i, &end);
return (*end == ' ' || *end == 0);
}
static bool OctalToNumber32(const char *srcString, UInt32 &res, bool allowEmpty = false)
{
const unsigned kSize = 8;
UInt64 res64;
if (!OctalToNumber(srcString, kSize, res64, allowEmpty))
return false;
res = (UInt32)res64;
return (res64 <= 0xFFFFFFFF);
}
#define RIF(x) { if (!(x)) return S_OK; }
static void ReadString(const char *s, unsigned size, AString &result)
{
result.SetFrom_CalcLen(s, size);
}
static bool ParseInt64(const char *p, Int64 &val, bool &isBin)
{
const UInt32 h = GetBe32(p);
val = (Int64)GetBe64(p + 4);
isBin = true;
if (h == (UInt32)1 << 31)
return ((val >> 63) & 1) == 0;
if (h == (UInt32)(Int32)-1)
return ((val >> 63) & 1) != 0;
isBin = false;
UInt64 u;
const bool res = OctalToNumber(p, 12, u);
val = (Int64)u;
return res;
}
static bool ParseInt64_MTime(const char *p, Int64 &val, bool &isBin)
{
// rare case tar : ZEROs in Docker-Windows TARs
// rare case tar : spaces
isBin = false;
if (GetUi32(p) != 0)
for (unsigned i = 0; i < 12; i++)
if (p[i] != ' ')
return ParseInt64(p, val, isBin);
val = 0;
return true;
}
static bool ParseSize(const char *p, UInt64 &val, bool &isBin)
{
if (GetBe32(p) == (UInt32)1 << 31)
{
// GNU extension
isBin = true;
val = GetBe64(p + 4);
return ((val >> 63) & 1) == 0;
}
isBin = false;
return OctalToNumber(p, 12, val,
true // 20.03: allow empty size for 'V' Label entry
);
}
static bool ParseSize(const char *p, UInt64 &val)
{
bool isBin;
return ParseSize(p, val, isBin);
}
#define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
{
if (size < NFileHeader::kRecordSize)
return k_IsArc_Res_NEED_MORE;
const char *p = (const char *)p2;
p += NFileHeader::kNameSize;
UInt32 mode;
// we allow empty Mode value for LongName prefix items
CHECK(OctalToNumber32(p, mode, true)) p += 8;
// if (!OctalToNumber32(p, item.UID)) item.UID = 0;
p += 8;
// if (!OctalToNumber32(p, item.GID)) item.GID = 0;
p += 8;
UInt64 packSize;
Int64 time;
UInt32 checkSum;
bool isBin;
CHECK(ParseSize(p, packSize, isBin)) p += 12;
CHECK(ParseInt64_MTime(p, time, isBin)) p += 12;
CHECK(OctalToNumber32(p, checkSum))
return k_IsArc_Res_YES;
}
HRESULT CArchive::GetNextItemReal(CItemEx &item)
{
char buf[NFileHeader::kRecordSize];
error = k_ErrorType_OK;
filled = false;
bool thereAreEmptyRecords = false;
for (;;)
{
size_t processedSize = NFileHeader::kRecordSize;
RINOK(ReadStream(SeqStream, buf, &processedSize))
if (processedSize == 0)
{
if (!thereAreEmptyRecords)
error = k_ErrorType_UnexpectedEnd; // "There are no trailing zero-filled records";
return S_OK;
}
if (processedSize != NFileHeader::kRecordSize)
{
if (!thereAreEmptyRecords)
error = k_ErrorType_UnexpectedEnd; // error = "There is no correct record at the end of archive";
else
{
/*
if (IsEmptyData(buf, processedSize))
error = k_ErrorType_UnexpectedEnd;
else
{
// extraReadSize = processedSize;
// error = k_ErrorType_Corrupted; // some data after the end tail zeros
}
*/
}
return S_OK;
}
if (IsBufNonZero(buf, NFileHeader::kRecordSize))
break;
item.HeaderSize += NFileHeader::kRecordSize;
thereAreEmptyRecords = true;
if (OpenCallback)
{
RINOK(Progress(item, 0))
}
}
if (thereAreEmptyRecords)
{
// error = "There are data after end of archive";
return S_OK;
}
char *p = buf;
error = k_ErrorType_Corrupted;
// ReadString(p, NFileHeader::kNameSize, item.Name);
p += NFileHeader::kNameSize;
/*
item.Name_CouldBeReduced =
(item.Name.Len() == NFileHeader::kNameSize ||
item.Name.Len() == NFileHeader::kNameSize - 1);
*/
// we allow empty Mode value for LongName prefix items
RIF(OctalToNumber32(p, item.Mode, true)) p += 8;
if (!OctalToNumber32(p, item.UID)) { item.UID = 0; } p += 8;
if (!OctalToNumber32(p, item.GID)) { item.GID = 0; } p += 8;
RIF(ParseSize(p, item.PackSize, item.PackSize_IsBin))
item.Size = item.PackSize;
item.Size_IsBin = item.PackSize_IsBin;
p += 12;
RIF(ParseInt64_MTime(p, item.MTime, item.MTime_IsBin)) p += 12;
UInt32 checkSum;
RIF(OctalToNumber32(p, checkSum))
memset(p, ' ', 8); p += 8;
item.LinkFlag = *p++;
ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
/*
item.LinkName_CouldBeReduced =
(item.LinkName.Len() == NFileHeader::kNameSize ||
item.LinkName.Len() == NFileHeader::kNameSize - 1);
*/
memcpy(item.Magic, p, 8); p += 8;
ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
item.DeviceMajor_Defined = (p[0] != 0); if (item.DeviceMajor_Defined) { RIF(OctalToNumber32(p, item.DeviceMajor)) } p += 8;
item.DeviceMinor_Defined = (p[0] != 0); if (item.DeviceMinor_Defined) { RIF(OctalToNumber32(p, item.DeviceMinor)) } p += 8;
if (p[0] != 0
&& item.IsMagic_ustar_5chars()
&& (item.LinkFlag != 'L' ))
{
item.Prefix_WasUsed = true;
ReadString(p, NFileHeader::kPrefixSize, item.Name);
item.Name += '/';
unsigned i;
for (i = 0; i < NFileHeader::kNameSize; i++)
if (buf[i] == 0)
break;
item.Name.AddFrom(buf, i);
}
else
ReadString(buf, NFileHeader::kNameSize, item.Name);
p += NFileHeader::kPrefixSize;
if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
{
item.PackSize = 0;
item.Size = 0;
}
if (item.LinkFlag == NFileHeader::NLinkFlag::kDirectory)
{
// GNU tar ignores Size field, if LinkFlag is kDirectory
// 21.02 : we set PackSize = 0 to be more compatible with GNU tar
item.PackSize = 0;
// item.Size = 0;
}
/*
TAR standard requires sum of unsigned byte values.
But some old TAR programs use sum of signed byte values.
So we check both values.
*/
// for (int y = 0; y < 100; y++) // for debug
{
UInt32 sum0 = 0;
{
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
sum0 += (Byte)buf[i];
}
if (sum0 != checkSum)
{
Int32 sum = 0;
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
sum += (signed char)buf[i];
if ((UInt32)sum != checkSum)
return S_OK;
item.IsSignedChecksum = true;
}
}
item.HeaderSize += NFileHeader::kRecordSize;
if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse)
{
Byte isExtended = (Byte)buf[482];
if (isExtended != 0 && isExtended != 1)
return S_OK;
RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin))
UInt64 min = 0;
for (unsigned i = 0; i < 4; i++)
{
p = buf + 386 + 24 * i;
if (GetBe32(p) == 0)
{
if (isExtended != 0)
return S_OK;
break;
}
CSparseBlock sb;
RIF(ParseSize(p, sb.Offset))
RIF(ParseSize(p + 12, sb.Size))
item.SparseBlocks.Add(sb);
if (sb.Offset < min || sb.Offset > item.Size)
return S_OK;
if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
return S_OK;
min = sb.Offset + sb.Size;
if (min < sb.Offset)
return S_OK;
}
if (min > item.Size)
return S_OK;
while (isExtended != 0)
{
size_t processedSize = NFileHeader::kRecordSize;
RINOK(ReadStream(SeqStream, buf, &processedSize))
if (processedSize != NFileHeader::kRecordSize)
{
error = k_ErrorType_UnexpectedEnd;
return S_OK;
}
item.HeaderSize += NFileHeader::kRecordSize;
if (OpenCallback)
{
RINOK(Progress(item, 0))
}
isExtended = (Byte)buf[21 * 24];
if (isExtended != 0 && isExtended != 1)
return S_OK;
for (unsigned i = 0; i < 21; i++)
{
p = buf + 24 * i;
if (GetBe32(p) == 0)
{
if (isExtended != 0)
return S_OK;
break;
}
CSparseBlock sb;
RIF(ParseSize(p, sb.Offset))
RIF(ParseSize(p + 12, sb.Size))
item.SparseBlocks.Add(sb);
if (sb.Offset < min || sb.Offset > item.Size)
return S_OK;
if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
return S_OK;
min = sb.Offset + sb.Size;
if (min < sb.Offset)
return S_OK;
}
}
if (min > item.Size)
return S_OK;
}
if (item.PackSize >= (UInt64)1 << 63)
return S_OK;
filled = true;
error = k_ErrorType_OK;
return S_OK;
}
HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset)
{
const UInt64 pos = item.Get_DataPos() + posOffset;
if (NumFiles - NumFiles_Prev < (1 << 16)
// && NumRecords - NumRecords_Prev < (1 << 16)
&& pos - Pos_Prev < ((UInt32)1 << 28))
return S_OK;
{
Pos_Prev = pos;
NumFiles_Prev = NumFiles;
// NumRecords_Prev = NumRecords;
// Sleep(100); // for debug
return OpenCallback->SetCompleted(&NumFiles, &pos);
}
}
HRESULT CArchive::ReadDataToBuffer(const CItemEx &item,
CTempBuffer &tb, size_t stringLimit)
{
tb.Init();
UInt64 packSize = item.Get_PackSize_Aligned();
if (packSize == 0)
return S_OK;
UInt64 pos;
{
size_t size = stringLimit;
if (size > packSize)
size = (size_t)packSize;
tb.Buffer.AllocAtLeast(size);
size_t processedSize = size;
const HRESULT res = ReadStream(SeqStream, tb.Buffer, &processedSize);
pos = processedSize;
if (processedSize != size)
{
error = k_ErrorType_UnexpectedEnd;
return res;
}
RINOK(res)
packSize -= size;
size_t i;
const Byte *p = tb.Buffer;
for (i = 0; i < size; i++)
if (p[i] == 0)
break;
if (i >= item.PackSize)
tb.StringSize_IsConfirmed = true;
if (i > item.PackSize)
{
tb.StringSize = (size_t)item.PackSize;
tb.IsNonZeroTail = true;
}
else
{
tb.StringSize = i;
if (i != size)
{
tb.StringSize_IsConfirmed = true;
if (IsBufNonZero(p + i, size - i))
tb.IsNonZeroTail = true;
}
}
if (packSize == 0)
return S_OK;
}
if (InStream)
{
RINOK(InStream->Seek((Int64)packSize, STREAM_SEEK_CUR, NULL))
return S_OK;
}
const unsigned kBufSize = 1 << 15;
Buffer.AllocAtLeast(kBufSize);
do
{
if (OpenCallback)
{
RINOK(Progress(item, pos))
}
unsigned size = kBufSize;
if (size > packSize)
size = (unsigned)packSize;
size_t processedSize = size;
const HRESULT res = ReadStream(SeqStream, Buffer, &processedSize);
if (processedSize != size)
{
error = k_ErrorType_UnexpectedEnd;
return res;
}
if (!tb.IsNonZeroTail)
{
if (IsBufNonZero(Buffer, size))
tb.IsNonZeroTail = true;
}
packSize -= size;
pos += size;
}
while (packSize != 0);
return S_OK;
}
struct CPaxInfo: public CPaxTimes
{
bool DoubleTagError;
bool TagParsingError;
bool UnknownLines_Overflow;
bool Size_Defined;
bool UID_Defined;
bool GID_Defined;
bool Path_Defined;
bool Link_Defined;
bool User_Defined;
bool Group_Defined;
UInt64 Size;
UInt32 UID;
UInt32 GID;
AString Path;
AString Link;
AString User;
AString Group;
AString UnknownLines;
bool ParseID(const AString &val, bool &defined, UInt32 &res)
{
if (defined)
DoubleTagError = true;
if (val.IsEmpty())
return false;
const char *end2;
res = ConvertStringToUInt32(val.Ptr(), &end2);
if (*end2 != 0)
return false;
defined = true;
return true;
}
bool ParsePax(const CTempBuffer &tb, bool isFile);
};
static bool ParsePaxTime(const AString &src, CPaxTime &pt, bool &doubleTagError)
{
if (pt.IsDefined())
doubleTagError = true;
pt.Clear();
const char *s = src.Ptr();
bool isNegative = false;
if (*s == '-')
{
isNegative = true;
s++;
}
const char *end;
{
UInt64 sec = ConvertStringToUInt64(s, &end);
if (s == end)
return false;
if (sec >= ((UInt64)1 << 63))
return false;
if (isNegative)
sec = (UInt64)-(Int64)sec;
pt.Sec = (Int64)sec;
}
if (*end == 0)
{
pt.Ns = 0;
pt.NumDigits = 0;
return true;
}
if (*end != '.')
return false;
s = end + 1;
UInt32 ns = 0;
unsigned i;
const unsigned kNsDigits = 9;
for (i = 0;; i++)
{
const char c = s[i];
if (c == 0)
break;
if (c < '0' || c > '9')
return false;
// we ignore digits after 9 digits as GNU TAR
if (i < kNsDigits)
{
ns *= 10;
ns += (unsigned)(c - '0');
}
}
pt.NumDigits = (int)(i < kNsDigits ? i : kNsDigits);
while (i < kNsDigits)
{
ns *= 10;
i++;
}
if (isNegative && ns != 0)
{
pt.Sec--;
ns = (UInt32)1000 * 1000 * 1000 - ns;
}
pt.Ns = ns;
return true;
}
bool CPaxInfo::ParsePax(const CTempBuffer &tb, bool isFile)
{
DoubleTagError = false;
TagParsingError = false;
UnknownLines_Overflow = false;
Size_Defined = false;
UID_Defined = false;
GID_Defined = false;
Path_Defined = false;
Link_Defined = false;
User_Defined = false;
Group_Defined = false;
// CPaxTimes::Clear();
const char *s = (const char *)(const void *)(const Byte *)tb.Buffer;
size_t rem = tb.StringSize;
Clear();
AString name, val;
while (rem != 0)
{
unsigned i;
for (i = 0;; i++)
{
if (i > 24 || i >= rem) // we use limitation for size of (size) field
return false;
if (s[i] == ' ')
break;
}
if (i == 0)
return false;
const char *end;
const UInt32 size = ConvertStringToUInt32(s, &end);
const unsigned offset = (unsigned)(end - s) + 1;
if (size > rem
|| size <= offset + 1
|| offset != i + 1
|| s[size - 1] != '\n')
return false;
for (i = offset; i < size; i++)
if (s[i] == 0)
return false;
for (i = offset; i < size - 1; i++)
if (s[i] == '=')
break;
if (i == size - 1)
return false;
name.SetFrom(s + offset, i - offset);
val.SetFrom(s + i + 1, (unsigned)(size - 1 - (i + 1)));
bool parsed = false;
if (isFile)
{
bool isDetectedName = true;
// only lower case (name) is supported
if (name.IsEqualTo("path"))
{
if (Path_Defined)
DoubleTagError = true;
Path = val;
Path_Defined = true;
parsed = true;
}
else if (name.IsEqualTo("linkpath"))
{
if (Link_Defined)
DoubleTagError = true;
Link = val;
Link_Defined = true;
parsed = true;
}
else if (name.IsEqualTo("uname"))
{
if (User_Defined)
DoubleTagError = true;
User = val;
User_Defined = true;
parsed = true;
}
else if (name.IsEqualTo("gname"))
{
if (Group_Defined)
DoubleTagError = true;
Group = val;
Group_Defined = true;
parsed = true;
}
else if (name.IsEqualTo("uid"))
{
parsed = ParseID(val, UID_Defined, UID);
}
else if (name.IsEqualTo("gid"))
{
parsed = ParseID(val, GID_Defined, GID);
}
else if (name.IsEqualTo("size"))
{
if (Size_Defined)
DoubleTagError = true;
Size_Defined = false;
if (!val.IsEmpty())
{
const char *end2;
Size = ConvertStringToUInt64(val.Ptr(), &end2);
if (*end2 == 0)
{
Size_Defined = true;
parsed = true;
}
}
}
else if (name.IsEqualTo("mtime"))
{ parsed = ParsePaxTime(val, MTime, DoubleTagError); }
else if (name.IsEqualTo("atime"))
{ parsed = ParsePaxTime(val, ATime, DoubleTagError); }
else if (name.IsEqualTo("ctime"))
{ parsed = ParsePaxTime(val, CTime, DoubleTagError); }
else
isDetectedName = false;
if (isDetectedName && !parsed)
TagParsingError = true;
}
if (!parsed)
{
if (!UnknownLines_Overflow)
{
const unsigned addSize = size - offset;
if (UnknownLines.Len() + addSize < (1 << 16))
UnknownLines.AddFrom(s + offset, addSize);
else
UnknownLines_Overflow = true;
}
}
s += size;
rem -= size;
}
return true;
}
HRESULT CArchive::ReadItem2(CItemEx &item)
{
// CItem
item.SparseBlocks.Clear();
item.PaxTimes.Clear();
// CItemEx
item.HeaderSize = 0;
item.Num_Pax_Records = 0;
item.LongName_WasUsed = false;
item.LongName_WasUsed_2 = false;
item.LongLink_WasUsed = false;
item.LongLink_WasUsed_2 = false;
item.HeaderError = false;
item.IsSignedChecksum = false;
item.Prefix_WasUsed = false;
item.Pax_Error = false;
item.Pax_Overflow = false;
item.pax_path_WasUsed = false;
item.pax_link_WasUsed = false;
item.pax_size_WasUsed = false;
item.PaxExtra.Clear();
item.EncodingCharacts.Clear();
// CArchive temp variable
NameBuf.Init();
LinkBuf.Init();
PaxBuf.Init();
PaxBuf_global.Init();
UInt64 numExtraRecords = 0;
for (;;)
{
if (OpenCallback)
{
RINOK(Progress(item, 0))
}
RINOK(GetNextItemReal(item))
// NumRecords++;
if (!filled)
{
if (error == k_ErrorType_OK)
if (numExtraRecords != 0
|| item.LongName_WasUsed
|| item.LongLink_WasUsed
|| item.Num_Pax_Records != 0)
error = k_ErrorType_Corrupted;
return S_OK;
}
if (error != k_ErrorType_OK)
return S_OK;
numExtraRecords++;
const char lf = item.LinkFlag;
if (lf == NFileHeader::NLinkFlag::kGnu_LongName ||
lf == NFileHeader::NLinkFlag::kGnu_LongLink)
{
// GNU tar ignores item.Name after LinkFlag test
// 22.00 : now we also ignore item.Name here
/*
if (item.Name != NFileHeader::kLongLink &&
item.Name != NFileHeader::kLongLink2)
{
break;
// return S_OK;
}
*/
CTempBuffer *tb =
lf == NFileHeader::NLinkFlag::kGnu_LongName ?
&NameBuf :
&LinkBuf;
/*
if (item.PackSize > (1 << 29))
{
// break;
return S_OK;
}
*/
const unsigned kLongNameSizeMax = (unsigned)1 << 14;
RINOK(ReadDataToBuffer(item, *tb, kLongNameSizeMax))
if (error != k_ErrorType_OK)
return S_OK;
if (lf == NFileHeader::NLinkFlag::kGnu_LongName)
{
item.LongName_WasUsed_2 =
item.LongName_WasUsed;
item.LongName_WasUsed = true;
}
else
{
item.LongLink_WasUsed_2 =
item.LongLink_WasUsed;
item.LongLink_WasUsed = true;
}
if (!tb->StringSize_IsConfirmed)
tb->StringSize = 0;
item.HeaderSize += item.Get_PackSize_Aligned();
if (tb->StringSize == 0 ||
tb->StringSize + 1 != item.PackSize)
item.HeaderError = true;
if (tb->IsNonZeroTail)
item.HeaderError = true;
continue;
}
if (lf == NFileHeader::NLinkFlag::kGlobal ||
lf == NFileHeader::NLinkFlag::kPax ||
lf == NFileHeader::NLinkFlag::kPax_2)
{
// GNU tar ignores item.Name after LinkFlag test
// 22.00 : now we also ignore item.Name here
/*
if (item.PackSize > (UInt32)1 << 26)
{
break; // we don't want big PaxBuf files
// return S_OK;
}
*/
const unsigned kParsingPaxSizeMax = (unsigned)1 << 26;
const bool isStartHeader = (item.HeaderSize == NFileHeader::kRecordSize);
CTempBuffer *tb = (lf == NFileHeader::NLinkFlag::kGlobal ? &PaxBuf_global : &PaxBuf);
RINOK(ReadDataToBuffer(item, *tb, kParsingPaxSizeMax))
if (error != k_ErrorType_OK)
return S_OK;
item.HeaderSize += item.Get_PackSize_Aligned();
if (tb->StringSize != item.PackSize
|| tb->StringSize == 0
|| tb->IsNonZeroTail)
item.Pax_Error = true;
item.Num_Pax_Records++;
if (lf != NFileHeader::NLinkFlag::kGlobal)
{
item.PaxExtra.RecordPath = item.Name;
continue;
}
// break; // for debug
{
if (PaxGlobal_Defined)
_is_PaxGlobal_Error = true;
CPaxInfo paxInfo;
if (paxInfo.ParsePax(PaxBuf_global, false))
{
PaxGlobal.RawLines = paxInfo.UnknownLines;
PaxGlobal.RecordPath = item.Name;
PaxGlobal_Defined = true;
}
else
_is_PaxGlobal_Error = true;
if (isStartHeader
&& item.Num_Pax_Records == 1
&& numExtraRecords == 1)
{
// we skip global pax header info after parsing
item.HeaderPos += item.HeaderSize;
item.HeaderSize = 0;
item.Num_Pax_Records = 0;
numExtraRecords = 0;
}
else
_is_PaxGlobal_Error = true;
}
continue;
}
/*
if (lf == NFileHeader::NLinkFlag::kDumpDir ||
lf == NFileHeader::NLinkFlag::kSparse)
{
// GNU Extensions to the Archive Format
break;
}
if (lf > '7' || (lf < '0' && lf != 0))
{
break;
// return S_OK;
}
*/
break;
}
// we still use name from main header, if long_name is bad
if (item.LongName_WasUsed && NameBuf.StringSize != 0)
{
NameBuf.CopyToString(item.Name);
// item.Name_CouldBeReduced = false;
}
if (item.LongLink_WasUsed)
{
// we use empty link, if long_link is bad
LinkBuf.CopyToString(item.LinkName);
// item.LinkName_CouldBeReduced = false;
}
error = k_ErrorType_OK;
if (PaxBuf.StringSize != 0)
{
CPaxInfo paxInfo;
if (!paxInfo.ParsePax(PaxBuf, true))
item.Pax_Error = true;
else
{
if (paxInfo.Path_Defined) // if (!paxInfo.Path.IsEmpty())
{
item.Name = paxInfo.Path;
item.pax_path_WasUsed = true;
}
if (paxInfo.Link_Defined) // (!paxInfo.Link.IsEmpty())
{
item.LinkName = paxInfo.Link;
item.pax_link_WasUsed = true;
}
if (paxInfo.User_Defined)
{
item.User = paxInfo.User;
// item.pax_uname_WasUsed = true;
}
if (paxInfo.Group_Defined)
{
item.Group = paxInfo.Group;
// item.pax_gname_WasUsed = true;
}
if (paxInfo.UID_Defined)
{
item.UID = (UInt32)paxInfo.UID;
}
if (paxInfo.GID_Defined)
{
item.GID = (UInt32)paxInfo.GID;
}
if (paxInfo.Size_Defined)
{
const UInt64 piSize = paxInfo.Size;
// GNU TAR ignores (item.Size) in that case
if (item.Size != 0 && item.Size != piSize)
item.Pax_Error = true;
item.Size = piSize;
item.PackSize = piSize;
item.pax_size_WasUsed = true;
}
item.PaxTimes = paxInfo;
item.PaxExtra.RawLines = paxInfo.UnknownLines;
if (paxInfo.UnknownLines_Overflow)
item.Pax_Overflow = true;
if (paxInfo.TagParsingError)
item.Pax_Error = true;
if (paxInfo.DoubleTagError)
item.Pax_Error = true;
}
}
return S_OK;
}
HRESULT CArchive::ReadItem(CItemEx &item)
{
item.HeaderPos = _phySize;
const HRESULT res = ReadItem2(item);
/*
if (error == k_ErrorType_Warning)
_is_Warning = true;
else
*/
if (error != k_ErrorType_OK)
_error = error;
RINOK(res)
if (filled)
{
if (item.IsMagic_GNU())
_are_Gnu = true;
else if (item.IsMagic_Posix_ustar_00())
_are_Posix = true;
if (item.Num_Pax_Records != 0)
_are_Pax = true;
if (item.PaxTimes.MTime.IsDefined()) _are_mtime = true;
if (item.PaxTimes.ATime.IsDefined()) _are_atime = true;
if (item.PaxTimes.CTime.IsDefined()) _are_ctime = true;
if (item.pax_path_WasUsed)
_are_pax_path = true;
if (item.pax_link_WasUsed)
_are_pax_link = true;
if (item.LongName_WasUsed)
_are_LongName = true;
if (item.LongLink_WasUsed)
_are_LongLink = true;
if (item.Prefix_WasUsed)
_pathPrefix_WasUsed = true;
/*
if (item.IsSparse())
_isSparse = true;
*/
if (item.Is_PaxExtendedHeader())
_are_Pax_Items = true;
if (item.IsThereWarning()
|| item.HeaderError
|| item.Pax_Error)
_is_Warning = true;
}
const UInt64 headerEnd = item.HeaderPos + item.HeaderSize;
// _headersSize += headerEnd - _phySize;
// we don't count skipped records
_headersSize += item.HeaderSize;
_phySize = headerEnd;
return S_OK;
}
}}