xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/GptHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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   { 0x44479540, NULL, "Linux root (x86)" },
115   { 0x4F68BCE3, NULL, "Linux root (x86-64)" },
116   { 0x69DAD710, NULL, "Linux root (ARM)" },
117   { 0xB921B045, NULL, "Linux root (ARM64)" },
118   { 0x993D8D3D, NULL, "Linux root (IA-64)" },
119 
120 
121   { 0x83BD6B9D, NULL, "FreeBSD Boot" },
122   { 0x516E7CB4, NULL, "FreeBSD Data" },
123   { 0x516E7CB5, NULL, "FreeBSD Swap" },
124   { 0x516E7CB6, "ufs", "FreeBSD UFS" },
125   { 0x516E7CB8, NULL, "FreeBSD Vinum" },
126   { 0x516E7CB8, "zfs", "FreeBSD ZFS" },
127 
128   { 0x48465300, "hfsx", "HFS+" },
129   { 0x7C3457EF, "apfs", "APFS" },
130 };
131 
FindPartType(const Byte * guid)132 static int FindPartType(const Byte *guid)
133 {
134   const UInt32 val = Get32(guid);
135   for (unsigned i = 0; i < Z7_ARRAY_SIZE(kPartTypes); i++)
136     if (kPartTypes[i].Id == val)
137       return (int)i;
138   return -1;
139 }
140 
141 
RawLeGuidToString_Upper(const Byte * g,char * s)142 static void RawLeGuidToString_Upper(const Byte *g, char *s)
143 {
144   RawLeGuidToString(g, s);
145   // MyStringUpper_Ascii(s);
146 }
147 
148 
149 Z7_class_CHandler_final: public CHandlerCont
150 {
151   Z7_IFACE_COM7_IMP(IInArchive_Cont)
152 
153   CRecordVector<CPartition> _items;
154   UInt64 _totalSize;
155   unsigned _sectorSizeLog;
156   Byte Guid[16];
157 
158   CByteBuffer _buffer;
159 
160   HRESULT Open2(IInStream *stream);
161 
162   virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override
163   {
164     const CPartition &item = _items[index];
165     pos = item.GetPos(_sectorSizeLog);
166     size = item.GetSize(_sectorSizeLog);
167     return NExtract::NOperationResult::kOK;
168   }
169 };
170 
171 
172 HRESULT CHandler::Open2(IInStream *stream)
173 {
174   const unsigned kBufSize = 2 << 12;
175   _buffer.Alloc(kBufSize);
176   RINOK(ReadStream_FALSE(stream, _buffer, kBufSize))
177   const Byte *buf = _buffer;
178   if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
179     return S_FALSE;
180   {
181     for (unsigned sectorSizeLog = 9;; sectorSizeLog += 3)
182     {
183       if (sectorSizeLog > 12)
184         return S_FALSE;
185       if (memcmp(buf + ((size_t)1 << sectorSizeLog), k_Signature, k_SignatureSize) == 0)
186       {
187         buf += ((size_t)1 << sectorSizeLog);
188         _sectorSizeLog = sectorSizeLog;
189         break;
190       }
191     }
192   }
193   const UInt32 kSectorSize = 1u << _sectorSizeLog;
194   {
195     // if (Get32(buf + 8) != 0x10000) return S_FALSE; // revision
196     const UInt32 headerSize = Get32(buf + 12); // = 0x5C usually
197     if (headerSize > kSectorSize)
198       return S_FALSE;
199     const UInt32 crc = Get32(buf + 0x10);
200     SetUi32(_buffer + kSectorSize + 0x10, 0)
201     if (CrcCalc(_buffer + kSectorSize, headerSize) != crc)
202       return S_FALSE;
203   }
204   // UInt32 reserved = Get32(buf + 0x14);
205   const UInt64 curLba = Get64(buf + 0x18);
206   if (curLba != 1)
207     return S_FALSE;
208   const UInt64 backupLba = Get64(buf + 0x20);
209   // UInt64 firstUsableLba = Get64(buf + 0x28);
210   // UInt64 lastUsableLba = Get64(buf + 0x30);
211   memcpy(Guid, buf + 0x38, 16);
212   const UInt64 tableLba = Get64(buf + 0x48);
213   if (tableLba < 2 || (tableLba >> (63 - _sectorSizeLog)) != 0)
214     return S_FALSE;
215   const UInt32 numEntries = Get32(buf + 0x50);
216   if (numEntries > (1 << 16))
217     return S_FALSE;
218   const UInt32 entrySize = Get32(buf + 0x54); // = 128 usually
219   if (entrySize < 128 || entrySize > (1 << 12))
220     return S_FALSE;
221   const UInt32 entriesCrc = Get32(buf + 0x58);
222 
223   const UInt32 tableSize = entrySize * numEntries;
224   const UInt32 tableSizeAligned = (tableSize + kSectorSize - 1) & ~(kSectorSize - 1);
225   _buffer.Alloc(tableSizeAligned);
226   const UInt64 tableOffset = tableLba * kSectorSize;
227   RINOK(InStream_SeekSet(stream, tableOffset))
228   RINOK(ReadStream_FALSE(stream, _buffer, tableSizeAligned))
229 
230   if (CrcCalc(_buffer, tableSize) != entriesCrc)
231     return S_FALSE;
232 
233   _totalSize = tableOffset + tableSizeAligned;
234 
235   for (UInt32 i = 0; i < numEntries; i++)
236   {
237     CPartition item;
238     item.Parse(_buffer + i * entrySize);
239     if (item.IsUnused())
240       continue;
241     if (item.LastLba < item.FirstLba)
242       return S_FALSE;
243     if ((item.LastLba >> (63 - _sectorSizeLog)) != 0)
244       return S_FALSE;
245     const UInt64 endPos = item.GetEnd(_sectorSizeLog);
246     if (_totalSize < endPos)
247       _totalSize = endPos;
248     _items.Add(item);
249   }
250 
251   _buffer.Free();
252   {
253     if ((backupLba >> (63 - _sectorSizeLog)) != 0)
254       return S_FALSE;
255     const UInt64 end = (backupLba + 1) * kSectorSize;
256     if (_totalSize < end)
257       _totalSize = end;
258   }
259   {
260     UInt64 fileEnd;
261     RINOK(InStream_GetSize_SeekToEnd(stream, fileEnd))
262 
263     if (_totalSize < fileEnd)
264     {
265       const UInt64 rem = fileEnd - _totalSize;
266       const UInt64 kRemMax = 1 << 22;
267       if (rem <= kRemMax)
268       {
269         RINOK(InStream_SeekSet(stream, _totalSize))
270         bool areThereNonZeros = false;
271         UInt64 numZeros = 0;
272         if (ReadZeroTail(stream, areThereNonZeros, numZeros, kRemMax) == S_OK)
273           if (!areThereNonZeros)
274             _totalSize += numZeros;
275       }
276     }
277   }
278 
279   return S_OK;
280 }
281 
282 
283 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
284     const UInt64 * /* maxCheckStartPosition */,
285     IArchiveOpenCallback * /* openArchiveCallback */))
286 {
287   COM_TRY_BEGIN
288   Close();
289   RINOK(Open2(stream))
290   _stream = stream;
291 
292   FOR_VECTOR (fileIndex, _items)
293   {
294     CPartition &item = _items[fileIndex];
295     const int typeIndex = FindPartType(item.Type);
296     if (typeIndex < 0)
297       continue;
298     const CPartType &t = kPartTypes[(unsigned)typeIndex];
299     if (t.Ext)
300     {
301       item.Ext = t.Ext;
302       continue;
303     }
304     if (t.Type && IsString1PrefixedByString2_NoCase_Ascii(t.Type, "Windows"))
305     {
306       CMyComPtr<ISequentialInStream> inStream;
307       if (
308           // ((IInArchiveGetStream *)this)->
309           GetStream(fileIndex, &inStream) == S_OK && inStream)
310       {
311         const char *fs = NMbr::GetFileSystem(inStream, item.GetSize(_sectorSizeLog));
312         if (fs)
313           item.Ext = fs;
314       }
315     }
316   }
317 
318   return S_OK;
319   COM_TRY_END
320 }
321 
322 Z7_COM7F_IMF(CHandler::Close())
323 {
324   _sectorSizeLog = 0;
325   _totalSize = 0;
326   memset(Guid, 0, sizeof(Guid));
327   _items.Clear();
328   _stream.Release();
329   return S_OK;
330 }
331 
332 static const Byte kProps[] =
333 {
334   kpidPath,
335   kpidSize,
336   kpidFileSystem,
337   kpidCharacts,
338   kpidOffset,
339   kpidId
340 };
341 
342 static const Byte kArcProps[] =
343 {
344   kpidSectorSize,
345   kpidId
346 };
347 
348 IMP_IInArchive_Props
349 IMP_IInArchive_ArcProps
350 
351 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
352 {
353   COM_TRY_BEGIN
354   NCOM::CPropVariant prop;
355   switch (propID)
356   {
357     case kpidMainSubfile:
358     {
359       if (_items.Size() == 1)
360         prop = (UInt32)0;
361       break;
362     }
363     case kpidPhySize: prop = _totalSize; break;
364     case kpidSectorSize: prop = (UInt32)((UInt32)1 << _sectorSizeLog); break;
365     case kpidId:
366     {
367       char s[48];
368       RawLeGuidToString_Upper(Guid, s);
369       prop = s;
370       break;
371     }
372   }
373   prop.Detach(value);
374   return S_OK;
375   COM_TRY_END
376 }
377 
378 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
379 {
380   *numItems = _items.Size();
381   return S_OK;
382 }
383 
384 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
385 {
386   COM_TRY_BEGIN
387   NCOM::CPropVariant prop;
388 
389   const CPartition &item = _items[index];
390 
391   switch (propID)
392   {
393     case kpidPath:
394     {
395       // Windows BDP partitions can have identical names.
396       // So we add the partition number at front
397       UString s;
398       s.Add_UInt32(index);
399       {
400         UString s2;
401         for (unsigned i = 0; i < kNameLen; i++)
402         {
403           wchar_t c = (wchar_t)Get16(item.Name + i * 2);
404           if (c == 0)
405             break;
406           s2 += c;
407         }
408         if (!s2.IsEmpty())
409         {
410           s.Add_Dot();
411           s += s2;
412         }
413       }
414       {
415         s.Add_Dot();
416         if (item.Ext)
417         {
418           AString fs (item.Ext);
419           fs.MakeLower_Ascii();
420           s += fs;
421         }
422         else
423           s += "img";
424       }
425       prop = s;
426       break;
427     }
428 
429     case kpidSize:
430     case kpidPackSize: prop = item.GetSize(_sectorSizeLog); break;
431     case kpidOffset: prop = item.GetPos(_sectorSizeLog); break;
432 
433     case kpidFileSystem:
434     {
435       char s[48];
436       const char *res;
437       const int typeIndex = FindPartType(item.Type);
438       if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Type)
439         res = kPartTypes[(unsigned)typeIndex].Type;
440       else
441       {
442         RawLeGuidToString_Upper(item.Type, s);
443         res = s;
444       }
445       prop = res;
446       break;
447     }
448 
449     case kpidId:
450     {
451       char s[48];
452       RawLeGuidToString_Upper(item.Id, s);
453       prop = s;
454       break;
455     }
456 
457     case kpidCharacts: FLAGS64_TO_PROP(g_PartitionFlags, item.Flags, prop); break;
458   }
459 
460   prop.Detach(value);
461   return S_OK;
462   COM_TRY_END
463 }
464 
465 // we suppport signature only for 512-bytes sector.
466 REGISTER_ARC_I(
467   "GPT", "gpt mbr", NULL, 0xCB,
468   k_Signature,
469   1 << 9,
470   0,
471   NULL)
472 
473 }}
474