xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/UefiHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // UefiHandler.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/7zCrc.h"
12 #include "../../../C/Alloc.h"
13 #include "../../../C/LzmaDec.h"
14 #include "../../../C/CpuArch.h"
15 
16 #include "../../Common/AutoPtr.h"
17 #include "../../Common/ComTry.h"
18 #include "../../Common/IntToString.h"
19 #include "../../Common/MyBuffer.h"
20 #include "../../Common/StringConvert.h"
21 
22 #include "../../Windows/PropVariantUtils.h"
23 
24 #include "../Common/ProgressUtils.h"
25 #include "../Common/RegisterArc.h"
26 #include "../Common/StreamObjects.h"
27 #include "../Common/StreamUtils.h"
28 
29 #include "../Compress/CopyCoder.h"
30 #include "../Compress/LzhDecoder.h"
31 
32 #ifdef SHOW_DEBUG_INFO
33 #define PRF(x) x
34 #else
35 #define PRF(x)
36 #endif
37 
38 #define Get16(p) GetUi16(p)
39 #define Get32(p) GetUi32(p)
40 #define Get64(p) GetUi64(p)
41 #define Get24(p) (Get32(p) & 0xFFFFFF)
42 
43 namespace NArchive {
44 namespace NUefi {
45 
46 static const size_t kBufTotalSizeMax = 1 << 29;
47 static const unsigned kNumFilesMax = 1 << 18;
48 static const unsigned kLevelMax = 64;
49 
50 static const Byte k_IntelMeSignature[] =
51 {
52   0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
53   0x5A, 0xA5, 0xF0, 0x0F
54 };
55 
IsIntelMe(const Byte * p)56 static bool IsIntelMe(const Byte *p)
57 {
58   return memcmp(p, k_IntelMeSignature, sizeof(k_IntelMeSignature)) == 0;
59 }
60 
61 static const unsigned kFvHeaderSize = 0x38;
62 
63 static const unsigned kGuidSize = 16;
64 
65 #define CAPSULE_SIGNATURE   0xBD,0x86,0x66,0x3B,0x76,0x0D,0x30,0x40,0xB7,0x0E,0xB5,0x51,0x9E,0x2F,0xC5,0xA0
66 #define CAPSULE2_SIGNATURE  0x8B,0xA6,0x3C,0x4A,0x23,0x77,0xFB,0x48,0x80,0x3D,0x57,0x8C,0xC1,0xFE,0xC4,0x4D
67 #define CAPSULE_UEFI_SIGNATURE  0xB9,0x82,0x91,0x53,0xB5,0xAB,0x91,0x43,0xB6,0x9A,0xE3,0xA9,0x43,0xF7,0x2F,0xCC
68 /*
69   6dcbd5ed-e82d-4c44-bda1-7194199ad92a : Firmware Management `
70 */
71 
72 static const Byte k_Guids_Capsules[][kGuidSize] =
73 {
74   { CAPSULE_SIGNATURE },
75   { CAPSULE2_SIGNATURE },
76   { CAPSULE_UEFI_SIGNATURE }
77 };
78 
79 
80 static const unsigned kFfsGuidOffset = 16;
81 
82 #define FFS1_SIGNATURE  0xD9,0x54,0x93,0x7A,0x68,0x04,0x4A,0x44,0x81,0xCE,0x0B,0xF6,0x17,0xD8,0x90,0xDF
83 #define FFS2_SIGNATURE  0x78,0xE5,0x8C,0x8C,0x3D,0x8A,0x1C,0x4F,0x99,0x35,0x89,0x61,0x85,0xC3,0x2D,0xD3
84 #define MACFS_SIGNATURE 0xAD,0xEE,0xAD,0x04,0xFF,0x61,0x31,0x4D,0xB6,0xBA,0x64,0xF8,0xBF,0x90,0x1F,0x5A
85 // APPLE_BOOT
86 /*
87   "FFS3":        "5473c07a-3dcb-4dca-bd6f-1e9689e7349a",
88   "NVRAM_EVSA":  "fff12b8d-7696-4c8b-a985-2747075b4f50",
89   "NVRAM_NVAR":  "cef5b9a3-476d-497f-9fdc-e98143e0422c",
90   "NVRAM_EVSA2": "00504624-8a59-4eeb-bd0f-6b36e96128e0",
91 static const Byte k_NVRAM_NVAR_Guid[kGuidSize] =
92   { 0xA3,0xB9,0xF5,0xCE,0x6D,0x47,0x7F,0x49,0x9F,0xDC,0xE9,0x81,0x43,0xE0,0x42,0x2C };
93 */
94 
95 static const Byte k_Guids_FS[][kGuidSize] =
96 {
97   { FFS1_SIGNATURE },
98   { FFS2_SIGNATURE },
99   { MACFS_SIGNATURE }
100 };
101 
102 
103 static const UInt32 kFvSignature = 0x4856465F; // "_FVH"
104 
105 static const Byte kGuids[][kGuidSize] =
106 {
107   { 0xB0,0xCD,0x1B,0xFC,0x31,0x7D,0xAA,0x49,0x93,0x6A,0xA4,0x60,0x0D,0x9D,0xD0,0x83 },
108   { 0x2E,0x06,0xA0,0x1B,0x79,0xC7,0x82,0x45,0x85,0x66,0x33,0x6A,0xE8,0xF7,0x8F,0x09 },
109   { 0x25,0x4E,0x37,0x7E,0x01,0x8E,0xEE,0x4F,0x87,0xf2,0x39,0x0C,0x23,0xC6,0x06,0xCD },
110   { 0x97,0xE5,0x1B,0x16,0xC5,0xE9,0xDB,0x49,0xAE,0x50,0xC4,0x62,0xAB,0x54,0xEE,0xDA },
111   { 0xDB,0x7F,0xAD,0x77,0x2A,0xDF,0x02,0x43,0x88,0x98,0xC7,0x2E,0x4C,0xDB,0xD0,0xF4 },
112   { 0xAB,0x71,0xCF,0xF5,0x4B,0xB0,0x7E,0x4B,0x98,0x8A,0xD8,0xA0,0xD4,0x98,0xE6,0x92 },
113   { 0x91,0x45,0x53,0x7A,0xCE,0x37,0x81,0x48,0xB3,0xC9,0x71,0x38,0x14,0xF4,0x5D,0x6B },
114   { 0x84,0xE6,0x7A,0x36,0x5D,0x33,0x71,0x46,0xA1,0x6D,0x89,0x9D,0xBF,0xEA,0x6B,0x88 },
115   { 0x98,0x07,0x40,0x24,0x07,0x38,0x42,0x4A,0xB4,0x13,0xA1,0xEC,0xEE,0x20,0x5D,0xD8 },
116   { 0xEE,0xA2,0x3F,0x28,0x2C,0x53,0x4D,0x48,0x93,0x83,0x9F,0x93,0xB3,0x6F,0x0B,0x7E },
117   { 0x9B,0xD5,0xB8,0x98,0xBA,0xE8,0xEE,0x48,0x98,0xDD,0xC2,0x95,0x39,0x2F,0x1E,0xDB },
118   { 0x09,0x6D,0xE3,0xC3,0x94,0x82,0x97,0x4B,0xA8,0x57,0xD5,0x28,0x8F,0xE3,0x3E,0x28 },
119   { 0x18,0x88,0x53,0x4A,0xE0,0x5A,0xB2,0x4E,0xB2,0xEB,0x48,0x8B,0x23,0x65,0x70,0x22 }
120 };
121 
122 static const Byte k_Guid_LZMA_COMPRESSED[kGuidSize] =
123   { 0x98,0x58,0x4E,0xEE,0x14,0x39,0x59,0x42,0x9D,0x6E,0xDC,0x7B,0xD7,0x94,0x03,0xCF };
124 
125 static const char * const kGuidNames[] =
126 {
127     "CRC"
128   , "VolumeTopFile"
129   , "ACPI"
130   , "ACPI2"
131   , "Main"
132   , "Intel32"
133   , "Intel64"
134   , "Intel32c"
135   , "Intel64c"
136   , "MacVolume"
137   , "MacUpdate.txt"
138   , "MacName"
139   , "Insyde"
140 };
141 
142 enum
143 {
144   kGuidIndex_CRC = 0
145 };
146 
147 struct CSigExtPair
148 {
149   const char *ext;
150   unsigned sigSize;
151   Byte sig[16];
152 };
153 
154 static const CSigExtPair g_Sigs[] =
155 {
156   { "bmp",  2, { 'B','M' } },
157   { "riff", 4, { 'R','I','F','F' } },
158   { "pe",   2, { 'M','Z'} },
159   { "gif",  6, { 'G','I','F','8','9', 'a' } },
160   { "png",  8, { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A } },
161   { "jpg", 10, { 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46 } },
162   { "rom",  2, { 0x55,0xAA } }
163 };
164 
165 enum
166 {
167   kSig_BMP,
168   kSig_RIFF,
169   kSig_PE
170 };
171 
FindExt(const Byte * p,size_t size)172 static const char *FindExt(const Byte *p, size_t size)
173 {
174   unsigned i;
175   for (i = 0; i < Z7_ARRAY_SIZE(g_Sigs); i++)
176   {
177     const CSigExtPair &pair = g_Sigs[i];
178     if (size >= pair.sigSize)
179       if (memcmp(p, pair.sig, pair.sigSize) == 0)
180         break;
181   }
182   if (i == Z7_ARRAY_SIZE(g_Sigs))
183     return NULL;
184   switch (i)
185   {
186     case kSig_BMP:
187       if (GetUi32(p + 2) > size || GetUi32(p + 0xA) > size)
188         return NULL;
189       break;
190     case kSig_RIFF:
191       if (GetUi32(p + 8) == 0x45564157 || GetUi32(p + 0xC) == 0x20746D66 )
192         return "wav";
193       break;
194     case kSig_PE:
195     {
196       if (size < 512)
197         return NULL;
198       UInt32 peOffset = GetUi32(p + 0x3C);
199       if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0)
200         return NULL;
201       if (GetUi32(p + peOffset) != 0x00004550)
202         return NULL;
203       break;
204     }
205   }
206   return g_Sigs[i].ext;
207 }
208 
AreGuidsEq(const Byte * p1,const Byte * p2)209 static bool AreGuidsEq(const Byte *p1, const Byte *p2)
210 {
211   return memcmp(p1, p2, kGuidSize) == 0;
212 }
213 
FindGuid(const Byte * p)214 static int FindGuid(const Byte *p)
215 {
216   for (unsigned i = 0; i < Z7_ARRAY_SIZE(kGuids); i++)
217     if (AreGuidsEq(p, kGuids[i]))
218       return (int)i;
219   return -1;
220 }
221 
IsFfs(const Byte * p)222 static bool IsFfs(const Byte *p)
223 {
224   if (Get32(p + 0x28) != kFvSignature)
225     return false;
226   for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_Guids_FS); i++)
227     if (AreGuidsEq(p + kFfsGuidOffset, k_Guids_FS[i]))
228       return true;
229   return false;
230 }
231 
232 #define FVB_ERASE_POLARITY  (1 << 11)
233 
234 /*
235 static const CUInt32PCharPair g_FV_Attribs[] =
236 {
237   {  0, "ReadDisabledCap" },
238   {  1, "ReadEnabledCap" },
239   {  2, "ReadEnabled" },
240   {  3, "WriteDisabledCap" },
241   {  4, "WriteEnabledCap" },
242   {  5, "WriteEnabled" },
243   {  6, "LockCap" },
244   {  7, "Locked" },
245 
246   {  9, "StickyWrite" },
247   { 10, "MemoryMapped" },
248   { 11, "ErasePolarity" },
249 
250   { 12, "ReadLockCap" },
251   { 13, "WriteLockCap" },
252   { 14, "WriteLockCap" }
253 };
254 */
255 
256 enum
257 {
258   FV_FILETYPE_ALL,
259   FV_FILETYPE_RAW,
260   FV_FILETYPE_FREEFORM,
261   FV_FILETYPE_SECURITY_CORE,
262   FV_FILETYPE_PEI_CORE,
263   FV_FILETYPE_DXE_CORE,
264   FV_FILETYPE_PEIM,
265   FV_FILETYPE_DRIVER,
266   FV_FILETYPE_COMBINED_PEIM_DRIVER,
267   FV_FILETYPE_APPLICATION,
268   // The value 0x0A is reserved and should not be used
269   FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0x0B,
270   // types 0xF0 - 0xFF are FFS file types
271   FV_FILETYPE_FFS_PAD = 0xF0
272 };
273 
274 static const char * const g_FileTypes[] =
275 {
276     "ALL"
277   , "RAW"
278   , "FREEFORM"
279   , "SECURITY_CORE"
280   , "PEI_CORE"
281   , "DXE_CORE"
282   , "PEIM"
283   , "DRIVER"
284   , "COMBINED_PEIM_DRIVER"
285   , "APPLICATION"
286   , "0xA"
287   , "VOLUME"
288 };
289 
290 // typedef Byte FFS_FILE_ATTRIBUTES;
291 // FFS File Attributes
292 #define FFS_ATTRIB_TAIL_PRESENT 0x01
293 // #define FFS_ATTRIB_RECOVERY 0x02
294 // #define FFS_ATTRIB_HEADER_EXTENSION 0x04
295 // #define FFS_ATTRIB_DATA_ALIGNMENT 0x38
296 #define FFS_ATTRIB_CHECKSUM 0x40
297 
298 static const CUInt32PCharPair g_FFS_FILE_ATTRIBUTES[] =
299 {
300   { 0, "" /* "TAIL" */ },
301   { 1, "RECOVERY" },
302   // { 2, "HEADER_EXTENSION" }, // reserved for future
303   { 6, "" /* "CHECKSUM" */ }
304 };
305 
306 // static const Byte g_Allignment[8] = { 3, 4, 7, 9, 10, 12, 15, 16 };
307 
308 // typedef Byte FFS_FILE_STATE;
309 
310 // Look also FVB_ERASE_POLARITY.
311 // Lower-order State bits are superceded by higher-order State bits.
312 
313 // #define FILE_HEADER_CONSTRUCTION  0x01
314 // #define FILE_HEADER_VALID         0x02
315 #define FILE_DATA_VALID           0x04
316 // #define FILE_MARKED_FOR_UPDATE    0x08
317 // #define FILE_DELETED              0x10
318 // #define FILE_HEADER_INVALID       0x20
319 
320 // SECTION_TYPE
321 
322 // #define SECTION_ALL 0x00
323 
324 #define SECTION_COMPRESSION  0x01
325 #define SECTION_GUID_DEFINED 0x02
326 
327 // Leaf section Type values
328 // #define SECTION_PE32      0x10
329 // #define SECTION_PIC       0x11
330 // #define SECTION_TE        0x12
331 #define SECTION_DXE_DEPEX 0x13
332 #define SECTION_VERSION   0x14
333 #define SECTION_USER_INTERFACE 0x15
334 // #define SECTION_COMPATIBILITY16 0x16
335 #define SECTION_FIRMWARE_VOLUME_IMAGE 0x17
336 #define SECTION_FREEFORM_SUBTYPE_GUID 0x18
337 #define SECTION_RAW       0x19
338 #define SECTION_PEI_DEPEX 0x1B
339 
340 
341 // #define GUIDED_SECTION_PROCESSING_REQUIRED 0x01
342 // #define GUIDED_SECTION_AUTH_STATUS_VALID 0x02
343 
344 static const CUInt32PCharPair g_GUIDED_SECTION_ATTRIBUTES[] =
345 {
346   { 0, "PROCESSING_REQUIRED" },
347   { 1, "AUTH" }
348 };
349 
350 static const CUInt32PCharPair g_SECTION_TYPE[] =
351 {
352   { 0x01, "COMPRESSION" },
353   { 0x02, "GUID" },
354   { 0x10, "efi" },
355   { 0x11, "PIC" },
356   { 0x12, "te" },
357   { 0x13, "DXE_DEPEX" },
358   { 0x14, "VERSION" },
359   { 0x15, "USER_INTERFACE" },
360   { 0x16, "COMPATIBILITY16" },
361   { 0x17, "VOLUME" },
362   { 0x18, "FREEFORM_SUBTYPE_GUID" },
363   { 0x19, "raw" },
364   { 0x1B, "PEI_DEPEX" }
365 };
366 
367 #define COMPRESSION_TYPE_NONE 0
368 #define COMPRESSION_TYPE_LZH  1
369 #define COMPRESSION_TYPE_LZMA 2
370 
371 static const char * const g_Methods[] =
372 {
373     "COPY"
374   , "LZH"
375   , "LZMA"
376 };
377 
378 
AddGuid(AString & dest,const Byte * p,bool full)379 static void AddGuid(AString &dest, const Byte *p, bool full)
380 {
381   char s[64];
382   ::RawLeGuidToString(p, s);
383   // MyStringUpper_Ascii(s);
384   if (!full)
385     s[8] = 0;
386   dest += s;
387 }
388 
389 static const char * const kExpressionCommands[] =
390 {
391   "BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"
392 };
393 
ParseDepedencyExpression(const Byte * p,UInt32 size,AString & res)394 static bool ParseDepedencyExpression(const Byte *p, UInt32 size, AString &res)
395 {
396   res.Empty();
397   for (UInt32 i = 0; i < size;)
398   {
399     unsigned command = p[i++];
400     if (command > Z7_ARRAY_SIZE(kExpressionCommands))
401       return false;
402     res += kExpressionCommands[command];
403     if (command < 3)
404     {
405       if (i + kGuidSize > size)
406         return false;
407       res.Add_Space();
408       AddGuid(res, p + i, false);
409       i += kGuidSize;
410     }
411     res += "; ";
412   }
413   return true;
414 }
415 
ParseUtf16zString(const Byte * p,UInt32 size,UString & res)416 static bool ParseUtf16zString(const Byte *p, UInt32 size, UString &res)
417 {
418   if ((size & 1) != 0)
419     return false;
420   res.Empty();
421   UInt32 i;
422   for (i = 0; i < size; i += 2)
423   {
424     wchar_t c = Get16(p + i);
425     if (c == 0)
426       break;
427     res += c;
428   }
429   return (i == size - 2);
430 }
431 
ParseUtf16zString2(const Byte * p,UInt32 size,AString & res)432 static bool ParseUtf16zString2(const Byte *p, UInt32 size, AString &res)
433 {
434   UString s;
435   if (!ParseUtf16zString(p, size, s))
436     return false;
437   res = UnicodeStringToMultiByte(s);
438   return true;
439 }
440 
441 #define FLAGS_TO_STRING(pairs, value) FlagsToString(pairs, Z7_ARRAY_SIZE(pairs), value)
442 #define TYPE_TO_STRING(table, value) TypeToString(table, Z7_ARRAY_SIZE(table), value)
443 #define TYPE_PAIR_TO_STRING(table, value) TypePairToString(table, Z7_ARRAY_SIZE(table), value)
444 
445 static const UInt32 kFileHeaderSize = 24;
446 
AddSpaceAndString(AString & res,const AString & newString)447 static void AddSpaceAndString(AString &res, const AString &newString)
448 {
449   if (!newString.IsEmpty())
450   {
451     res.Add_Space_if_NotEmpty();
452     res += newString;
453   }
454 }
455 
456 class CFfsFileHeader
457 {
458 PRF(public:)
459   Byte CheckHeader;
460   Byte CheckFile;
461   Byte Attrib;
462   Byte State;
463 
GetTailReference() const464   UInt16 GetTailReference() const { return (UInt16)(CheckHeader | ((UInt16)CheckFile << 8)); }
GetTailSize() const465   UInt32 GetTailSize() const { return IsThereTail() ? 2 : 0; }
IsThereFileChecksum() const466   bool IsThereFileChecksum() const { return (Attrib & FFS_ATTRIB_CHECKSUM) != 0; }
IsThereTail() const467   bool IsThereTail() const { return (Attrib & FFS_ATTRIB_TAIL_PRESENT) != 0; }
468 public:
469   Byte GuidName[kGuidSize];
470   Byte Type;
471   UInt32 Size;
472 
Parse(const Byte * p)473   bool Parse(const Byte *p)
474   {
475     unsigned i;
476     for (i = 0; i < kFileHeaderSize; i++)
477       if (p[i] != 0xFF)
478         break;
479     if (i == kFileHeaderSize)
480       return false;
481     memcpy(GuidName, p, kGuidSize);
482     CheckHeader = p[0x10];
483     CheckFile = p[0x11];
484     Type = p[0x12];
485     Attrib = p[0x13];
486     Size = Get24(p + 0x14);
487     State = p[0x17];
488     return true;
489   }
490 
GetDataSize() const491   UInt32 GetDataSize() const { return Size - kFileHeaderSize - GetTailSize(); }
GetDataSize2(UInt32 rem) const492   UInt32 GetDataSize2(UInt32 rem) const { return rem - kFileHeaderSize - GetTailSize(); }
493 
Check(const Byte * p,UInt32 size)494   bool Check(const Byte *p, UInt32 size)
495   {
496     if (Size > size)
497       return false;
498     UInt32 tailSize = GetTailSize();
499     if (Size < kFileHeaderSize + tailSize)
500       return false;
501 
502     {
503       unsigned checkSum = 0;
504       for (UInt32 i = 0; i < kFileHeaderSize; i++)
505         checkSum += p[i];
506       checkSum -= p[0x17];
507       checkSum -= p[0x11];
508       if ((Byte)checkSum != 0)
509         return false;
510     }
511 
512     if (IsThereFileChecksum())
513     {
514       unsigned checkSum = 0;
515       UInt32 checkSize = Size - tailSize;
516       for (UInt32 i = 0; i < checkSize; i++)
517         checkSum += p[i];
518       checkSum -= p[0x17];
519       if ((Byte)checkSum != 0)
520         return false;
521     }
522 
523     if (IsThereTail())
524       if (GetTailReference() != (UInt16)~Get16(p + Size - 2))
525         return false;
526 
527     int polarity = 0;
528     int i;
529     for (i = 5; i >= 0; i--)
530       if (((State >> i) & 1) == polarity)
531       {
532         // AddSpaceAndString(s, g_FFS_FILE_STATE_Flags[i]);
533         if ((1 << i) != FILE_DATA_VALID)
534           return false;
535         break;
536       }
537     if (i < 0)
538       return false;
539 
540     return true;
541   }
542 
GetCharacts() const543   AString GetCharacts() const
544   {
545     AString s;
546     if (Type == FV_FILETYPE_FFS_PAD)
547       s += "PAD";
548     else
549       s += TYPE_TO_STRING(g_FileTypes, Type);
550     AddSpaceAndString(s, FLAGS_TO_STRING(g_FFS_FILE_ATTRIBUTES, Attrib & 0xC7));
551     /*
552     int align = (Attrib >> 3) & 7;
553     if (align != 0)
554     {
555       s += " Align:";
556       s.Add_UInt32((UInt32)1 << g_Allignment[align]);
557     }
558     */
559     return s;
560   }
561 };
562 
563 #define G32(_offs_, dest) dest = Get32(p + (_offs_))
564 #define G16(_offs_, dest) dest = Get16(p + (_offs_))
565 
566 struct CCapsuleHeader
567 {
568   UInt32 HeaderSize;
569   UInt32 Flags;
570   UInt32 CapsuleImageSize;
571   UInt32 SequenceNumber;
572   // Guid InstanceId;
573   UInt32 OffsetToSplitInformation;
574   UInt32 OffsetToCapsuleBody;
575   UInt32 OffsetToOemDefinedHeader;
576   UInt32 OffsetToAuthorInformation;
577   UInt32 OffsetToRevisionInformation;
578   UInt32 OffsetToShortDescription;
579   UInt32 OffsetToLongDescription;
580   UInt32 OffsetToApplicableDevices;
581 
ClearNArchive::NUefi::CCapsuleHeader582   void Clear() { memset(this, 0, sizeof(*this)); }
583 
ParseNArchive::NUefi::CCapsuleHeader584   bool Parse(const Byte *p)
585   {
586     Clear();
587     G32(0x10, HeaderSize);
588     G32(0x14, Flags);
589     G32(0x18, CapsuleImageSize);
590     if (HeaderSize < 0x1C)
591       return false;
592     if (AreGuidsEq(p, k_Guids_Capsules[0]))
593     {
594       const unsigned kHeaderSize = 80;
595       if (HeaderSize != kHeaderSize)
596         return false;
597       G32(0x1C, SequenceNumber);
598       G32(0x30, OffsetToSplitInformation);
599       G32(0x34, OffsetToCapsuleBody);
600       G32(0x38, OffsetToOemDefinedHeader);
601       G32(0x3C, OffsetToAuthorInformation);
602       G32(0x40, OffsetToRevisionInformation);
603       G32(0x44, OffsetToShortDescription);
604       G32(0x48, OffsetToLongDescription);
605       G32(0x4C, OffsetToApplicableDevices);
606       return true;
607     }
608     else if (AreGuidsEq(p, k_Guids_Capsules[1]))
609     {
610       // capsule v2
611       G16(0x1C, OffsetToCapsuleBody);
612       G16(0x1E, OffsetToOemDefinedHeader);
613       return true;
614     }
615     else if (AreGuidsEq(p, k_Guids_Capsules[2]))
616     {
617       OffsetToCapsuleBody = HeaderSize;
618       return true;
619     }
620     else
621     {
622       // here we must check for another capsule types
623       return false;
624     }
625   }
626 };
627 
628 
629 struct CItem
630 {
631   AString Name;
632   AString Characts;
633   int Parent;
634   int Method;
635   int NameIndex;
636   unsigned NumChilds;
637   bool IsDir;
638   bool Skip;
639   bool ThereAreSubDirs;
640   bool ThereIsUniqueName;
641   bool KeepName;
642 
643   unsigned BufIndex;
644   UInt32 Offset;
645   UInt32 Size;
646 
CItemNArchive::NUefi::CItem647   CItem(): Parent(-1), Method(-1), NameIndex(-1), NumChilds(0),
648       IsDir(false), Skip(false), ThereAreSubDirs(false), ThereIsUniqueName(false), KeepName(true) {}
649   void SetGuid(const Byte *guidName, bool full = false);
650   AString GetName(int numChildsInParent) const;
651 };
652 
SetGuid(const Byte * guidName,bool full)653 void CItem::SetGuid(const Byte *guidName, bool full)
654 {
655   ThereIsUniqueName = true;
656   int index = FindGuid(guidName);
657   if (index >= 0)
658     Name = kGuidNames[(unsigned)index];
659   else
660   {
661     Name.Empty();
662     AddGuid(Name, guidName, full);
663   }
664 }
665 
GetName(int numChildsInParent) const666 AString CItem::GetName(int numChildsInParent) const
667 {
668   if (numChildsInParent <= 1 || NameIndex < 0)
669     return Name;
670   char sz[32];
671   char sz2[32];
672   ConvertUInt32ToString((unsigned)NameIndex, sz);
673   ConvertUInt32ToString((unsigned)numChildsInParent - 1, sz2);
674   const int numZeros = (int)strlen(sz2) - (int)strlen(sz);
675   AString res;
676   for (int i = 0; i < numZeros; i++)
677     res += '0';
678   res += sz;
679   res.Add_Dot();
680   res += Name;
681   return res;
682 }
683 
684 struct CItem2
685 {
686   AString Name;
687   AString Characts;
688   unsigned MainIndex;
689   int Parent;
690 
CItem2NArchive::NUefi::CItem2691   CItem2(): Parent(-1) {}
692 };
693 
694 
695 Z7_CLASS_IMP_CHandler_IInArchive_1(
696     IInArchiveGetStream
697 )
698   CObjectVector<CItem> _items;
699   CObjectVector<CItem2> _items2;
700   CObjectVector<CByteBuffer> _bufs;
701   UString _comment;
702   UInt32 _methodsMask;
703   bool _capsuleMode;
704   bool _headersError;
705 
706   size_t _totalBufsSize;
707   CCapsuleHeader _h;
708   UInt64 _phySize;
709 
710   void AddCommentString(const char *name, UInt32 pos);
711   unsigned AddItem(const CItem &item);
712   unsigned AddFileItemWithIndex(CItem &item);
713   unsigned AddDirItem(CItem &item);
714   unsigned AddBuf(size_t size);
715 
716   HRESULT DecodeLzma(const Byte *data, size_t inputSize);
717 
718   HRESULT ParseSections(unsigned bufIndex, UInt32 pos,
719       UInt32 size,
720       int parent, int method, unsigned level,
721       bool &error);
722 
723   HRESULT ParseIntelMe(unsigned bufIndex, UInt32 posBase,
724       UInt32 exactSize, UInt32 limitSize,
725       int parent, int method, unsigned level);
726 
727   HRESULT ParseVolume(unsigned bufIndex, UInt32 posBase,
728       UInt32 exactSize, UInt32 limitSize,
729       int parent, int method, unsigned level);
730 
731   HRESULT OpenCapsule(IInStream *stream);
732   HRESULT OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
733   HRESULT Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
734 public:
735   CHandler(bool capsuleMode): _capsuleMode(capsuleMode) {}
736 };
737 
738 
739 static const Byte kProps[] =
740 {
741   kpidPath,
742   kpidIsDir,
743   kpidSize,
744   // kpidOffset,
745   kpidMethod,
746   kpidCharacts
747 };
748 
749 static const Byte kArcProps[] =
750 {
751   kpidComment,
752   kpidMethod,
753   kpidCharacts
754 };
755 
756 IMP_IInArchive_Props
757 IMP_IInArchive_ArcProps
758 
759 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
760 {
761   COM_TRY_BEGIN
762   NWindows::NCOM::CPropVariant prop;
763   const CItem2 &item2 = _items2[index];
764   const CItem &item = _items[item2.MainIndex];
765   switch (propID)
766   {
767     case kpidPath:
768     {
769       AString path (item2.Name);
770       int cur = item2.Parent;
771       while (cur >= 0)
772       {
773         const CItem2 &item3 = _items2[cur];
774         path.InsertAtFront(CHAR_PATH_SEPARATOR);
775         path.Insert(0, item3.Name);
776         cur = item3.Parent;
777       }
778       prop = path;
779       break;
780     }
781     case kpidIsDir: prop = item.IsDir; break;
782     case kpidMethod: if (item.Method >= 0) prop = g_Methods[(unsigned)item.Method]; break;
783     case kpidCharacts: if (!item2.Characts.IsEmpty()) prop = item2.Characts; break;
784     case kpidSize: if (!item.IsDir) prop = (UInt64)item.Size; break;
785     // case kpidOffset: if (!item.IsDir) prop = item.Offset; break;
786   }
787   prop.Detach(value);
788   return S_OK;
789   COM_TRY_END
790 }
791 
792 void CHandler::AddCommentString(const char *name, UInt32 pos)
793 {
794   UString s;
795   if (pos < _h.HeaderSize)
796     return;
797   if (pos >= _h.OffsetToCapsuleBody)
798     return;
799   UInt32 limit = (_h.OffsetToCapsuleBody - pos) & ~(UInt32)1;
800   const Byte *buf = _bufs[0] + pos;
801   for (UInt32 i = 0;;)
802   {
803     if (s.Len() > (1 << 16) || i >= limit)
804       return;
805     wchar_t c = Get16(buf + i);
806     i += 2;
807     if (c == 0)
808     {
809       if (i >= limit)
810         return;
811       c = Get16(buf + i);
812       i += 2;
813       if (c == 0)
814         break;
815       s.Add_LF();
816     }
817     s += c;
818   }
819   if (s.IsEmpty())
820     return;
821   _comment.Add_LF();
822   _comment += name;
823   _comment += ": ";
824   _comment += s;
825 }
826 
827 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
828 {
829   COM_TRY_BEGIN
830   NWindows::NCOM::CPropVariant prop;
831   switch (propID)
832   {
833     case kpidMethod:
834     {
835       AString s;
836       for (unsigned i = 0; i < 32; i++)
837         if ((_methodsMask & ((UInt32)1 << i)) != 0)
838           AddSpaceAndString(s, (AString)g_Methods[i]);
839       if (!s.IsEmpty())
840         prop = s;
841       break;
842     }
843     case kpidComment: if (!_comment.IsEmpty()) prop = _comment; break;
844     case kpidPhySize: prop = (UInt64)_phySize; break;
845 
846     case kpidErrorFlags:
847     {
848       UInt32 v = 0;
849       if (_headersError) v |= kpv_ErrorFlags_HeadersError;
850       if (v != 0)
851         prop = v;
852       break;
853     }
854   }
855   prop.Detach(value);
856   return S_OK;
857   COM_TRY_END
858 }
859 
860 #ifdef SHOW_DEBUG_INFO
861 static void PrintLevel(unsigned level)
862 {
863   PRF(printf("\n"));
864   for (unsigned i = 0; i < level; i++)
865     PRF(printf("  "));
866 }
867 static void MyPrint(UInt32 posBase, UInt32 size, unsigned level, const char *name)
868 {
869   PrintLevel(level);
870   PRF(printf("%s, pos = %6x, size = %6x", name, posBase, size));
871 }
872 #else
873 #define PrintLevel(level)
874 #define MyPrint(posBase, size, level, name)
875 #endif
876 
877 
878 
879 unsigned CHandler::AddItem(const CItem &item)
880 {
881   if (_items.Size() >= kNumFilesMax)
882     throw 2;
883   return _items.Add(item);
884 }
885 
886 unsigned CHandler::AddFileItemWithIndex(CItem &item)
887 {
888   unsigned nameIndex = _items.Size();
889   if (item.Parent >= 0)
890     nameIndex = _items[item.Parent].NumChilds++;
891   item.NameIndex = (int)nameIndex;
892   return AddItem(item);
893 }
894 
895 unsigned CHandler::AddDirItem(CItem &item)
896 {
897   if (item.Parent >= 0)
898     _items[item.Parent].ThereAreSubDirs = true;
899   item.IsDir = true;
900   item.Size = 0;
901   return AddItem(item);
902 }
903 
904 unsigned CHandler::AddBuf(size_t size)
905 {
906   if (size > kBufTotalSizeMax - _totalBufsSize)
907     throw 1;
908   _totalBufsSize += size;
909   unsigned index = _bufs.Size();
910   _bufs.AddNew().Alloc(size);
911   return index;
912 }
913 
914 
915 HRESULT CHandler::DecodeLzma(const Byte *data, size_t inputSize)
916 {
917   if (inputSize < 5 + 8)
918     return S_FALSE;
919   const UInt64 unpackSize = Get64(data + 5);
920   if (unpackSize > ((UInt32)1 << 30))
921     return S_FALSE;
922   SizeT destLen = (SizeT)unpackSize;
923   const unsigned newBufIndex = AddBuf((size_t)unpackSize);
924   CByteBuffer &buf = _bufs[newBufIndex];
925   ELzmaStatus status;
926   SizeT srcLen = inputSize - (5 + 8);
927   const SizeT srcLen2 = srcLen;
928   SRes res = LzmaDecode(buf, &destLen, data + 13, &srcLen,
929       data, 5, LZMA_FINISH_END, &status, &g_Alloc);
930   if (res != 0)
931     return S_FALSE;
932   if (srcLen != srcLen2 || destLen != unpackSize || (
933       status != LZMA_STATUS_FINISHED_WITH_MARK &&
934       status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
935     return S_FALSE;
936   return S_OK;
937 }
938 
939 
940 HRESULT CHandler::ParseSections(unsigned bufIndex, UInt32 posBase, UInt32 size, int parent, int method, unsigned level, bool &error)
941 {
942   error = false;
943 
944   if (level > kLevelMax)
945     return S_FALSE;
946   MyPrint(posBase, size, level, "Sections");
947   level++;
948   const Byte *bufData = _bufs[bufIndex];
949   UInt32 pos = 0;
950   for (;;)
951   {
952     if (size == pos)
953       return S_OK;
954     PrintLevel(level);
955     PRF(printf("%s, abs = %6x, relat = %6x", "Sect", posBase + pos, pos));
956     pos = (pos + 3) & ~(UInt32)3;
957     if (pos > size)
958       return S_FALSE;
959     UInt32 rem = size - pos;
960     if (rem == 0)
961       return S_OK;
962     if (rem < 4)
963       return S_FALSE;
964 
965     const Byte *p = bufData + posBase + pos;
966 
967     const UInt32 sectSize = Get24(p);
968     const Byte type = p[3];
969 
970     // PrintLevel(level);
971     PRF(printf(" type = %2x, sectSize = %6x", type, sectSize));
972 
973     if (sectSize > rem || sectSize < 4)
974     {
975       _headersError = true;
976       error = true;
977       return S_OK;
978       // return S_FALSE;
979     }
980 
981     CItem item;
982     item.Method = method;
983     item.BufIndex = bufIndex;
984     item.Parent = parent;
985     item.Offset = posBase + pos + 4;
986     UInt32 sectDataSize = sectSize - 4;
987     item.Size = sectDataSize;
988     item.Name = TYPE_PAIR_TO_STRING(g_SECTION_TYPE, type);
989 
990     if (type == SECTION_COMPRESSION)
991     {
992       if (sectSize < 4 + 5)
993         return S_FALSE;
994       const UInt32 uncompressedSize = Get32(p + 4);
995       const Byte compressionType = p[8];
996 
997       UInt32 newSectSize = sectSize - 9;
998       UInt32 newOffset = posBase + pos + 9;
999       const Byte *pStart = p + 9;
1000 
1001       item.KeepName = false;
1002       if (compressionType > 2)
1003       {
1004         // AddFileItemWithIndex(item);
1005         return S_FALSE;
1006       }
1007       else
1008       {
1009         item.Name = g_Methods[compressionType];
1010         // int parent = AddDirItem(item);
1011         if (compressionType == COMPRESSION_TYPE_NONE)
1012         {
1013           bool error2;
1014           RINOK(ParseSections(bufIndex, newOffset, newSectSize, parent, method, level, error2))
1015         }
1016         else if (compressionType == COMPRESSION_TYPE_LZH)
1017         {
1018           const unsigned newBufIndex = AddBuf(uncompressedSize);
1019           CByteBuffer &buf = _bufs[newBufIndex];
1020           CMyUniquePtr<NCompress::NLzh::NDecoder::CCoder> lzhDecoder;
1021           lzhDecoder.Create_if_Empty();
1022           {
1023             const Byte *src = pStart;
1024             if (newSectSize < 8)
1025               return S_FALSE;
1026             UInt32 packSize = Get32(src);
1027             const UInt32 unpackSize = Get32(src + 4);
1028 
1029             PRF(printf(" LZH packSize = %6x, unpackSize = %6x", packSize, unpackSize));
1030 
1031             if (uncompressedSize != unpackSize || newSectSize - 8 != packSize)
1032               return S_FALSE;
1033             if (packSize < 1)
1034               return S_FALSE;
1035             packSize--;
1036             src += 8;
1037             if (src[packSize] != 0)
1038               return S_FALSE;
1039 
1040             CMyComPtr2_Create<IInStream, CBufInStream> inStream;
1041             CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> outStream;
1042             // const UInt64 uncompressedSize64 = uncompressedSize;
1043             // lzhDecoder->FinishMode = true;
1044             /*
1045               EFI 1.1 probably used LZH with small dictionary and (pbit = 4). It was named "Efi compression".
1046               New version of compression code (named Tiano) uses LZH with (1 << 19) dictionary.
1047               But maybe LZH decoder in UEFI decoder supports larger than (1 << 19) dictionary.
1048               We check both LZH versions: Tiano and then Efi.
1049             */
1050             HRESULT res = S_FALSE;
1051             for (unsigned m = 0 ; m < 2; m++)
1052             {
1053               inStream->Init(src, packSize);
1054               outStream->Init(buf, uncompressedSize);
1055               lzhDecoder->SetDictSize(m == 0 ? ((UInt32)1 << 19) : ((UInt32)1 << 14));
1056               res = lzhDecoder->Code(inStream, outStream, uncompressedSize, NULL);
1057               if (res == S_OK)
1058                 break;
1059             }
1060             RINOK(res)
1061           }
1062           bool error2;
1063           RINOK(ParseSections(newBufIndex, 0, uncompressedSize, parent, compressionType, level, error2))
1064         }
1065         else
1066         {
1067           if (newSectSize < 4 + 5 + 8)
1068             return S_FALSE;
1069           unsigned addSize = 4;
1070           if (pStart[0] == 0x5d && pStart[1] == 0 && pStart[2] == 0 && pStart[3] == 0x80 && pStart[4] == 0)
1071           {
1072             addSize = 0;
1073             // some archives have such header
1074           }
1075           else
1076           {
1077             // normal BIOS contains uncompressed size here
1078             // UInt32 uncompressedSize2 = Get24(pStart);
1079             // Byte firstSectType = p[9 + 3];
1080             // firstSectType can be 0 in some archives
1081           }
1082           pStart += addSize;
1083 
1084           RINOK(DecodeLzma(pStart, newSectSize - addSize))
1085           const size_t lzmaUncompressedSize = _bufs.Back().Size();
1086           // if (lzmaUncompressedSize != uncompressedSize)
1087           if (lzmaUncompressedSize < uncompressedSize)
1088             return S_FALSE;
1089           bool error2;
1090           RINOK(ParseSections(_bufs.Size() - 1, 0, (UInt32)lzmaUncompressedSize, parent, compressionType, level, error2))
1091         }
1092         _methodsMask |= (1 << compressionType);
1093       }
1094     }
1095     else if (type == SECTION_GUID_DEFINED)
1096     {
1097       const unsigned kHeaderSize = 4 + kGuidSize + 4;
1098       if (sectSize < kHeaderSize)
1099         return S_FALSE;
1100       item.SetGuid(p + 4);
1101       const UInt32 dataOffset = Get16(p + 4 + kGuidSize);
1102       const UInt32 attrib = Get16(p + 4 + kGuidSize + 2);
1103       if (dataOffset > sectSize || dataOffset < kHeaderSize)
1104         return S_FALSE;
1105       UInt32 newSectSize = sectSize - dataOffset;
1106       item.Size = newSectSize;
1107       UInt32 newOffset = posBase + pos + dataOffset;
1108       item.Offset = newOffset;
1109       const UInt32 propsSize = dataOffset - kHeaderSize;
1110       AddSpaceAndString(item.Characts, FLAGS_TO_STRING(g_GUIDED_SECTION_ATTRIBUTES, attrib));
1111 
1112       bool needDir = true;
1113       unsigned newBufIndex = bufIndex;
1114       int newMethod = method;
1115 
1116       if (AreGuidsEq(p + 0x4, k_Guid_LZMA_COMPRESSED))
1117       {
1118         // item.Name = "guid.lzma";
1119         // AddItem(item);
1120         const Byte *pStart = bufData + newOffset;
1121         // do we need correct pStart here for lzma steram offset?
1122         RINOK(DecodeLzma(pStart, newSectSize))
1123         _methodsMask |= (1 << COMPRESSION_TYPE_LZMA);
1124         newBufIndex = _bufs.Size() - 1;
1125         newOffset = 0;
1126         newSectSize = (UInt32)_bufs.Back().Size();
1127         newMethod = COMPRESSION_TYPE_LZMA;
1128       }
1129       else if (AreGuidsEq(p + 0x4, kGuids[kGuidIndex_CRC]) && propsSize == 4)
1130       {
1131         needDir = false;
1132         item.KeepName = false;
1133         if (CrcCalc(bufData + newOffset, newSectSize) != Get32(p + kHeaderSize))
1134           return S_FALSE;
1135       }
1136       else
1137       {
1138         if (propsSize != 0)
1139         {
1140           CItem item2 = item;
1141           item2.Name += ".prop";
1142           item2.Size = propsSize;
1143           item2.Offset = posBase + pos + kHeaderSize;
1144           AddItem(item2);
1145         }
1146       }
1147 
1148       int newParent = parent;
1149       if (needDir)
1150         newParent = (int)AddDirItem(item);
1151       bool error2;
1152       RINOK(ParseSections(newBufIndex, newOffset, newSectSize, newParent, newMethod, level, error2))
1153     }
1154     else if (type == SECTION_FIRMWARE_VOLUME_IMAGE)
1155     {
1156       item.KeepName = false;
1157       const int newParent = (int)AddDirItem(item);
1158       RINOK(ParseVolume(bufIndex, posBase + pos + 4,
1159           sectSize - 4,
1160           sectSize - 4,
1161           newParent, method, level))
1162     }
1163     else
1164     {
1165       bool needAdd = true;
1166       switch (type)
1167       {
1168         case SECTION_RAW:
1169         {
1170           const UInt32 kInsydeOffset = 12;
1171           if (sectDataSize >= kFvHeaderSize + kInsydeOffset)
1172           {
1173             if (IsFfs(p + 4 + kInsydeOffset) &&
1174                 sectDataSize - kInsydeOffset == Get64(p + 4 + kInsydeOffset + 0x20))
1175             {
1176               needAdd = false;
1177               item.Name = "vol";
1178               const unsigned newParent = AddDirItem(item);
1179               RINOK(ParseVolume(bufIndex, posBase + pos + 4 + kInsydeOffset,
1180                   sectDataSize - kInsydeOffset,
1181                   sectDataSize - kInsydeOffset,
1182                   (int)newParent, method, level))
1183             }
1184 
1185             if (needAdd)
1186             {
1187               const char *ext = FindExt(p + 4, sectDataSize);
1188               if (ext)
1189                 item.Name = ext;
1190             }
1191           }
1192           break;
1193         }
1194         case SECTION_DXE_DEPEX:
1195         case SECTION_PEI_DEPEX:
1196         {
1197           AString s;
1198           if (ParseDepedencyExpression(p + 4, sectDataSize, s))
1199           {
1200             if (s.Len() < (1 << 9))
1201             {
1202               s.InsertAtFront('[');
1203               s += ']';
1204               AddSpaceAndString(_items[item.Parent].Characts, s);
1205               needAdd = false;
1206             }
1207             else
1208             {
1209               item.BufIndex = AddBuf(s.Len());
1210               CByteBuffer &buf0 = _bufs[item.BufIndex];
1211               if (s.Len() != 0)
1212                 memcpy(buf0, s, s.Len());
1213               item.Offset = 0;
1214               item.Size = s.Len();
1215             }
1216           }
1217           break;
1218         }
1219         case SECTION_VERSION:
1220         {
1221           if (sectDataSize > 2)
1222           {
1223             AString s;
1224             if (ParseUtf16zString2(p + 6, sectDataSize - 2, s))
1225             {
1226               AString s2 ("ver:");
1227               s2.Add_UInt32(Get16(p + 4));
1228               s2.Add_Space();
1229               s2 += s;
1230               AddSpaceAndString(_items[item.Parent].Characts, s2);
1231               needAdd = false;
1232             }
1233           }
1234           break;
1235         }
1236         case SECTION_USER_INTERFACE:
1237         {
1238           AString s;
1239           if (ParseUtf16zString2(p + 4, sectDataSize, s))
1240           {
1241             _items[parent].Name = s;
1242             needAdd = false;
1243           }
1244           break;
1245         }
1246         case SECTION_FREEFORM_SUBTYPE_GUID:
1247         {
1248           if (sectDataSize >= kGuidSize)
1249           {
1250             item.SetGuid(p + 4);
1251             item.Size = sectDataSize - kGuidSize;
1252             item.Offset = posBase + pos + 4 + kGuidSize;
1253           }
1254           break;
1255         }
1256       }
1257 
1258       if (needAdd)
1259         AddFileItemWithIndex(item);
1260     }
1261 
1262     pos += sectSize;
1263   }
1264 }
1265 
1266 static UInt32 Count_FF_Bytes(const Byte *p, UInt32 size)
1267 {
1268   UInt32 i;
1269   for (i = 0; i < size && p[i] == 0xFF; i++);
1270   return i;
1271 }
1272 
1273 static bool Is_FF_Stream(const Byte *p, UInt32 size)
1274 {
1275   return (Count_FF_Bytes(p, size) == size);
1276 }
1277 
1278 struct CVolFfsHeader
1279 {
1280   UInt32 HeaderLen;
1281   UInt64 VolSize;
1282 
1283   bool Parse(const Byte *p);
1284 };
1285 
1286 bool CVolFfsHeader::Parse(const Byte *p)
1287 {
1288   if (Get32(p + 0x28) != kFvSignature)
1289     return false;
1290 
1291   UInt32 attribs = Get32(p + 0x2C);
1292   if ((attribs & FVB_ERASE_POLARITY) == 0)
1293     return false;
1294   VolSize = Get64(p + 0x20);
1295   HeaderLen = Get16(p + 0x30);
1296   if (HeaderLen < kFvHeaderSize || (HeaderLen & 0x7) != 0 || VolSize < HeaderLen)
1297     return false;
1298   return true;
1299 }
1300 
1301 
1302 HRESULT CHandler::ParseVolume(
1303     unsigned bufIndex, UInt32 posBase,
1304     UInt32 exactSize, UInt32 limitSize,
1305     int parent, int method, unsigned level)
1306 {
1307   if (level > kLevelMax)
1308     return S_FALSE;
1309   MyPrint(posBase, exactSize, level, "Volume");
1310   level++;
1311   if (exactSize < kFvHeaderSize)
1312     return S_FALSE;
1313   const Byte *p = _bufs[bufIndex] + posBase;
1314   // first 16 bytes must be zeros, but they are not zeros sometimes.
1315   if (!IsFfs(p))
1316   {
1317     CItem item;
1318     item.Method = method;
1319     item.BufIndex = bufIndex;
1320     item.Parent = parent;
1321     item.Offset = posBase;
1322     item.Size = exactSize;
1323     if (!Is_FF_Stream(p + kFfsGuidOffset, 16))
1324       item.SetGuid(p + kFfsGuidOffset);
1325     // if (item.Name.IsEmpty())
1326     item.Name += "[VOL]";
1327     AddItem(item);
1328     return S_OK;
1329   }
1330 
1331   CVolFfsHeader ffsHeader;
1332   if (!ffsHeader.Parse(p))
1333     return S_FALSE;
1334   // if (parent >= 0) AddSpaceAndString(_items[parent].Characts, FLAGS_TO_STRING(g_FV_Attribs, attribs));
1335 
1336   // VolSize > exactSize (fh.Size) for some UEFI archives (is it correct UEFI?)
1337   // so we check VolSize for limitSize instead.
1338 
1339   if (ffsHeader.HeaderLen > limitSize || ffsHeader.VolSize > limitSize)
1340     return S_FALSE;
1341 
1342   {
1343     UInt32 checkCalc = 0;
1344     for (UInt32 i = 0; i < ffsHeader.HeaderLen; i += 2)
1345       checkCalc += Get16(p + i);
1346     if ((checkCalc & 0xFFFF) != 0)
1347       return S_FALSE;
1348   }
1349 
1350   // 3 reserved bytes are not zeros sometimes.
1351   // UInt16 ExtHeaderOffset; // in new SPECIFICATION?
1352   // Byte revision = p[0x37];
1353 
1354   UInt32 pos = kFvHeaderSize;
1355   for (;;)
1356   {
1357     if (pos >= ffsHeader.HeaderLen)
1358       return S_FALSE;
1359     UInt32 numBlocks = Get32(p + pos);
1360     UInt32 length = Get32(p + pos + 4);
1361     pos += 8;
1362     if (numBlocks == 0 && length == 0)
1363       break;
1364   }
1365   if (pos != ffsHeader.HeaderLen)
1366     return S_FALSE;
1367 
1368   CRecordVector<UInt32> guidsVector;
1369 
1370   for (;;)
1371   {
1372     UInt32 rem = (UInt32)ffsHeader.VolSize - pos;
1373     if (rem < kFileHeaderSize)
1374       break;
1375     pos = (pos + 7) & ~7u;
1376     rem = (UInt32)ffsHeader.VolSize - pos;
1377     if (rem < kFileHeaderSize)
1378       break;
1379 
1380     CItem item;
1381     item.Method = method;
1382     item.BufIndex = bufIndex;
1383     item.Parent = parent;
1384 
1385     const Byte *pFile = p + pos;
1386     CFfsFileHeader fh;
1387     if (!fh.Parse(pFile))
1388     {
1389       UInt32 num_FF_bytes = Count_FF_Bytes(pFile, rem);
1390       if (num_FF_bytes != rem)
1391       {
1392         item.Name = "[junk]";
1393         item.Offset = posBase + pos + num_FF_bytes;
1394         item.Size = rem - num_FF_bytes;
1395         AddItem(item);
1396       }
1397       PrintLevel(level); PRF(printf("== FF FF reminder"));
1398 
1399       break;
1400     }
1401 
1402     PrintLevel(level); PRF(printf("%s, type = %3d,  pos = %7x, size = %7x", "FILE", fh.Type, posBase + pos, fh.Size));
1403 
1404     if (!fh.Check(pFile, rem))
1405       return S_FALSE;
1406 
1407     UInt32 offset = posBase + pos + kFileHeaderSize;
1408     UInt32 sectSize = fh.GetDataSize();
1409     item.Offset = offset;
1410     item.Size = sectSize;
1411 
1412     pos += fh.Size;
1413 
1414     if (fh.Type == FV_FILETYPE_FFS_PAD)
1415       if (Is_FF_Stream(pFile + kFileHeaderSize, sectSize))
1416         continue;
1417 
1418     UInt32 guid32 = Get32(fh.GuidName);
1419     bool full = true;
1420     if (guidsVector.FindInSorted(guid32) < 0)
1421     {
1422       guidsVector.AddToUniqueSorted(guid32);
1423       full = false;
1424     }
1425     item.SetGuid(fh.GuidName, full);
1426 
1427     item.Characts = fh.GetCharacts();
1428     // PrintLevel(level);
1429     PRF(printf(" : %s", item.Characts));
1430 
1431     {
1432       PRF(printf(" attrib = %2d State = %3d ", (unsigned)fh.Attrib, (unsigned)fh.State));
1433       PRF(char s[64]);
1434       PRF(RawLeGuidToString(fh.GuidName, s));
1435       PRF(printf(" : %s ", s));
1436     }
1437 
1438     if (fh.Type == FV_FILETYPE_FFS_PAD ||
1439         fh.Type == FV_FILETYPE_RAW)
1440     {
1441       bool isVolume = false;
1442       if (fh.Type == FV_FILETYPE_RAW)
1443       {
1444         if (sectSize >= kFvHeaderSize)
1445           if (IsFfs(pFile + kFileHeaderSize))
1446             isVolume = true;
1447       }
1448       if (isVolume)
1449       {
1450         const unsigned newParent = AddDirItem(item);
1451         const UInt32 limSize = fh.GetDataSize2(rem);
1452         // volume.VolSize > fh.Size for some UEFI archives (is it correct UEFI?)
1453         // so we will check VolSize for limitSize instead.
1454         RINOK(ParseVolume(bufIndex, offset, sectSize, limSize, (int)newParent, method, level))
1455       }
1456       else
1457         AddItem(item);
1458     }
1459     else
1460     {
1461       /*
1462       if (fh.Type == FV_FILETYPE_FREEFORM)
1463       {
1464         // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1465         // AddItem(item);
1466       }
1467       else
1468       */
1469       {
1470         const unsigned newParent = AddDirItem(item);
1471         bool error2;
1472         RINOK(ParseSections(bufIndex, offset, sectSize, (int)newParent, method, level + 1, error2))
1473         if (error2)
1474         {
1475           // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1476           item.IsDir = false;
1477           item.Size = sectSize;
1478           item.Name.Insert(0, "[ERROR]");
1479           AddItem(item);
1480         }
1481       }
1482     }
1483   }
1484 
1485   return S_OK;
1486 }
1487 
1488 
1489 static const char * const kRegionName[] =
1490 {
1491     "Descriptor"
1492   , "BIOS"
1493   , "ME"
1494   , "GbE"
1495   , "PDR"
1496   , "Region5"
1497   , "Region6"
1498   , "Region7"
1499 };
1500 
1501 
1502 HRESULT CHandler::ParseIntelMe(
1503     unsigned bufIndex, UInt32 posBase,
1504     UInt32 exactSize, UInt32 limitSize,
1505     int parent, int method, unsigned /* level */)
1506 {
1507   UNUSED_VAR(limitSize)
1508   // level++;
1509 
1510   const Byte *p = _bufs[bufIndex] + posBase;
1511   if (exactSize < 16 + 16)
1512     return S_FALSE;
1513   if (!IsIntelMe(p))
1514     return S_FALSE;
1515 
1516   UInt32 v0 = GetUi32(p + 20);
1517   // UInt32 numRegions = (v0 >> 24) & 0x7;
1518   UInt32 regAddr = (v0 >> 12) & 0xFF0;
1519   // UInt32 numComps  = (v0 >> 8) & 0x3;
1520   // UInt32 fcba = (v0 <<  4) & 0xFF0;
1521 
1522   // (numRegions == 0) in header in some new images.
1523   // So we don't use the value from header
1524   UInt32 numRegions = 7;
1525 
1526   for (unsigned i = 0; i <= numRegions; i++)
1527   {
1528     UInt32 offset = regAddr + i * 4;
1529     if (offset + 4 > exactSize)
1530       break;
1531     UInt32 val = GetUi32(p + offset);
1532 
1533     // only 12 bits probably are OK.
1534     // How does it work for files larger than 16 MB?
1535     const UInt32 kMask = 0xFFF;
1536     // const UInt32 kMask = 0xFFFF; // 16-bit is more logical
1537     const UInt32 lim  = (val >> 16) & kMask;
1538     const UInt32 base = (val & kMask);
1539 
1540     /*
1541       strange combinations:
1542         PDR:   base = 0x1FFF lim = 0;
1543         empty: base = 0xFFFF lim = 0xFFFF;
1544 
1545         PDR:   base = 0x7FFF lim = 0;
1546         empty: base = 0x7FFF lim = 0;
1547     */
1548     if (base == kMask && lim == 0)
1549       continue; // unused
1550 
1551     if (lim < base)
1552       continue; // unused
1553 
1554     CItem item;
1555     item.Name = kRegionName[i];
1556     item.Method = method;
1557     item.BufIndex = bufIndex;
1558     item.Parent = parent;
1559     item.Offset = posBase + (base << 12);
1560     if (item.Offset > exactSize)
1561       continue;
1562     item.Size = (lim + 1 - base) << 12;
1563     // item.SetGuid(p + kFfsGuidOffset);
1564     // item.Name += " [VOLUME]";
1565     AddItem(item);
1566   }
1567   return S_OK;
1568 }
1569 
1570 
1571 HRESULT CHandler::OpenCapsule(IInStream *stream)
1572 {
1573   const unsigned kHeaderSize = 80;
1574   Byte buf[kHeaderSize];
1575   RINOK(ReadStream_FALSE(stream, buf, kHeaderSize))
1576   if (!_h.Parse(buf))
1577     return S_FALSE;
1578   if (_h.CapsuleImageSize < kHeaderSize
1579        || _h.CapsuleImageSize < _h.HeaderSize
1580        || _h.OffsetToCapsuleBody < _h.HeaderSize
1581        || _h.OffsetToCapsuleBody > _h.CapsuleImageSize
1582        || _h.CapsuleImageSize > (1u << 30) // to reduce false detection
1583        || _h.HeaderSize > (1u << 28) // to reduce false detection
1584       )
1585     return S_FALSE;
1586   _phySize = _h.CapsuleImageSize;
1587 
1588   if (_h.SequenceNumber != 0 ||
1589       _h.OffsetToSplitInformation != 0 )
1590     return E_NOTIMPL;
1591 
1592   const unsigned bufIndex = AddBuf(_h.CapsuleImageSize);
1593   CByteBuffer &buf0 = _bufs[bufIndex];
1594   memcpy(buf0, buf, kHeaderSize);
1595   ReadStream_FALSE(stream, buf0 + kHeaderSize, _h.CapsuleImageSize - kHeaderSize);
1596 
1597   AddCommentString("Author", _h.OffsetToAuthorInformation);
1598   AddCommentString("Revision", _h.OffsetToRevisionInformation);
1599   AddCommentString("Short Description", _h.OffsetToShortDescription);
1600   AddCommentString("Long Description", _h.OffsetToLongDescription);
1601 
1602 
1603   const UInt32 size = _h.CapsuleImageSize - _h.OffsetToCapsuleBody;
1604 
1605   if (size >= 32 && IsIntelMe(buf0 + _h.OffsetToCapsuleBody))
1606     return ParseIntelMe(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1607 
1608   return ParseVolume(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1609 }
1610 
1611 
1612 HRESULT CHandler::OpenFv(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback * /* callback */)
1613 {
1614   Byte buf[kFvHeaderSize];
1615   RINOK(ReadStream_FALSE(stream, buf, kFvHeaderSize))
1616   if (!IsFfs(buf))
1617     return S_FALSE;
1618   CVolFfsHeader ffsHeader;
1619   if (!ffsHeader.Parse(buf))
1620     return S_FALSE;
1621   if (ffsHeader.VolSize > ((UInt32)1 << 30))
1622     return S_FALSE;
1623   _phySize = ffsHeader.VolSize;
1624   RINOK(InStream_SeekToBegin(stream))
1625   UInt32 fvSize32 = (UInt32)ffsHeader.VolSize;
1626   unsigned bufIndex = AddBuf(fvSize32);
1627   RINOK(ReadStream_FALSE(stream, _bufs[bufIndex], fvSize32))
1628   return ParseVolume(bufIndex, 0, fvSize32, fvSize32, -1, -1, 0);
1629 }
1630 
1631 
1632 HRESULT CHandler::Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
1633 {
1634   if (_capsuleMode)
1635   {
1636     RINOK(OpenCapsule(stream))
1637   }
1638   else
1639   {
1640     RINOK(OpenFv(stream, maxCheckStartPosition, callback))
1641   }
1642 
1643   const unsigned num = _items.Size();
1644   CIntArr numChilds(num);
1645 
1646   unsigned i;
1647 
1648   for (i = 0; i < num; i++)
1649     numChilds[i] = 0;
1650 
1651   for (i = 0; i < num; i++)
1652   {
1653     const int parent = _items[i].Parent;
1654     if (parent >= 0)
1655       numChilds[(unsigned)parent]++;
1656   }
1657 
1658   for (i = 0; i < num; i++)
1659   {
1660     const CItem &item = _items[i];
1661     const int parent = item.Parent;
1662     if (parent >= 0)
1663     {
1664       CItem &parentItem = _items[(unsigned)parent];
1665       if (numChilds[(unsigned)parent] == 1)
1666         if (!item.ThereIsUniqueName || !parentItem.ThereIsUniqueName || !parentItem.ThereAreSubDirs)
1667           parentItem.Skip = true;
1668     }
1669   }
1670 
1671   CUIntVector mainToReduced;
1672 
1673   for (i = 0; i < _items.Size(); i++)
1674   {
1675     mainToReduced.Add(_items2.Size());
1676     const CItem &item = _items[i];
1677     if (item.Skip)
1678       continue;
1679     AString name;
1680     int numItems = -1;
1681     int parent = item.Parent;
1682     if (parent >= 0)
1683       numItems = numChilds[(unsigned)parent];
1684     AString name2 (item.GetName(numItems));
1685     AString characts2 (item.Characts);
1686     if (item.KeepName)
1687       name = name2;
1688 
1689     while (parent >= 0)
1690     {
1691       const CItem &item3 = _items[(unsigned)parent];
1692       if (!item3.Skip)
1693         break;
1694       if (item3.KeepName)
1695       {
1696         AString name3 (item3.GetName(-1));
1697         if (name.IsEmpty())
1698           name = name3;
1699         else
1700           name = name3 + '.' + name;
1701       }
1702       AddSpaceAndString(characts2, item3.Characts);
1703       parent = item3.Parent;
1704     }
1705 
1706     if (name.IsEmpty())
1707       name = name2;
1708 
1709     CItem2 item2;
1710     item2.MainIndex = i;
1711     item2.Name = name;
1712     item2.Characts = characts2;
1713     if (parent >= 0)
1714       item2.Parent = (int)mainToReduced[(unsigned)parent];
1715     _items2.Add(item2);
1716     /*
1717     CItem2 item2;
1718     item2.MainIndex = i;
1719     item2.Name = item.Name;
1720     item2.Parent = item.Parent;
1721     _items2.Add(item2);
1722     */
1723   }
1724 
1725   return S_OK;
1726 }
1727 
1728 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
1729     const UInt64 *maxCheckStartPosition,
1730     IArchiveOpenCallback *callback))
1731 {
1732   COM_TRY_BEGIN
1733   Close();
1734   {
1735     HRESULT res = Open2(inStream, maxCheckStartPosition, callback);
1736     if (res == E_NOTIMPL)
1737       res = S_FALSE;
1738     return res;
1739   }
1740   COM_TRY_END
1741 }
1742 
1743 Z7_COM7F_IMF(CHandler::Close())
1744 {
1745   _phySize = 0;
1746   _totalBufsSize = 0;
1747   _methodsMask = 0;
1748   _items.Clear();
1749   _items2.Clear();
1750   _bufs.Clear();
1751   _comment.Empty();
1752   _headersError = false;
1753   _h.Clear();
1754   return S_OK;
1755 }
1756 
1757 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1758 {
1759   *numItems = _items2.Size();
1760   return S_OK;
1761 }
1762 
1763 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1764     Int32 testMode, IArchiveExtractCallback *extractCallback))
1765 {
1766   COM_TRY_BEGIN
1767   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1768   if (allFilesMode)
1769     numItems = _items2.Size();
1770   if (numItems == 0)
1771     return S_OK;
1772   UInt64 totalSize = 0;
1773   UInt32 i;
1774   for (i = 0; i < numItems; i++)
1775     totalSize += _items[_items2[allFilesMode ? i : indices[i]].MainIndex].Size;
1776   RINOK(extractCallback->SetTotal(totalSize))
1777   totalSize = 0;
1778 
1779   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1780   lps->Init(extractCallback, false);
1781   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1782 
1783   for (i = 0;; i++)
1784   {
1785     lps->InSize = lps->OutSize = totalSize;
1786     RINOK(lps->SetCur())
1787     if (i >= numItems)
1788       break;
1789     Int32 opRes;
1790     {
1791       CMyComPtr<ISequentialOutStream> realOutStream;
1792       const Int32 askMode = testMode ?
1793           NExtract::NAskMode::kTest :
1794           NExtract::NAskMode::kExtract;
1795       const UInt32 index = allFilesMode ? i : indices[i];
1796       const CItem &item = _items[_items2[index].MainIndex];
1797       RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1798       totalSize += item.Size;
1799       if (!testMode && !realOutStream)
1800         continue;
1801       RINOK(extractCallback->PrepareOperation(askMode))
1802       if (testMode || item.IsDir)
1803         opRes = NExtract::NOperationResult::kOK;
1804       else
1805       {
1806         opRes = NExtract::NOperationResult::kDataError;
1807         CMyComPtr<ISequentialInStream> inStream;
1808         GetStream(index, &inStream);
1809         if (inStream)
1810         {
1811           RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps))
1812           if (copyCoder->TotalSize == item.Size)
1813             opRes = NExtract::NOperationResult::kOK;
1814         }
1815       }
1816     }
1817     RINOK(extractCallback->SetOperationResult(opRes))
1818   }
1819   return S_OK;
1820   COM_TRY_END
1821 }
1822 
1823 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
1824 {
1825   COM_TRY_BEGIN
1826   const CItem &item = _items[_items2[index].MainIndex];
1827   if (item.IsDir)
1828     return S_FALSE;
1829   CBufInStream *streamSpec = new CBufInStream;
1830   CMyComPtr<IInStream> streamTemp = streamSpec;
1831   const CByteBuffer &buf = _bufs[item.BufIndex];
1832   if (item.Offset > buf.Size())
1833     return S_FALSE;
1834   size_t size = buf.Size() - item.Offset;
1835   if (size > item.Size)
1836     size = item.Size;
1837   streamSpec->Init(buf + item.Offset, size, (IInArchive *)this);
1838   *stream = streamTemp.Detach();
1839   return S_OK;
1840   COM_TRY_END
1841 }
1842 
1843 
1844 namespace UEFIc {
1845 
1846 static const Byte k_Capsule_Signatures[] =
1847 {
1848    16, CAPSULE_SIGNATURE,
1849    16, CAPSULE2_SIGNATURE,
1850    16, CAPSULE_UEFI_SIGNATURE
1851 };
1852 
1853 REGISTER_ARC_I_CLS(
1854   CHandler(true),
1855   "UEFIc", "scap", NULL, 0xD0,
1856   k_Capsule_Signatures,
1857   0,
1858   NArcInfoFlags::kMultiSignature |
1859   NArcInfoFlags::kFindSignature,
1860   NULL)
1861 
1862 }
1863 
1864 namespace UEFIf {
1865 
1866 static const Byte k_FFS_Signatures[] =
1867 {
1868    16, FFS1_SIGNATURE,
1869    16, FFS2_SIGNATURE
1870 };
1871 
1872 
1873 REGISTER_ARC_I_CLS(
1874   CHandler(false),
1875   "UEFIf", "uefif", NULL, 0xD1,
1876   k_FFS_Signatures,
1877   kFfsGuidOffset,
1878   NArcInfoFlags::kMultiSignature |
1879   NArcInfoFlags::kFindSignature,
1880   NULL)
1881 
1882 }
1883 
1884 }}
1885