• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // GptHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/7zCrc.h"
6 #include "../../../C/CpuArch.h"
7 
8 #include "../../Common/ComTry.h"
9 #include "../../Common/IntToString.h"
10 #include "../../Common/MyBuffer.h"
11 
12 #include "../../Windows/PropVariantUtils.h"
13 
14 #include "../Common/RegisterArc.h"
15 #include "../Common/StreamUtils.h"
16 
17 #include "HandlerCont.h"
18 
19 #define Get16(p) GetUi16(p)
20 #define Get32(p) GetUi32(p)
21 #define Get64(p) GetUi64(p)
22 
23 using namespace NWindows;
24 
25 namespace NArchive {
26 
27 namespace NMbr {
28 const char *GetFileSystem(ISequentialInStream *stream, UInt64 partitionSize);
29 }
30 
31 namespace NFat {
32 API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size);
33 }
34 
35 namespace NGpt {
36 
37 static const unsigned k_SignatureSize = 12;
38 static const Byte k_Signature[k_SignatureSize] =
39     { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T', 0, 0, 1, 0 };
40 
41 static const CUInt32PCharPair g_PartitionFlags[] =
42 {
43   { 0, "Sys" },
44   { 1, "Ignore" },
45   { 2, "Legacy" },
46   { 60, "Win-Read-only" },
47   { 62, "Win-Hidden" },
48   { 63, "Win-Not-Automount" }
49 };
50 
51 static const unsigned kNameLen = 36;
52 
53 struct CPartition
54 {
55   Byte Type[16];
56   Byte Id[16];
57   UInt64 FirstLba;
58   UInt64 LastLba;
59   UInt64 Flags;
60   const char *Ext; // detected later
61   Byte Name[kNameLen * 2];
62 
IsUnusedNArchive::NGpt::CPartition63   bool IsUnused() const
64   {
65     for (unsigned i = 0; i < 16; i++)
66       if (Type[i] != 0)
67         return false;
68     return true;
69   }
70 
GetSizeNArchive::NGpt::CPartition71   UInt64 GetSize(unsigned sectorSizeLog) const { return (LastLba - FirstLba + 1) << sectorSizeLog; }
GetPosNArchive::NGpt::CPartition72   UInt64 GetPos(unsigned sectorSizeLog) const { return FirstLba << sectorSizeLog; }
GetEndNArchive::NGpt::CPartition73   UInt64 GetEnd(unsigned sectorSizeLog) const { return (LastLba + 1) << sectorSizeLog; }
74 
ParseNArchive::NGpt::CPartition75   void Parse(const Byte *p)
76   {
77     memcpy(Type, p, 16);
78     memcpy(Id, p + 16, 16);
79     FirstLba = Get64(p + 32);
80     LastLba = Get64(p + 40);
81     Flags = Get64(p + 48);
82     memcpy(Name, p + 56, kNameLen * 2);
83     Ext = NULL;
84   }
85 };
86 
87 
88 struct CPartType
89 {
90   UInt32 Id;
91   const char *Ext;
92   const char *Type;
93 };
94 
95 static const CPartType kPartTypes[] =
96 {
97   // { 0x0, NULL, "Unused" },
98 
99   { 0x21686148, NULL, "BIOS Boot" },
100 
101   { 0xC12A7328, NULL, "EFI System" },
102   { 0x024DEE41, NULL, "MBR" },
103 
104   { 0xE3C9E316, NULL, "Windows MSR" },
105   { 0xEBD0A0A2, NULL, "Windows BDP" },
106   { 0x5808C8AA, NULL, "Windows LDM Metadata" },
107   { 0xAF9B60A0, NULL, "Windows LDM Data" },
108   { 0xDE94BBA4, NULL, "Windows Recovery" },
109   // { 0x37AFFC90, NULL, "IBM GPFS" },
110   // { 0xE75CAF8F, NULL, "Windows Storage Spaces" },
111 
112   { 0x0FC63DAF, NULL, "Linux Data" },
113   { 0x0657FD6D, NULL, "Linux Swap" },
114 
115   { 0x83BD6B9D, NULL, "FreeBSD Boot" },
116   { 0x516E7CB4, NULL, "FreeBSD Data" },
117   { 0x516E7CB5, NULL, "FreeBSD Swap" },
118   { 0x516E7CB6, "ufs", "FreeBSD UFS" },
119   { 0x516E7CB8, NULL, "FreeBSD Vinum" },
120   { 0x516E7CB8, "zfs", "FreeBSD ZFS" },
121 
122   { 0x48465300, "hfsx", "HFS+" },
123   { 0x7C3457EF, "apfs", "APFS" },
124 };
125 
FindPartType(const Byte * guid)126 static int FindPartType(const Byte *guid)
127 {
128   const UInt32 val = Get32(guid);
129   for (unsigned i = 0; i < Z7_ARRAY_SIZE(kPartTypes); i++)
130     if (kPartTypes[i].Id == val)
131       return (int)i;
132   return -1;
133 }
134 
135 
RawLeGuidToString_Upper(const Byte * g,char * s)136 static void RawLeGuidToString_Upper(const Byte *g, char *s)
137 {
138   RawLeGuidToString(g, s);
139   // MyStringUpper_Ascii(s);
140 }
141 
142 
143 Z7_class_CHandler_final: public CHandlerCont
144 {
145   Z7_IFACE_COM7_IMP(IInArchive_Cont)
146 
147   CRecordVector<CPartition> _items;
148   UInt64 _totalSize;
149   unsigned _sectorSizeLog;
150   Byte Guid[16];
151 
152   CByteBuffer _buffer;
153 
154   HRESULT Open2(IInStream *stream);
155 
156   virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override
157   {
158     const CPartition &item = _items[index];
159     pos = item.GetPos(_sectorSizeLog);
160     size = item.GetSize(_sectorSizeLog);
161     return NExtract::NOperationResult::kOK;
162   }
163 };
164 
165 
166 HRESULT CHandler::Open2(IInStream *stream)
167 {
168   const unsigned kBufSize = 2 << 12;
169   _buffer.Alloc(kBufSize);
170   RINOK(ReadStream_FALSE(stream, _buffer, kBufSize))
171   const Byte *buf = _buffer;
172   if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
173     return S_FALSE;
174   {
175     for (unsigned sectorSizeLog = 9;; sectorSizeLog += 3)
176     {
177       if (sectorSizeLog > 12)
178         return S_FALSE;
179       if (memcmp(buf + ((size_t)1 << sectorSizeLog), k_Signature, k_SignatureSize) == 0)
180       {
181         buf += ((size_t)1 << sectorSizeLog);
182         _sectorSizeLog = sectorSizeLog;
183         break;
184       }
185     }
186   }
187   const UInt32 kSectorSize = 1u << _sectorSizeLog;
188   {
189     // if (Get32(buf + 8) != 0x10000) return S_FALSE; // revision
190     const UInt32 headerSize = Get32(buf + 12); // = 0x5C usually
191     if (headerSize > kSectorSize)
192       return S_FALSE;
193     const UInt32 crc = Get32(buf + 0x10);
194     SetUi32(_buffer + kSectorSize + 0x10, 0)
195     if (CrcCalc(_buffer + kSectorSize, headerSize) != crc)
196       return S_FALSE;
197   }
198   // UInt32 reserved = Get32(buf + 0x14);
199   const UInt64 curLba = Get64(buf + 0x18);
200   if (curLba != 1)
201     return S_FALSE;
202   const UInt64 backupLba = Get64(buf + 0x20);
203   // UInt64 firstUsableLba = Get64(buf + 0x28);
204   // UInt64 lastUsableLba = Get64(buf + 0x30);
205   memcpy(Guid, buf + 0x38, 16);
206   const UInt64 tableLba = Get64(buf + 0x48);
207   if (tableLba < 2 || (tableLba >> (63 - _sectorSizeLog)) != 0)
208     return S_FALSE;
209   const UInt32 numEntries = Get32(buf + 0x50);
210   if (numEntries > (1 << 16))
211     return S_FALSE;
212   const UInt32 entrySize = Get32(buf + 0x54); // = 128 usually
213   if (entrySize < 128 || entrySize > (1 << 12))
214     return S_FALSE;
215   const UInt32 entriesCrc = Get32(buf + 0x58);
216 
217   const UInt32 tableSize = entrySize * numEntries;
218   const UInt32 tableSizeAligned = (tableSize + kSectorSize - 1) & ~(kSectorSize - 1);
219   _buffer.Alloc(tableSizeAligned);
220   const UInt64 tableOffset = tableLba * kSectorSize;
221   RINOK(InStream_SeekSet(stream, tableOffset))
222   RINOK(ReadStream_FALSE(stream, _buffer, tableSizeAligned))
223 
224   if (CrcCalc(_buffer, tableSize) != entriesCrc)
225     return S_FALSE;
226 
227   _totalSize = tableOffset + tableSizeAligned;
228 
229   for (UInt32 i = 0; i < numEntries; i++)
230   {
231     CPartition item;
232     item.Parse(_buffer + i * entrySize);
233     if (item.IsUnused())
234       continue;
235     if (item.LastLba < item.FirstLba)
236       return S_FALSE;
237     if ((item.LastLba >> (63 - _sectorSizeLog)) != 0)
238       return S_FALSE;
239     const UInt64 endPos = item.GetEnd(_sectorSizeLog);
240     if (_totalSize < endPos)
241       _totalSize = endPos;
242     _items.Add(item);
243   }
244 
245   _buffer.Free();
246   {
247     if ((backupLba >> (63 - _sectorSizeLog)) != 0)
248       return S_FALSE;
249     const UInt64 end = (backupLba + 1) * kSectorSize;
250     if (_totalSize < end)
251       _totalSize = end;
252   }
253   {
254     UInt64 fileEnd;
255     RINOK(InStream_GetSize_SeekToEnd(stream, fileEnd))
256 
257     if (_totalSize < fileEnd)
258     {
259       const UInt64 rem = fileEnd - _totalSize;
260       const UInt64 kRemMax = 1 << 22;
261       if (rem <= kRemMax)
262       {
263         RINOK(InStream_SeekSet(stream, _totalSize))
264         bool areThereNonZeros = false;
265         UInt64 numZeros = 0;
266         if (ReadZeroTail(stream, areThereNonZeros, numZeros, kRemMax) == S_OK)
267           if (!areThereNonZeros)
268             _totalSize += numZeros;
269       }
270     }
271   }
272 
273   return S_OK;
274 }
275 
276 
277 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
278     const UInt64 * /* maxCheckStartPosition */,
279     IArchiveOpenCallback * /* openArchiveCallback */))
280 {
281   COM_TRY_BEGIN
282   Close();
283   RINOK(Open2(stream))
284   _stream = stream;
285 
286   FOR_VECTOR (fileIndex, _items)
287   {
288     CPartition &item = _items[fileIndex];
289     const int typeIndex = FindPartType(item.Type);
290     if (typeIndex < 0)
291       continue;
292     const CPartType &t = kPartTypes[(unsigned)typeIndex];
293     if (t.Ext)
294     {
295       item.Ext = t.Ext;
296       continue;
297     }
298     if (t.Type && IsString1PrefixedByString2_NoCase_Ascii(t.Type, "Windows"))
299     {
300       CMyComPtr<ISequentialInStream> inStream;
301       if (
302           // ((IInArchiveGetStream *)this)->
303           GetStream(fileIndex, &inStream) == S_OK && inStream)
304       {
305         const char *fs = NMbr::GetFileSystem(inStream, item.GetSize(_sectorSizeLog));
306         if (fs)
307           item.Ext = fs;
308       }
309     }
310   }
311 
312   return S_OK;
313   COM_TRY_END
314 }
315 
316 Z7_COM7F_IMF(CHandler::Close())
317 {
318   _sectorSizeLog = 0;
319   _totalSize = 0;
320   memset(Guid, 0, sizeof(Guid));
321   _items.Clear();
322   _stream.Release();
323   return S_OK;
324 }
325 
326 static const Byte kProps[] =
327 {
328   kpidPath,
329   kpidSize,
330   kpidFileSystem,
331   kpidCharacts,
332   kpidOffset,
333   kpidId
334 };
335 
336 static const Byte kArcProps[] =
337 {
338   kpidSectorSize,
339   kpidId
340 };
341 
342 IMP_IInArchive_Props
343 IMP_IInArchive_ArcProps
344 
345 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
346 {
347   COM_TRY_BEGIN
348   NCOM::CPropVariant prop;
349   switch (propID)
350   {
351     case kpidMainSubfile:
352     {
353       if (_items.Size() == 1)
354         prop = (UInt32)0;
355       break;
356     }
357     case kpidPhySize: prop = _totalSize; break;
358     case kpidSectorSize: prop = (UInt32)((UInt32)1 << _sectorSizeLog); break;
359     case kpidId:
360     {
361       char s[48];
362       RawLeGuidToString_Upper(Guid, s);
363       prop = s;
364       break;
365     }
366   }
367   prop.Detach(value);
368   return S_OK;
369   COM_TRY_END
370 }
371 
372 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
373 {
374   *numItems = _items.Size();
375   return S_OK;
376 }
377 
378 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
379 {
380   COM_TRY_BEGIN
381   NCOM::CPropVariant prop;
382 
383   const CPartition &item = _items[index];
384 
385   switch (propID)
386   {
387     case kpidPath:
388     {
389       // Windows BDP partitions can have identical names.
390       // So we add the partition number at front
391       UString s;
392       s.Add_UInt32(index);
393       {
394         UString s2;
395         for (unsigned i = 0; i < kNameLen; i++)
396         {
397           wchar_t c = (wchar_t)Get16(item.Name + i * 2);
398           if (c == 0)
399             break;
400           s2 += c;
401         }
402         if (!s2.IsEmpty())
403         {
404           s.Add_Dot();
405           s += s2;
406         }
407       }
408       {
409         s.Add_Dot();
410         if (item.Ext)
411         {
412           AString fs (item.Ext);
413           fs.MakeLower_Ascii();
414           s += fs;
415         }
416         else
417           s += "img";
418       }
419       prop = s;
420       break;
421     }
422 
423     case kpidSize:
424     case kpidPackSize: prop = item.GetSize(_sectorSizeLog); break;
425     case kpidOffset: prop = item.GetPos(_sectorSizeLog); break;
426 
427     case kpidFileSystem:
428     {
429       char s[48];
430       const char *res;
431       const int typeIndex = FindPartType(item.Type);
432       if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Type)
433         res = kPartTypes[(unsigned)typeIndex].Type;
434       else
435       {
436         RawLeGuidToString_Upper(item.Type, s);
437         res = s;
438       }
439       prop = res;
440       break;
441     }
442 
443     case kpidId:
444     {
445       char s[48];
446       RawLeGuidToString_Upper(item.Id, s);
447       prop = s;
448       break;
449     }
450 
451     case kpidCharacts: FLAGS64_TO_PROP(g_PartitionFlags, item.Flags, prop); break;
452   }
453 
454   prop.Detach(value);
455   return S_OK;
456   COM_TRY_END
457 }
458 
459 // we suppport signature only for 512-bytes sector.
460 REGISTER_ARC_I(
461   "GPT", "gpt mbr", NULL, 0xCB,
462   k_Signature,
463   1 << 9,
464   0,
465   NULL)
466 
467 }}
468