diff options
Diffstat (limited to 'CPP/7zip/Archive/Tar/TarIn.cpp')
-rwxr-xr-x[-rw-r--r--] | CPP/7zip/Archive/Tar/TarIn.cpp | 945 |
1 files changed, 774 insertions, 171 deletions
diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp index 58399d0c..4fd8c5b1 100644..100755 --- a/CPP/7zip/Archive/Tar/TarIn.cpp +++ b/CPP/7zip/Archive/Tar/TarIn.cpp @@ -12,6 +12,45 @@ #include "TarIn.h" +#define NUM_UNROLL_BYTES (8 * 4) + +MY_NO_INLINE static bool IsBufNonZero(const void *data, size_t size); +MY_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 { @@ -41,10 +80,11 @@ static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res, boo return (*end == ' ' || *end == 0); } -static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, bool allowEmpty = false) +static bool OctalToNumber32(const char *srcString, UInt32 &res, bool allowEmpty = false) { + const unsigned kSize = 8; UInt64 res64; - if (!OctalToNumber(srcString, size, res64, allowEmpty)) + if (!OctalToNumber(srcString, kSize, res64, allowEmpty)) return false; res = (UInt32)res64; return (res64 <= 0xFFFFFFFF); @@ -52,68 +92,61 @@ static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, b #define RIF(x) { if (!(x)) return S_OK; } -/* -static bool IsEmptyData(const char *buf, size_t size) -{ - for (unsigned i = 0; i < size; i++) - if (buf[i] != 0) - return false; - return true; -} -*/ - -static bool IsRecordLast(const char *buf) -{ - for (unsigned i = 0; i < NFileHeader::kRecordSize; i++) - if (buf[i] != 0) - return false; - return true; -} - static void ReadString(const char *s, unsigned size, AString &result) { result.SetFrom_CalcLen(s, size); } -static bool ParseInt64(const char *p, Int64 &val) +static bool ParseInt64(const char *p, Int64 &val, bool &isBin) { - UInt32 h = GetBe32(p); + 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; - UInt64 uv; - bool res = OctalToNumber(p, 12, uv); - val = (Int64)uv; + 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) +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); + return ParseInt64(p, val, isBin); val = 0; return true; } -static bool ParseSize(const char *p, UInt64 &val) +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) @@ -126,26 +159,27 @@ API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size) UInt32 mode; // we allow empty Mode value for LongName prefix items - CHECK(OctalToNumber32(p, 8, mode, true)); p += 8; + CHECK(OctalToNumber32(p, mode, true)); p += 8; - // if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0; + // if (!OctalToNumber32(p, item.UID)) item.UID = 0; p += 8; - // if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0; + // if (!OctalToNumber32(p, item.GID)) item.GID = 0; p += 8; UInt64 packSize; Int64 time; UInt32 checkSum; - CHECK(ParseSize(p, packSize)); p += 12; - CHECK(ParseInt64_MTime(p, time)); p += 12; - CHECK(OctalToNumber32(p, 8, 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; } -static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) + +HRESULT CArchive::GetNextItemReal(CItemEx &item) { char buf[NFileHeader::kRecordSize]; - char *p = buf; error = k_ErrorType_OK; filled = false; @@ -154,7 +188,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE for (;;) { size_t processedSize = NFileHeader::kRecordSize; - RINOK(ReadStream(stream, buf, &processedSize)); + RINOK(ReadStream(SeqStream, buf, &processedSize)); if (processedSize == 0) { if (!thereAreEmptyRecords) @@ -180,10 +214,14 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE return S_OK; } - if (!IsRecordLast(buf)) + if (IsBufNonZero(buf, NFileHeader::kRecordSize)) break; item.HeaderSize += NFileHeader::kRecordSize; thereAreEmptyRecords = true; + if (OpenCallback) + { + RINOK(Progress(item, 0)); + } } if (thereAreEmptyRecords) { @@ -191,52 +229,69 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE return S_OK; } + char *p = buf; + error = k_ErrorType_Corrupted; - ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize; - item.NameCouldBeReduced = + + // 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, 8, item.Mode, true)); p += 8; + RIF(OctalToNumber32(p, item.Mode, true)); p += 8; - if (!OctalToNumber32(p, 8, item.UID)) { item.UID = 0; } p += 8; - if (!OctalToNumber32(p, 8, item.GID)) { item.GID = 0; } 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)); + 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)); p += 12; + RIF(ParseInt64_MTime(p, item.MTime, item.MTime_IsBin)); p += 12; UInt32 checkSum; - RIF(OctalToNumber32(p, 8, checkSum)); + RIF(OctalToNumber32(p, checkSum)); memset(p, ' ', 8); p += 8; item.LinkFlag = *p++; ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize; - item.LinkNameCouldBeReduced = + + /* + 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.DeviceMajorDefined = (p[0] != 0); if (item.DeviceMajorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMajor)); } p += 8; - item.DeviceMinorDefined = (p[0] != 0); if (item.DeviceMinorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMinor)); } p += 8; + 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) + if (p[0] != 0 + && item.IsMagic_ustar_5chars() + && (item.LinkFlag != 'L' )) { - AString prefix; - ReadString(p, NFileHeader::kPrefixSize, prefix); - if (!prefix.IsEmpty() - && item.IsUstarMagic() - && (item.LinkFlag != 'L' /* || prefix != "00000000000" */ )) - item.Name = prefix + '/' + item.Name; + 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) @@ -255,22 +310,25 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE /* TAR standard requires sum of unsigned byte values. - But some TAR programs use sum of signed byte values. + But some old TAR programs use sum of signed byte values. So we check both values. */ - UInt32 checkSumReal = 0; - Int32 checkSumReal_Signed = 0; - for (unsigned i = 0; i < NFileHeader::kRecordSize; i++) + // for (int y = 0; y < 100; y++) // for debug { - char c = buf[i]; - checkSumReal_Signed += (signed char)c; - checkSumReal += (Byte)buf[i]; - } - - if (checkSumReal != checkSum) - { - if ((UInt32)checkSumReal_Signed != checkSum) - return S_OK; + 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; @@ -280,7 +338,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE Byte isExtended = (Byte)buf[482]; if (isExtended != 0 && isExtended != 1) return S_OK; - RIF(ParseSize(buf + 483, item.Size)); + RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin)); UInt64 min = 0; for (unsigned i = 0; i < 4; i++) { @@ -309,7 +367,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE while (isExtended != 0) { size_t processedSize = NFileHeader::kRecordSize; - RINOK(ReadStream(stream, buf, &processedSize)); + RINOK(ReadStream(SeqStream, buf, &processedSize)); if (processedSize != NFileHeader::kRecordSize) { error = k_ErrorType_UnexpectedEnd; @@ -317,6 +375,12 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE } item.HeaderSize += NFileHeader::kRecordSize; + + if (OpenCallback) + { + RINOK(Progress(item, 0)); + } + isExtended = (Byte)buf[21 * 24]; if (isExtended != 0 && isExtended != 1) return S_OK; @@ -346,172 +410,711 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE return S_OK; } + if (item.PackSize >= (UInt64)1 << 63) + return S_OK; + filled = true; error = k_ErrorType_OK; return S_OK; } -static HRESULT ReadDataToString(ISequentialInStream *stream, CItemEx &item, AString &s, EErrorType &error) +HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset) { - const unsigned packSize = (unsigned)item.GetPackSizeAligned(); - size_t processedSize = packSize; - HRESULT res = ReadStream(stream, s.GetBuf(packSize), &processedSize); - item.HeaderSize += (unsigned)processedSize; - s.ReleaseBuf_CalcLen((unsigned)item.PackSize); - RINOK(res); - if (processedSize != packSize) - error = k_ErrorType_UnexpectedEnd; + 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; } + -static bool ParsePaxLongName(const AString &src, AString &dest) + +struct CPaxInfo: public CPaxTimes { - dest.Empty(); - for (unsigned pos = 0;;) + 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 (pos >= src.Len()) + if (defined) + DoubleTagError = true; + if (val.IsEmpty()) return false; - const char *start = src.Ptr(pos); - const char *end; - const UInt32 lineLen = ConvertStringToUInt32(start, &end); - if (end == start) + const char *end2; + res = ConvertStringToUInt32(val.Ptr(), &end2); + if (*end2 != 0) return false; - if (*end != ' ') + 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 (lineLen > src.Len() - pos) + if (sec >= ((UInt64)1 << 63)) return false; - unsigned offset = (unsigned)(end - start) + 1; - if (lineLen < offset) + if (isNegative) + sec = -(Int64)sec; + pt.Sec = 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; - if (IsString1PrefixedByString2(src.Ptr(pos + offset), "path=")) + // we ignore digits after 9 digits as GNU TAR + if (i < kNsDigits) + { + ns *= 10; + ns += c - '0'; + } + } + pt.NumDigits = (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++) { - offset += 5; // "path=" - dest = src.Mid(pos + offset, lineLen - offset); - if (dest.IsEmpty()) + if (i > 24 || i >= rem) // we use limitation for size of (size) field return false; - if (dest.Back() != '\n') + 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; - dest.DeleteBack(); - return true; + + 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, 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; } - pos += lineLen; + 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 ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) + +HRESULT CArchive::ReadItem2(CItemEx &item) { + // CItem + + item.SparseBlocks.Clear(); + item.PaxTimes.Clear(); + + // CItemEx + item.HeaderSize = 0; + item.Num_Pax_Records = 0; - bool flagL = false; - bool flagK = false; - AString nameL; - AString nameK; - AString pax; + 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; - for (;;) + 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(); + + for (unsigned recordIndex = 0;; recordIndex++) { - RINOK(GetNextItemReal(stream, filled, item, error)); + if (OpenCallback) + { + RINOK(Progress(item, 0)); + } + + RINOK(GetNextItemReal(item)); + + // NumRecords++; + if (!filled) { - if (error == k_ErrorType_OK && (flagL || flagK)) + if (error == k_ErrorType_OK) + if (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; - if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName || // file contains a long name - item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongLink) // file contains a long linkname + const char lf = item.LinkFlag; + if (lf == NFileHeader::NLinkFlag::kGnu_LongName || + lf == NFileHeader::NLinkFlag::kGnu_LongLink) { - AString *name; - if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName) - { if (flagL) return S_OK; flagL = true; name = &nameL; } - else - { if (flagK) return S_OK; flagK = true; name = &nameK; } - + // 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; - if (item.PackSize > (1 << 14)) - return S_OK; + } + */ - RINOK(ReadDataToString(stream, item, *name, error)); + 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; } - switch (item.LinkFlag) + if (lf == NFileHeader::NLinkFlag::kGlobal || + lf == NFileHeader::NLinkFlag::kPax || + lf == NFileHeader::NLinkFlag::kPax_2) { - case 'g': - case 'x': - case 'X': + // GNU tar ignores item.Name after LinkFlag test + // 22.00 : now we also ignore item.Name here + /* + if (item.PackSize > (UInt32)1 << 26) { - const char *s = item.Name.Ptr(); - if (IsString1PrefixedByString2(s, "./")) - s += 2; - if (IsString1PrefixedByString2(s, "./")) - s += 2; - if ( IsString1PrefixedByString2(s, "PaxHeader/") - || IsString1PrefixedByString2(s, "PaxHeaders.X/") - || IsString1PrefixedByString2(s, "PaxHeaders.4467/") - || StringsAreEqual_Ascii(s, "@PaxHeader") - ) - { - RINOK(ReadDataToString(stream, item, pax, error)); - if (error != k_ErrorType_OK) - return S_OK; - continue; - } - break; + break; // we don't want big PaxBuf files + // return S_OK; } - case NFileHeader::NLinkFlag::kDumpDir: + */ + 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) { - break; - // GNU Extensions to the Archive Format + item.PaxExtra.RecordPath = item.Name; + continue; } - case NFileHeader::NLinkFlag::kSparse: + // break; // for debug { - break; - // GNU Extensions to the Archive Format + 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) + { + // we skip global pax header info after parsing + item.HeaderPos += item.HeaderSize; + item.HeaderSize = 0; + } } - default: - if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0)) - return S_OK; + continue; } - if (flagL) + /* + if (lf == NFileHeader::NLinkFlag::kDumpDir || + lf == NFileHeader::NLinkFlag::kSparse) { - item.Name = nameL; - item.NameCouldBeReduced = false; + // GNU Extensions to the Archive Format + break; } - - if (flagK) + if (lf > '7' || (lf < '0' && lf != 0)) { - item.LinkName = nameK; - item.LinkNameCouldBeReduced = false; + break; + // return S_OK; } - - error = k_ErrorType_OK; - - if (!pax.IsEmpty()) + */ + 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 { - AString name; - if (ParsePaxLongName(pax, name)) - item.Name = name; - 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) { - // no "path" property is allowed in pax4467 - // error = k_ErrorType_Warning; + item.UID = (UInt32)paxInfo.UID; } - pax.Empty(); + 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; + 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; } }} |