Files
easy7zip/CPP/7zip/Archive/MbrHandler.cpp
Igor Pavlov c99f3ebdd6 9.06 beta
2016-05-28 00:16:00 +01:00

508 lines
12 KiB
C++
Executable File

// MbrHandler.cpp
#include "StdAfx.h"
// #define SHOW_DEBUG_INFO
#ifdef SHOW_DEBUG_INFO
#include <stdio.h>
#endif
#include "../../../C/CpuArch.h"
#include "Common/Buffer.h"
#include "Common/ComTry.h"
#include "Common/IntToString.h"
#include "Common/MyString.h"
#include "Windows/PropVariant.h"
#include "../Common/LimitedStreams.h"
#include "../Common/ProgressUtils.h"
#include "../Common/RegisterArc.h"
#include "../Common/StreamUtils.h"
#include "../Compress/CopyCoder.h"
#ifdef SHOW_DEBUG_INFO
#define PRF(x) x
#else
#define PRF(x)
#endif
using namespace NWindows;
namespace NArchive {
namespace NMbr {
struct CChs
{
Byte Head;
Byte SectCyl;
Byte Cyl8;
UInt32 GetSector() const { return SectCyl & 0x3F; }
UInt32 GetCyl() const { return ((UInt32)SectCyl >> 6 << 8) | Cyl8; }
void ToString(NCOM::CPropVariant &prop) const;
void Parse(const Byte *p)
{
Head = p[0];
SectCyl = p[1];
Cyl8 = p[2];
}
bool Check() const { return GetSector() > 0; }
};
#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
static int CompareChs(const CChs &c1, const CChs &c2)
{
RINOZ(MyCompare(c1.GetCyl(), c2.GetCyl()));
RINOZ(MyCompare(c1.Head, c2.Head));
return MyCompare(c1.GetSector(), c2.GetSector());
}
static void AddUIntToString(UInt32 val, AString &res)
{
char s[16];
ConvertUInt32ToString(val, s);
res += s;
}
void CChs::ToString(NCOM::CPropVariant &prop) const
{
AString s;
AddUIntToString(GetCyl(), s);
s += '-';
AddUIntToString(Head, s);
s += '-';
AddUIntToString(GetSector(), s);
prop = s;
}
struct CPartition
{
Byte Status;
CChs BeginChs;
Byte Type;
CChs EndChs;
UInt32 Lba;
UInt32 NumBlocks;
CPartition() { memset (this, 0, sizeof(*this)); }
bool IsEmpty() const { return Type == 0; }
bool IsExtended() const { return Type == 5 || Type == 0xF; }
UInt32 GetLimit() const { return Lba + NumBlocks; }
// bool IsActive() const { return Status == 0x80; }
UInt64 GetPos() const { return (UInt64)Lba * 512; }
UInt64 GetSize() const { return (UInt64)NumBlocks * 512; }
bool CheckLbaLimits() const { return (UInt32)0xFFFFFFFF - Lba >= NumBlocks; }
bool Parse(const Byte *p)
{
Status = p[0];
BeginChs.Parse(p + 1);
Type = p[4];
EndChs.Parse(p + 5);
Lba = GetUi32(p + 8);
NumBlocks = GetUi32(p + 12);
if (Type == 0)
return true;
if (Status != 0 && Status != 0x80)
return false;
return
BeginChs.Check() &&
EndChs.Check() &&
CompareChs(BeginChs, EndChs) <= 0 &&
NumBlocks > 0 &&
CheckLbaLimits();
}
#ifdef SHOW_DEBUG_INFO
void Print() const
{
NCOM::CPropVariant prop, prop2;
BeginChs.ToString(prop);
EndChs.ToString(prop2);
printf(" %2x %2x %8X %8X %12S %12S", (int)Status, (int)Type, Lba, NumBlocks, prop.bstrVal, prop2.bstrVal);
}
#endif
};
struct CPartType
{
UInt32 Id;
const char *Ext;
const char *Name;
};
static const char *kFat = "fat";
static const CPartType kPartTypes[] =
{
{ 0x01, kFat, "FAT12" },
{ 0x04, kFat, "FAT16 DOS 3.0+" },
{ 0x05, 0, "Extended" },
{ 0x06, kFat, "FAT16 DOS 3.31+" },
{ 0x07, "ntfs", "NTFS" },
{ 0x0B, kFat, "FAT32" },
{ 0x0C, kFat, "FAT32-LBA" },
{ 0x0E, kFat, "FAT16-LBA" },
{ 0x0F, 0, "Extended-LBA" },
{ 0x11, kFat, "FAT12-Hidden" },
{ 0x14, kFat, "FAT16-Hidden < 32 MB" },
{ 0x16, kFat, "FAT16-Hidden >= 32 MB" },
{ 0x1B, kFat, "FAT32-Hidden" },
{ 0x1C, kFat, "FAT32-LBA-Hidden" },
{ 0x1E, kFat, "FAT16-LBA-WIN95-Hidden" },
{ 0x82, 0, "Solaris x86 / Linux swap" },
{ 0x83, 0, "Linux" },
{ 0xBE, 0, "Solaris 8 boot" },
{ 0xBF, 0, "New Solaris x86" },
{ 0xC2, 0, "Linux-Hidden" },
{ 0xC3, 0, "Linux swap-Hidden" },
{ 0xEE, 0, "EFI-MBR" },
{ 0xEE, 0, "EFI" }
};
static int FindPartType(UInt32 type)
{
for (int i = 0; i < sizeof(kPartTypes) / sizeof(kPartTypes[0]); i++)
if (kPartTypes[i].Id == type)
return i;
return -1;
}
struct CItem
{
bool IsReal;
bool IsPrim;
UInt64 Size;
CPartition Part;
};
class CHandler:
public IInArchive,
public IInArchiveGetStream,
public CMyUnknownImp
{
CMyComPtr<IInStream> _stream;
CObjectVector<CItem> _items;
UInt64 _totalSize;
CByteBuffer _buffer;
HRESULT ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, int level);
public:
MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
INTERFACE_IInArchive(;)
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
};
HRESULT CHandler::ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, int level)
{
if (level >= 128 || _items.Size() >= 128)
return S_FALSE;
const int kNumHeaderParts = 4;
CPartition parts[kNumHeaderParts];
{
const UInt32 kSectorSize = 512;
_buffer.SetCapacity(kSectorSize);
Byte *buf = _buffer;
UInt64 newPos = (UInt64)lba << 9;
if (newPos + 512 > _totalSize)
return S_FALSE;
RINOK(stream->Seek(newPos, STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(stream, buf, kSectorSize));
if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
return S_FALSE;
for (int i = 0; i < kNumHeaderParts; i++)
if (!parts[i].Parse(buf + 0x1BE + 16 * i))
return S_FALSE;
}
PRF(printf("\n# %8X", lba));
UInt32 limLba = lba + 1;
if (limLba == 0)
return S_FALSE;
for (int i = 0; i < kNumHeaderParts; i++)
{
CPartition &part = parts[i];
if (part.IsEmpty())
continue;
PRF(printf("\n %2d ", (int)level));
#ifdef SHOW_DEBUG_INFO
part.Print();
#endif
int numItems = _items.Size();
UInt32 newLba = lba + part.Lba;
if (part.IsExtended())
{
// if (part.Type == 5) // Check it!
newLba = baseLba + part.Lba;
if (newLba < limLba)
return S_FALSE;
HRESULT res = ReadTables(stream, level < 1 ? newLba : baseLba, newLba, level + 1);
if (res != S_FALSE && res != S_OK)
return res;
}
if (newLba < limLba)
return S_FALSE;
part.Lba = newLba;
if (!part.CheckLbaLimits())
return S_FALSE;
CItem n;
n.Part = part;
bool addItem = false;
if (numItems == _items.Size())
{
n.IsPrim = (level == 0);
n.IsReal = true;
addItem = true;
}
else
{
const CItem &back = _items.Back();
UInt32 backLimit = back.Part.GetLimit();
UInt32 partLimit = part.GetLimit();
if (backLimit < partLimit)
{
n.IsReal = false;
n.Part.Lba = backLimit;
n.Part.NumBlocks = partLimit - backLimit;
addItem = true;
}
}
if (addItem)
{
if (n.Part.GetLimit() < limLba)
return S_FALSE;
limLba = n.Part.GetLimit();
n.Size = n.Part.GetSize();
_items.Add(n);
}
}
return S_OK;
}
STDMETHODIMP CHandler::Open(IInStream *stream,
const UInt64 * /* maxCheckStartPosition */,
IArchiveOpenCallback * /* openArchiveCallback */)
{
COM_TRY_BEGIN
Close();
RINOK(stream->Seek(0, STREAM_SEEK_END, &_totalSize));
RINOK(ReadTables(stream, 0, 0, 0));
if (_items.IsEmpty())
return S_FALSE;
UInt32 lbaLimit = _items.Back().Part.GetLimit();
UInt64 lim = (UInt64)lbaLimit << 9;
if (lim < _totalSize)
{
CItem n;
n.Part.Lba = lbaLimit;
n.Size = _totalSize - lim;
n.IsReal = false;
_items.Add(n);
}
_stream = stream;
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Close()
{
_items.Clear();
_stream.Release();
return S_OK;
}
enum
{
kpidPrimary = kpidUserDefined,
kpidBegChs,
kpidEndChs,
};
STATPROPSTG kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidSize, VT_UI8},
{ NULL, kpidFileSystem, VT_BSTR},
{ NULL, kpidOffset, VT_UI8},
{ L"Primary", kpidPrimary, VT_BOOL},
{ L"Begin CHS", kpidBegChs, VT_BSTR},
{ L"End CHS", kpidEndChs, VT_BSTR}
};
IMP_IInArchive_Props_WITH_NAME
IMP_IInArchive_ArcProps_NO_Table
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
NCOM::CPropVariant prop;
switch(propID)
{
case kpidMainSubfile:
{
int mainIndex = -1;
for (int i = 0; i < _items.Size(); i++)
if (_items[i].IsReal)
{
if (mainIndex >= 0)
{
mainIndex = -1;
break;
}
mainIndex = i;
}
if (mainIndex >= 0)
prop = (UInt32)mainIndex;
break;
}
}
prop.Detach(value);
return S_OK;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = _items.Size();
return S_OK;
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
const CItem &item = _items[index];
const CPartition &part = item.Part;
switch(propID)
{
case kpidPath:
{
AString s;
AddUIntToString(index, s);
if (item.IsReal)
{
int typeIndex = FindPartType(part.Type);
s += '.';
const char *ext = "img";
if (typeIndex >= 0 && kPartTypes[typeIndex].Ext != 0)
ext = kPartTypes[typeIndex].Ext;
s += ext;
}
prop = s;
break;
}
case kpidFileSystem:
if (item.IsReal)
{
char s[32];
ConvertUInt32ToString(part.Type, s);
const char *res = s;
int typeIndex = FindPartType(part.Type);
if (typeIndex >= 0 && kPartTypes[typeIndex].Name)
res = kPartTypes[typeIndex].Name;
prop = res;
}
break;
case kpidSize: prop = item.Size; break;;
case kpidPackSize: prop = item.Size; break;
case kpidOffset: prop = part.GetPos(); break;
case kpidPrimary: if (item.IsReal) prop = item.IsPrim; break;
case kpidBegChs: if (item.IsReal) part.BeginChs.ToString(prop); break;
case kpidEndChs: if (item.IsReal) part.EndChs.ToString(prop); break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
bool allFilesMode = (numItems == (UInt32)-1);
if (allFilesMode)
numItems = _items.Size();
if (numItems == 0)
return S_OK;
UInt64 totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
totalSize += _items[allFilesMode ? i : indices[i]].Size;
extractCallback->SetTotal(totalSize);
totalSize = 0;
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(extractCallback, false);
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
CMyComPtr<ISequentialInStream> inStream(streamSpec);
streamSpec->SetStream(_stream);
for (i = 0; i < numItems; i++)
{
lps->InSize = totalSize;
lps->OutSize = totalSize;
RINOK(lps->SetCur());
CMyComPtr<ISequentialOutStream> outStream;
Int32 askMode = testMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract;
Int32 index = allFilesMode ? i : indices[i];
const CItem &item = _items[index];
const CPartition &part = item.Part;
RINOK(extractCallback->GetStream(index, &outStream, askMode));
totalSize += item.Size;
if (!testMode && !outStream)
continue;
RINOK(extractCallback->PrepareOperation(askMode));
RINOK(_stream->Seek(part.GetPos(), STREAM_SEEK_SET, NULL));
streamSpec->Init(item.Size);
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
outStream.Release();
RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == item.Size ?
NExtract::NOperationResult::kOK:
NExtract::NOperationResult::kDataError));
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
{
COM_TRY_BEGIN
const CItem &item = _items[index];
return CreateLimitedInStream(_stream, item.Part.GetPos(), item.Size, stream);
COM_TRY_END
}
static IInArchive *CreateArc() { return new CHandler; }
static CArcInfo g_ArcInfo =
{ L"MBR", L"mbr", 0, 0xDB, { 1, 1, 0 }, 3, false, CreateArc, 0 };
REGISTER_ARC(Mbr)
}}