7zip/CPP/7zip/Archive/GptHandler.cpp
Igor Pavlov e008ce3976 24.08
2024-08-12 16:50:32 +05:00

474 lines
11 KiB
C++

// GptHandler.cpp
#include "StdAfx.h"
#include "../../../C/7zCrc.h"
#include "../../../C/CpuArch.h"
#include "../../Common/ComTry.h"
#include "../../Common/IntToString.h"
#include "../../Common/MyBuffer.h"
#include "../../Windows/PropVariantUtils.h"
#include "../Common/RegisterArc.h"
#include "../Common/StreamUtils.h"
#include "HandlerCont.h"
#define Get16(p) GetUi16(p)
#define Get32(p) GetUi32(p)
#define Get64(p) GetUi64(p)
using namespace NWindows;
namespace NArchive {
namespace NMbr {
const char *GetFileSystem(ISequentialInStream *stream, UInt64 partitionSize);
}
namespace NFat {
API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size);
}
namespace NGpt {
static const unsigned k_SignatureSize = 12;
static const Byte k_Signature[k_SignatureSize] =
{ 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T', 0, 0, 1, 0 };
static const CUInt32PCharPair g_PartitionFlags[] =
{
{ 0, "Sys" },
{ 1, "Ignore" },
{ 2, "Legacy" },
{ 60, "Win-Read-only" },
{ 62, "Win-Hidden" },
{ 63, "Win-Not-Automount" }
};
static const unsigned kNameLen = 36;
struct CPartition
{
Byte Type[16];
Byte Id[16];
UInt64 FirstLba;
UInt64 LastLba;
UInt64 Flags;
const char *Ext; // detected later
Byte Name[kNameLen * 2];
bool IsUnused() const
{
for (unsigned i = 0; i < 16; i++)
if (Type[i] != 0)
return false;
return true;
}
UInt64 GetSize(unsigned sectorSizeLog) const { return (LastLba - FirstLba + 1) << sectorSizeLog; }
UInt64 GetPos(unsigned sectorSizeLog) const { return FirstLba << sectorSizeLog; }
UInt64 GetEnd(unsigned sectorSizeLog) const { return (LastLba + 1) << sectorSizeLog; }
void Parse(const Byte *p)
{
memcpy(Type, p, 16);
memcpy(Id, p + 16, 16);
FirstLba = Get64(p + 32);
LastLba = Get64(p + 40);
Flags = Get64(p + 48);
memcpy(Name, p + 56, kNameLen * 2);
Ext = NULL;
}
};
struct CPartType
{
UInt32 Id;
const char *Ext;
const char *Type;
};
static const CPartType kPartTypes[] =
{
// { 0x0, NULL, "Unused" },
{ 0x21686148, NULL, "BIOS Boot" },
{ 0xC12A7328, NULL, "EFI System" },
{ 0x024DEE41, NULL, "MBR" },
{ 0xE3C9E316, NULL, "Windows MSR" },
{ 0xEBD0A0A2, NULL, "Windows BDP" },
{ 0x5808C8AA, NULL, "Windows LDM Metadata" },
{ 0xAF9B60A0, NULL, "Windows LDM Data" },
{ 0xDE94BBA4, NULL, "Windows Recovery" },
// { 0x37AFFC90, NULL, "IBM GPFS" },
// { 0xE75CAF8F, NULL, "Windows Storage Spaces" },
{ 0x0FC63DAF, NULL, "Linux Data" },
{ 0x0657FD6D, NULL, "Linux Swap" },
{ 0x44479540, NULL, "Linux root (x86)" },
{ 0x4F68BCE3, NULL, "Linux root (x86-64)" },
{ 0x69DAD710, NULL, "Linux root (ARM)" },
{ 0xB921B045, NULL, "Linux root (ARM64)" },
{ 0x993D8D3D, NULL, "Linux root (IA-64)" },
{ 0x83BD6B9D, NULL, "FreeBSD Boot" },
{ 0x516E7CB4, NULL, "FreeBSD Data" },
{ 0x516E7CB5, NULL, "FreeBSD Swap" },
{ 0x516E7CB6, "ufs", "FreeBSD UFS" },
{ 0x516E7CB8, NULL, "FreeBSD Vinum" },
{ 0x516E7CB8, "zfs", "FreeBSD ZFS" },
{ 0x48465300, "hfsx", "HFS+" },
{ 0x7C3457EF, "apfs", "APFS" },
};
static int FindPartType(const Byte *guid)
{
const UInt32 val = Get32(guid);
for (unsigned i = 0; i < Z7_ARRAY_SIZE(kPartTypes); i++)
if (kPartTypes[i].Id == val)
return (int)i;
return -1;
}
static void RawLeGuidToString_Upper(const Byte *g, char *s)
{
RawLeGuidToString(g, s);
// MyStringUpper_Ascii(s);
}
Z7_class_CHandler_final: public CHandlerCont
{
Z7_IFACE_COM7_IMP(IInArchive_Cont)
CRecordVector<CPartition> _items;
UInt64 _totalSize;
unsigned _sectorSizeLog;
Byte Guid[16];
CByteBuffer _buffer;
HRESULT Open2(IInStream *stream);
virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override
{
const CPartition &item = _items[index];
pos = item.GetPos(_sectorSizeLog);
size = item.GetSize(_sectorSizeLog);
return NExtract::NOperationResult::kOK;
}
};
HRESULT CHandler::Open2(IInStream *stream)
{
const unsigned kBufSize = 2 << 12;
_buffer.Alloc(kBufSize);
RINOK(ReadStream_FALSE(stream, _buffer, kBufSize))
const Byte *buf = _buffer;
if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
return S_FALSE;
{
for (unsigned sectorSizeLog = 9;; sectorSizeLog += 3)
{
if (sectorSizeLog > 12)
return S_FALSE;
if (memcmp(buf + ((size_t)1 << sectorSizeLog), k_Signature, k_SignatureSize) == 0)
{
buf += ((size_t)1 << sectorSizeLog);
_sectorSizeLog = sectorSizeLog;
break;
}
}
}
const UInt32 kSectorSize = 1u << _sectorSizeLog;
{
// if (Get32(buf + 8) != 0x10000) return S_FALSE; // revision
const UInt32 headerSize = Get32(buf + 12); // = 0x5C usually
if (headerSize > kSectorSize)
return S_FALSE;
const UInt32 crc = Get32(buf + 0x10);
SetUi32(_buffer + kSectorSize + 0x10, 0)
if (CrcCalc(_buffer + kSectorSize, headerSize) != crc)
return S_FALSE;
}
// UInt32 reserved = Get32(buf + 0x14);
const UInt64 curLba = Get64(buf + 0x18);
if (curLba != 1)
return S_FALSE;
const UInt64 backupLba = Get64(buf + 0x20);
// UInt64 firstUsableLba = Get64(buf + 0x28);
// UInt64 lastUsableLba = Get64(buf + 0x30);
memcpy(Guid, buf + 0x38, 16);
const UInt64 tableLba = Get64(buf + 0x48);
if (tableLba < 2 || (tableLba >> (63 - _sectorSizeLog)) != 0)
return S_FALSE;
const UInt32 numEntries = Get32(buf + 0x50);
if (numEntries > (1 << 16))
return S_FALSE;
const UInt32 entrySize = Get32(buf + 0x54); // = 128 usually
if (entrySize < 128 || entrySize > (1 << 12))
return S_FALSE;
const UInt32 entriesCrc = Get32(buf + 0x58);
const UInt32 tableSize = entrySize * numEntries;
const UInt32 tableSizeAligned = (tableSize + kSectorSize - 1) & ~(kSectorSize - 1);
_buffer.Alloc(tableSizeAligned);
const UInt64 tableOffset = tableLba * kSectorSize;
RINOK(InStream_SeekSet(stream, tableOffset))
RINOK(ReadStream_FALSE(stream, _buffer, tableSizeAligned))
if (CrcCalc(_buffer, tableSize) != entriesCrc)
return S_FALSE;
_totalSize = tableOffset + tableSizeAligned;
for (UInt32 i = 0; i < numEntries; i++)
{
CPartition item;
item.Parse(_buffer + i * entrySize);
if (item.IsUnused())
continue;
if (item.LastLba < item.FirstLba)
return S_FALSE;
if ((item.LastLba >> (63 - _sectorSizeLog)) != 0)
return S_FALSE;
const UInt64 endPos = item.GetEnd(_sectorSizeLog);
if (_totalSize < endPos)
_totalSize = endPos;
_items.Add(item);
}
_buffer.Free();
{
if ((backupLba >> (63 - _sectorSizeLog)) != 0)
return S_FALSE;
const UInt64 end = (backupLba + 1) * kSectorSize;
if (_totalSize < end)
_totalSize = end;
}
{
UInt64 fileEnd;
RINOK(InStream_GetSize_SeekToEnd(stream, fileEnd))
if (_totalSize < fileEnd)
{
const UInt64 rem = fileEnd - _totalSize;
const UInt64 kRemMax = 1 << 22;
if (rem <= kRemMax)
{
RINOK(InStream_SeekSet(stream, _totalSize))
bool areThereNonZeros = false;
UInt64 numZeros = 0;
if (ReadZeroTail(stream, areThereNonZeros, numZeros, kRemMax) == S_OK)
if (!areThereNonZeros)
_totalSize += numZeros;
}
}
}
return S_OK;
}
Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
const UInt64 * /* maxCheckStartPosition */,
IArchiveOpenCallback * /* openArchiveCallback */))
{
COM_TRY_BEGIN
Close();
RINOK(Open2(stream))
_stream = stream;
FOR_VECTOR (fileIndex, _items)
{
CPartition &item = _items[fileIndex];
const int typeIndex = FindPartType(item.Type);
if (typeIndex < 0)
continue;
const CPartType &t = kPartTypes[(unsigned)typeIndex];
if (t.Ext)
{
item.Ext = t.Ext;
continue;
}
if (t.Type && IsString1PrefixedByString2_NoCase_Ascii(t.Type, "Windows"))
{
CMyComPtr<ISequentialInStream> inStream;
if (
// ((IInArchiveGetStream *)this)->
GetStream(fileIndex, &inStream) == S_OK && inStream)
{
const char *fs = NMbr::GetFileSystem(inStream, item.GetSize(_sectorSizeLog));
if (fs)
item.Ext = fs;
}
}
}
return S_OK;
COM_TRY_END
}
Z7_COM7F_IMF(CHandler::Close())
{
_sectorSizeLog = 0;
_totalSize = 0;
memset(Guid, 0, sizeof(Guid));
_items.Clear();
_stream.Release();
return S_OK;
}
static const Byte kProps[] =
{
kpidPath,
kpidSize,
kpidFileSystem,
kpidCharacts,
kpidOffset,
kpidId
};
static const Byte kArcProps[] =
{
kpidSectorSize,
kpidId
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps
Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
switch (propID)
{
case kpidMainSubfile:
{
if (_items.Size() == 1)
prop = (UInt32)0;
break;
}
case kpidPhySize: prop = _totalSize; break;
case kpidSectorSize: prop = (UInt32)((UInt32)1 << _sectorSizeLog); break;
case kpidId:
{
char s[48];
RawLeGuidToString_Upper(Guid, s);
prop = s;
break;
}
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
{
*numItems = _items.Size();
return S_OK;
}
Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
const CPartition &item = _items[index];
switch (propID)
{
case kpidPath:
{
// Windows BDP partitions can have identical names.
// So we add the partition number at front
UString s;
s.Add_UInt32(index);
{
UString s2;
for (unsigned i = 0; i < kNameLen; i++)
{
wchar_t c = (wchar_t)Get16(item.Name + i * 2);
if (c == 0)
break;
s2 += c;
}
if (!s2.IsEmpty())
{
s.Add_Dot();
s += s2;
}
}
{
s.Add_Dot();
if (item.Ext)
{
AString fs (item.Ext);
fs.MakeLower_Ascii();
s += fs;
}
else
s += "img";
}
prop = s;
break;
}
case kpidSize:
case kpidPackSize: prop = item.GetSize(_sectorSizeLog); break;
case kpidOffset: prop = item.GetPos(_sectorSizeLog); break;
case kpidFileSystem:
{
char s[48];
const char *res;
const int typeIndex = FindPartType(item.Type);
if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Type)
res = kPartTypes[(unsigned)typeIndex].Type;
else
{
RawLeGuidToString_Upper(item.Type, s);
res = s;
}
prop = res;
break;
}
case kpidId:
{
char s[48];
RawLeGuidToString_Upper(item.Id, s);
prop = s;
break;
}
case kpidCharacts: FLAGS64_TO_PROP(g_PartitionFlags, item.Flags, prop); break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
// we suppport signature only for 512-bytes sector.
REGISTER_ARC_I(
"GPT", "gpt mbr", NULL, 0xCB,
k_Signature,
1 << 9,
0,
NULL)
}}