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