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