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