1 // MbrHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #endif
10 
11 #include "../../../C/CpuArch.h"
12 
13 #include "../../Common/ComTry.h"
14 #include "../../Common/IntToString.h"
15 #include "../../Common/MyBuffer.h"
16 
17 #include "../../Windows/PropVariant.h"
18 
19 #include "../Common/RegisterArc.h"
20 #include "../Common/StreamUtils.h"
21 
22 #include "HandlerCont.h"
23 
24 #ifdef SHOW_DEBUG_INFO
25 #define PRF(x) x
26 #else
27 #define PRF(x)
28 #endif
29 
30 using namespace NWindows;
31 
32 namespace NArchive {
33 namespace NMbr {
34 
35 struct CChs
36 {
37   Byte Head;
38   Byte SectCyl;
39   Byte Cyl8;
40 
GetSectorNArchive::NMbr::CChs41   UInt32 GetSector() const { return SectCyl & 0x3F; }
GetCylNArchive::NMbr::CChs42   UInt32 GetCyl() const { return ((UInt32)SectCyl >> 6 << 8) | Cyl8; }
43   void ToString(NCOM::CPropVariant &prop) const;
44 
ParseNArchive::NMbr::CChs45   void Parse(const Byte *p)
46   {
47     Head = p[0];
48     SectCyl = p[1];
49     Cyl8 = p[2];
50   }
CheckNArchive::NMbr::CChs51   bool Check() const { return GetSector() > 0; }
52 };
53 
54 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
55 
56 // Chs in some MBRs contains only low bits of "Cyl number". So we disable check.
57 /*
58 static int CompareChs(const CChs &c1, const CChs &c2)
59 {
60   RINOZ(MyCompare(c1.GetCyl(), c2.GetCyl()));
61   RINOZ(MyCompare(c1.Head, c2.Head));
62   return MyCompare(c1.GetSector(), c2.GetSector());
63 }
64 */
65 
ToString(NCOM::CPropVariant & prop) const66 void CChs::ToString(NCOM::CPropVariant &prop) const
67 {
68   AString s;
69   s.Add_UInt32(GetCyl());
70   s += '-';
71   s.Add_UInt32(Head);
72   s += '-';
73   s.Add_UInt32(GetSector());
74   prop = s;
75 }
76 
77 struct CPartition
78 {
79   Byte Status;
80   CChs BeginChs;
81   Byte Type;
82   CChs EndChs;
83   UInt32 Lba;
84   UInt32 NumBlocks;
85 
CPartitionNArchive::NMbr::CPartition86   CPartition() { memset (this, 0, sizeof(*this)); }
87 
IsEmptyNArchive::NMbr::CPartition88   bool IsEmpty() const { return Type == 0; }
IsExtendedNArchive::NMbr::CPartition89   bool IsExtended() const { return Type == 5 || Type == 0xF; }
GetLimitNArchive::NMbr::CPartition90   UInt32 GetLimit() const { return Lba + NumBlocks; }
91   // bool IsActive() const { return Status == 0x80; }
GetPosNArchive::NMbr::CPartition92   UInt64 GetPos() const { return (UInt64)Lba * 512; }
GetSizeNArchive::NMbr::CPartition93   UInt64 GetSize() const { return (UInt64)NumBlocks * 512; }
94 
CheckLbaLimitsNArchive::NMbr::CPartition95   bool CheckLbaLimits() const { return (UInt32)0xFFFFFFFF - Lba >= NumBlocks; }
ParseNArchive::NMbr::CPartition96   bool Parse(const Byte *p)
97   {
98     Status = p[0];
99     BeginChs.Parse(p + 1);
100     Type = p[4];
101     EndChs.Parse(p + 5);
102     Lba = GetUi32(p + 8);
103     NumBlocks = GetUi32(p + 12);
104     if (Type == 0)
105       return true;
106     if (Status != 0 && Status != 0x80)
107       return false;
108     return BeginChs.Check()
109        && EndChs.Check()
110        // && CompareChs(BeginChs, EndChs) <= 0
111        && NumBlocks > 0
112        && CheckLbaLimits();
113   }
114 
115   #ifdef SHOW_DEBUG_INFO
PrintNArchive::NMbr::CPartition116   void Print() const
117   {
118     NCOM::CPropVariant prop, prop2;
119     BeginChs.ToString(prop);
120     EndChs.ToString(prop2);
121     printf("   %2x %2x %8X %8X %12S %12S", (int)Status, (int)Type, Lba, NumBlocks, prop.bstrVal, prop2.bstrVal);
122   }
123   #endif
124 };
125 
126 struct CPartType
127 {
128   UInt32 Id;
129   const char *Ext;
130   const char *Name;
131 };
132 
133 #define kFat "fat"
134 
135 static const CPartType kPartTypes[] =
136 {
137   { 0x01, kFat, "FAT12" },
138   { 0x04, kFat, "FAT16 DOS 3.0+" },
139   { 0x05, 0, "Extended" },
140   { 0x06, kFat, "FAT16 DOS 3.31+" },
141   { 0x07, "ntfs", "NTFS" },
142   { 0x0B, kFat, "FAT32" },
143   { 0x0C, kFat, "FAT32-LBA" },
144   { 0x0E, kFat, "FAT16-LBA" },
145   { 0x0F, 0, "Extended-LBA" },
146   { 0x11, kFat, "FAT12-Hidden" },
147   { 0x14, kFat, "FAT16-Hidden < 32 MB" },
148   { 0x16, kFat, "FAT16-Hidden >= 32 MB" },
149   { 0x1B, kFat, "FAT32-Hidden" },
150   { 0x1C, kFat, "FAT32-LBA-Hidden" },
151   { 0x1E, kFat, "FAT16-LBA-WIN95-Hidden" },
152   { 0x27, "ntfs", "NTFS-WinRE" },
153   { 0x82, 0, "Solaris x86 / Linux swap" },
154   { 0x83, 0, "Linux" },
155   { 0x8E, "lvm", "Linux LVM" },
156   { 0xA5, 0, "BSD slice" },
157   { 0xBE, 0, "Solaris 8 boot" },
158   { 0xBF, 0, "New Solaris x86" },
159   { 0xC2, 0, "Linux-Hidden" },
160   { 0xC3, 0, "Linux swap-Hidden" },
161   { 0xEE, 0, "GPT" },
162   { 0xEE, 0, "EFI" }
163 };
164 
FindPartType(UInt32 type)165 static int FindPartType(UInt32 type)
166 {
167   for (unsigned i = 0; i < ARRAY_SIZE(kPartTypes); i++)
168     if (kPartTypes[i].Id == type)
169       return i;
170   return -1;
171 }
172 
173 struct CItem
174 {
175   bool IsReal;
176   bool IsPrim;
177   UInt64 Size;
178   CPartition Part;
179 };
180 
181 class CHandler: public CHandlerCont
182 {
183   CObjectVector<CItem> _items;
184   UInt64 _totalSize;
185   CByteBuffer _buffer;
186 
GetItem_ExtractInfo(UInt32 index,UInt64 & pos,UInt64 & size) const187   virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const
188   {
189     const CItem &item = _items[index];
190     pos = item.Part.GetPos();
191     size = item.Size;
192     return NExtract::NOperationResult::kOK;
193   }
194 
195   HRESULT ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, unsigned level);
196 public:
197   INTERFACE_IInArchive_Cont(;)
198 };
199 
ReadTables(IInStream * stream,UInt32 baseLba,UInt32 lba,unsigned level)200 HRESULT CHandler::ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, unsigned level)
201 {
202   if (level >= 128 || _items.Size() >= 128)
203     return S_FALSE;
204 
205   const unsigned kNumHeaderParts = 4;
206   CPartition parts[kNumHeaderParts];
207 
208   {
209     const UInt32 kSectorSize = 512;
210     _buffer.Alloc(kSectorSize);
211     Byte *buf = _buffer;
212     UInt64 newPos = (UInt64)lba << 9;
213     if (newPos + 512 > _totalSize)
214       return S_FALSE;
215     RINOK(stream->Seek(newPos, STREAM_SEEK_SET, NULL));
216     RINOK(ReadStream_FALSE(stream, buf, kSectorSize));
217 
218     if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
219       return S_FALSE;
220 
221     for (unsigned i = 0; i < kNumHeaderParts; i++)
222       if (!parts[i].Parse(buf + 0x1BE + 16 * i))
223         return S_FALSE;
224   }
225 
226   PRF(printf("\n# %8X", lba));
227 
228   UInt32 limLba = lba + 1;
229   if (limLba == 0)
230     return S_FALSE;
231 
232   for (unsigned i = 0; i < kNumHeaderParts; i++)
233   {
234     CPartition &part = parts[i];
235 
236     if (part.IsEmpty())
237       continue;
238     PRF(printf("\n   %2d ", (unsigned)level));
239     #ifdef SHOW_DEBUG_INFO
240     part.Print();
241     #endif
242 
243     unsigned numItems = _items.Size();
244     UInt32 newLba = lba + part.Lba;
245 
246     if (part.IsExtended())
247     {
248       // if (part.Type == 5) // Check it!
249       newLba = baseLba + part.Lba;
250       if (newLba < limLba)
251         return S_FALSE;
252       HRESULT res = ReadTables(stream, level < 1 ? newLba : baseLba, newLba, level + 1);
253       if (res != S_FALSE && res != S_OK)
254         return res;
255     }
256     if (newLba < limLba)
257       return S_FALSE;
258     part.Lba = newLba;
259     if (!part.CheckLbaLimits())
260       return S_FALSE;
261 
262     CItem n;
263     n.Part = part;
264     bool addItem = false;
265     if (numItems == _items.Size())
266     {
267       n.IsPrim = (level == 0);
268       n.IsReal = true;
269       addItem = true;
270     }
271     else
272     {
273       const CItem &back = _items.Back();
274       UInt32 backLimit = back.Part.GetLimit();
275       UInt32 partLimit = part.GetLimit();
276       if (backLimit < partLimit)
277       {
278         n.IsReal = false;
279         n.Part.Lba = backLimit;
280         n.Part.NumBlocks = partLimit - backLimit;
281         addItem = true;
282       }
283     }
284     if (addItem)
285     {
286       if (n.Part.GetLimit() < limLba)
287         return S_FALSE;
288       limLba = n.Part.GetLimit();
289       n.Size = n.Part.GetSize();
290       _items.Add(n);
291     }
292   }
293   return S_OK;
294 }
295 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)296 STDMETHODIMP CHandler::Open(IInStream *stream,
297     const UInt64 * /* maxCheckStartPosition */,
298     IArchiveOpenCallback * /* openArchiveCallback */)
299 {
300   COM_TRY_BEGIN
301   Close();
302   RINOK(stream->Seek(0, STREAM_SEEK_END, &_totalSize));
303   RINOK(ReadTables(stream, 0, 0, 0));
304   if (_items.IsEmpty())
305     return S_FALSE;
306   UInt32 lbaLimit = _items.Back().Part.GetLimit();
307   UInt64 lim = (UInt64)lbaLimit << 9;
308   if (lim < _totalSize)
309   {
310     CItem n;
311     n.Part.Lba = lbaLimit;
312     n.Size = _totalSize - lim;
313     n.IsReal = false;
314     _items.Add(n);
315   }
316   _stream = stream;
317   return S_OK;
318   COM_TRY_END
319 }
320 
Close()321 STDMETHODIMP CHandler::Close()
322 {
323   _totalSize = 0;
324   _items.Clear();
325   _stream.Release();
326   return S_OK;
327 }
328 
329 enum
330 {
331   kpidPrimary = kpidUserDefined,
332   kpidBegChs,
333   kpidEndChs
334 };
335 
336 static const CStatProp kProps[] =
337 {
338   { NULL, kpidPath, VT_BSTR},
339   { NULL, kpidSize, VT_UI8},
340   { NULL, kpidFileSystem, VT_BSTR},
341   { NULL, kpidOffset, VT_UI8},
342   { "Primary", kpidPrimary, VT_BOOL},
343   { "Begin CHS", kpidBegChs, VT_BSTR},
344   { "End CHS", kpidEndChs, VT_BSTR}
345 };
346 
347 IMP_IInArchive_Props_WITH_NAME
348 IMP_IInArchive_ArcProps_NO_Table
349 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)350 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
351 {
352   NCOM::CPropVariant prop;
353   switch (propID)
354   {
355     case kpidMainSubfile:
356     {
357       int mainIndex = -1;
358       FOR_VECTOR (i, _items)
359         if (_items[i].IsReal)
360         {
361           if (mainIndex >= 0)
362           {
363             mainIndex = -1;
364             break;
365           }
366           mainIndex = i;
367         }
368       if (mainIndex >= 0)
369         prop = (UInt32)mainIndex;
370       break;
371     }
372     case kpidPhySize: prop = _totalSize; break;
373   }
374   prop.Detach(value);
375   return S_OK;
376 }
377 
GetNumberOfItems(UInt32 * numItems)378 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
379 {
380   *numItems = _items.Size();
381   return S_OK;
382 }
383 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)384 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
385 {
386   COM_TRY_BEGIN
387   NCOM::CPropVariant prop;
388 
389   const CItem &item = _items[index];
390   const CPartition &part = item.Part;
391   switch (propID)
392   {
393     case kpidPath:
394     {
395       AString s;
396       s.Add_UInt32(index);
397       if (item.IsReal)
398       {
399         s += '.';
400         const char *ext = NULL;
401         int typeIndex = FindPartType(part.Type);
402         if (typeIndex >= 0)
403           ext = kPartTypes[(unsigned)typeIndex].Ext;
404         if (!ext)
405           ext = "img";
406         s += ext;
407       }
408       prop = s;
409       break;
410     }
411     case kpidFileSystem:
412       if (item.IsReal)
413       {
414         char s[32];
415         ConvertUInt32ToString(part.Type, s);
416         const char *res = s;
417         int typeIndex = FindPartType(part.Type);
418         if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Name)
419           res = kPartTypes[(unsigned)typeIndex].Name;
420         prop = res;
421       }
422       break;
423     case kpidSize:
424     case kpidPackSize: prop = item.Size; break;
425     case kpidOffset: prop = part.GetPos(); break;
426     case kpidPrimary: if (item.IsReal) prop = item.IsPrim; break;
427     case kpidBegChs: if (item.IsReal) part.BeginChs.ToString(prop); break;
428     case kpidEndChs: if (item.IsReal) part.EndChs.ToString(prop); break;
429   }
430   prop.Detach(value);
431   return S_OK;
432   COM_TRY_END
433 }
434 
435 
436   // 3, { 1, 1, 0 },
437   // 2, { 0x55, 0x1FF },
438 
439 REGISTER_ARC_I_NO_SIG(
440   "MBR", "mbr", 0, 0xDB,
441   0,
442   NArcInfoFlags::kPureStartOpen,
443   NULL)
444 
445 }}
446