p7zip/CPP/7zip/Archive/Tar/TarIn.cpp

423 lines
10 KiB
C++
Raw Normal View History

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