xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/ApmHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ApmHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 
9 #include "../../Windows/PropVariantUtils.h"
10 
11 #include "../Common/RegisterArc.h"
12 #include "../Common/StreamUtils.h"
13 
14 #include "HandlerCont.h"
15 
16 #define Get32(p) GetBe32a(p)
17 
18 using namespace NWindows;
19 
20 namespace NArchive {
21 
22 namespace NDmg {
23   const char *Find_Apple_FS_Ext(const AString &name);
24   bool Is_Apple_FS_Or_Unknown(const AString &name);
25 }
26 
27 namespace NApm {
28 
29 static const Byte kSig0 = 'E';
30 static const Byte kSig1 = 'R';
31 
32 static const CUInt32PCharPair k_Flags[] =
33 {
34   { 0, "VALID" },
35   { 1, "ALLOCATED" },
36   { 2, "IN_USE" },
37   { 3, "BOOTABLE" },
38   { 4, "READABLE" },
39   { 5, "WRITABLE" },
40   { 6, "OS_PIC_CODE" },
41   // { 7, "OS_SPECIFIC_2" }, // "Unused"
42   { 8, "ChainCompatible" }, // "OS_SPECIFIC_1"
43   { 9, "RealDeviceDriver" },
44   // { 10, "CanChainToNext" },
45   { 30, "MOUNTED_AT_STARTUP" },
46   { 31, "STARTUP" }
47 };
48 
49 #define DPME_FLAGS_VALID      (1u << 0)
50 #define DPME_FLAGS_ALLOCATED  (1u << 1)
51 
52 static const unsigned k_Str_Size = 32;
53 
54 struct CItem
55 {
56   UInt32 StartBlock;
57   UInt32 NumBlocks;
58   UInt32 Flags; // pmPartStatus
59   char Name[k_Str_Size];
60   char Type[k_Str_Size];
61   /*
62   UInt32 DataStartBlock;
63   UInt32 NumDataBlocks;
64   UInt32 BootStartBlock;
65   UInt32 BootSize;
66   UInt32 BootAddr;
67   UInt32 BootEntry;
68   UInt32 BootChecksum;
69   char Processor[16];
70   */
71 
Is_Valid_and_AllocatedNArchive::NApm::CItem72   bool Is_Valid_and_Allocated() const
73     { return (Flags & (DPME_FLAGS_VALID | DPME_FLAGS_ALLOCATED)) != 0; }
74 
ParseNArchive::NApm::CItem75   bool Parse(const UInt32 *p32, UInt32 &numBlocksInMap)
76   {
77     if (GetUi32a(p32) != 0x4d50) // "PM"
78       return false;
79     numBlocksInMap = Get32(p32 + 4 / 4);
80     StartBlock = Get32(p32 + 8 / 4);
81     NumBlocks = Get32(p32 + 0xc / 4);
82     Flags = Get32(p32 + 0x58 / 4);
83     memcpy(Name, p32 + 0x10 / 4, k_Str_Size);
84     memcpy(Type, p32 + 0x30 / 4, k_Str_Size);
85     /*
86     DataStartBlock = Get32(p + 0x50);
87     NumDataBlocks = Get32(p + 0x54);
88     BootStartBlock = Get32(p + 0x5c);
89     BootSize = Get32(p + 0x60);
90     BootAddr = Get32(p + 0x64);
91     if (Get32(p + 0x68) != 0)
92       return false;
93     BootEntry = Get32(p + 0x6c);
94     if (Get32(p + 0x70) != 0)
95       return false;
96     BootChecksum = Get32(p + 0x74);
97     memcpy(Processor, p32 + 0x78 / 4, 16);
98     */
99     return true;
100   }
101 };
102 
103 
104 Z7_class_CHandler_final: public CHandlerCont
105 {
106   Z7_IFACE_COM7_IMP(IInArchive_Cont)
107 
108   CRecordVector<CItem> _items;
109   unsigned _blockSizeLog;
110   bool _isArc;
111   // UInt32 _numBlocks;
112   UInt64 _phySize;
113 
114   UInt64 BlocksToBytes(UInt32 i) const { return (UInt64)i << _blockSizeLog; }
115 
116   virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override
117   {
118     const CItem &item = _items[index];
119     pos = BlocksToBytes(item.StartBlock);
120     size = BlocksToBytes(item.NumBlocks);
121     return NExtract::NOperationResult::kOK;
122   }
123 };
124 
125 static const UInt32 kSectorSize = 512;
126 
127 // we support only 4 cluster sizes: 512, 1024, 2048, 4096 */
128 
129 API_FUNC_static_IsArc IsArc_Apm(const Byte *p, size_t size)
130 {
131   if (size < kSectorSize)
132     return k_IsArc_Res_NEED_MORE;
133   if (GetUi32(p + 12) != 0)
134     return k_IsArc_Res_NO;
135   UInt32 v = GetUi32(p); // we read as little-endian
136   v ^= kSig0 | (unsigned)kSig1 << 8;
137   if (v & ~((UInt32)0xf << 17))
138     return k_IsArc_Res_NO;
139   if ((0x116u >> (v >> 17)) & 1)
140     return k_IsArc_Res_YES;
141   return k_IsArc_Res_NO;
142 }
143 }
144 
145 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */))
146 {
147   COM_TRY_BEGIN
148   Close();
149 
150   UInt32 buf32[kSectorSize / 4];
151   unsigned numPadSectors, blockSizeLog_from_Header;
152   {
153     // Driver Descriptor Map (DDM)
154     RINOK(ReadStream_FALSE(stream, buf32, kSectorSize))
155     //  8: UInt16 sbDevType : =0 (usually), =1 in Apple Mac OS X 10.3.0 iso
156     // 10: UInt16 sbDevId   : =0 (usually), =1 in Apple Mac OS X 10.3.0 iso
157     // 12: UInt32 sbData    : =0
158     if (buf32[3] != 0)
159       return S_FALSE;
160     UInt32 v = GetUi32a(buf32); // we read as little-endian
161     v ^= kSig0 | (unsigned)kSig1 << 8;
162     if (v & ~((UInt32)0xf << 17))
163       return S_FALSE;
164     v >>= 16;
165     if (v == 0)
166       return S_FALSE;
167     if (v & (v - 1))
168       return S_FALSE;
169     // v == { 16,8,4,2 } : block size (x256 bytes)
170     const unsigned a =
171 #if 1
172         (0x30210u >> v) & 3;
173 #else
174         0; // for debug : hardcoded switch to 512-bytes mode
175 #endif
176     numPadSectors = (1u << a) - 1;
177     _blockSizeLog = blockSizeLog_from_Header = 9 + a;
178   }
179 
180 /*
181   some APMs (that are ".iso" macOS installation files) contain
182     (blockSizeLog == 11) in DDM header,
183   and contain 2 overlapping maps:
184     1) map for  512-bytes-step
185     2) map for 2048-bytes-step
186    512-bytes-step map is correct.
187   2048-bytes-step map can be incorrect in some cases.
188 
189   macos 8 / OSX DP2 iso:
190     There is shared "hfs" item in both maps.
191     And correct (offset/size) values for "hfs" partition
192     can be calculated only in 512-bytes mode (ignoring blockSizeLog == 11).
193     But some records (Macintosh.Apple_Driver*_)
194     can be correct on both modes: 512-bytes mode / 2048-bytes-step.
195 
196   macos 921 ppc / Apple Mac OS X 10.3.0 iso:
197     Both maps are correct.
198     If we use 512-bytes-step, each 4th item is (Apple_Void) with zero size.
199     And these zero size (Apple_Void) items will be first items in 2048-bytes-step map.
200 */
201 
202 // we define Z7_APM_SWITCH_TO_512_BYTES, because
203 // we want to support old MACOS APMs that contain correct value only
204 // for 512-bytes-step mode
205 #define Z7_APM_SWITCH_TO_512_BYTES
206 
207   const UInt32 numBlocks_from_Header = Get32(buf32 + 1);
208   UInt32 numBlocks = 0;
209   {
210     for (unsigned k = 0; k < numPadSectors; k++)
211     {
212       RINOK(ReadStream_FALSE(stream, buf32, kSectorSize))
213 #ifdef Z7_APM_SWITCH_TO_512_BYTES
214       if (k == 0)
215       {
216         if (GetUi32a(buf32) == 0x4d50        // "PM"
217             // && (Get32(buf32 + 0x58 / 4) & 1) // Flags::VALID
218             // some old APMs don't use VALID flag for Apple_partition_map item
219             && Get32(buf32 + 8 / 4) == 1)    // StartBlock
220         {
221           // we switch the mode to 512-bytes-step map reading:
222           numPadSectors = 0;
223           _blockSizeLog = 9;
224           break;
225         }
226       }
227 #endif
228     }
229   }
230 
231   for (unsigned i = 0;;)
232   {
233 #ifdef Z7_APM_SWITCH_TO_512_BYTES
234     if (i != 0 || _blockSizeLog == blockSizeLog_from_Header)
235 #endif
236     {
237       RINOK(ReadStream_FALSE(stream, buf32, kSectorSize))
238     }
239 
240     CItem item;
241     UInt32 numBlocksInMap = 0;
242     if (!item.Parse(buf32, numBlocksInMap))
243       return S_FALSE;
244     // v24.09: we don't check that all entries have same (numBlocksInMap) values,
245     // because some APMs have different (numBlocksInMap) values, if (Apple_Void) is used.
246     if (numBlocksInMap > (1 << 8) || numBlocksInMap <= i)
247       return S_FALSE;
248 
249     const UInt32 finish = item.StartBlock + item.NumBlocks;
250     if (finish < item.StartBlock)
251       return S_FALSE;
252     if (numBlocks < finish)
253         numBlocks = finish;
254 
255     _items.Add(item);
256     if (numPadSectors != 0)
257     {
258       RINOK(stream->Seek(numPadSectors << 9, STREAM_SEEK_CUR, NULL))
259     }
260     if (++i == numBlocksInMap)
261       break;
262   }
263 
264   _phySize = BlocksToBytes(numBlocks);
265   // _numBlocks = numBlocks;
266   const UInt64 physSize = (UInt64)numBlocks_from_Header << blockSizeLog_from_Header;
267   if (_phySize < physSize)
268       _phySize = physSize;
269   _isArc = true;
270   _stream = stream;
271 
272   return S_OK;
273   COM_TRY_END
274 }
275 
276 
277 Z7_COM7F_IMF(CHandler::Close())
278 {
279   _isArc = false;
280   _phySize = 0;
281   _items.Clear();
282   _stream.Release();
283   return S_OK;
284 }
285 
286 
287 static const Byte kProps[] =
288 {
289   kpidPath,
290   kpidSize,
291   kpidOffset,
292   kpidCharacts
293   // , kpidCpu
294 };
295 
296 static const Byte kArcProps[] =
297 {
298   kpidClusterSize
299   // , kpidNumBlocks
300 };
301 
302 IMP_IInArchive_Props
303 IMP_IInArchive_ArcProps
304 
305 static void GetString(AString &dest, const char *src)
306 {
307   dest.SetFrom_CalcLen(src, k_Str_Size);
308 }
309 
310 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
311 {
312   COM_TRY_BEGIN
313   NCOM::CPropVariant prop;
314   switch (propID)
315   {
316     case kpidMainSubfile:
317     {
318       int mainIndex = -1;
319       FOR_VECTOR (i, _items)
320       {
321         const CItem &item = _items[i];
322         if (!item.Is_Valid_and_Allocated())
323           continue;
324         AString s;
325         GetString(s, item.Type);
326         if (NDmg::Is_Apple_FS_Or_Unknown(s))
327         {
328           if (mainIndex != -1)
329           {
330             mainIndex = -1;
331             break;
332           }
333           mainIndex = (int)i;
334         }
335       }
336       if (mainIndex != -1)
337         prop = (UInt32)(Int32)mainIndex;
338       break;
339     }
340     case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break;
341     case kpidPhySize: prop = _phySize; break;
342     // case kpidNumBlocks: prop = _numBlocks; break;
343 
344     case kpidErrorFlags:
345     {
346       UInt32 v = 0;
347       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
348       prop = v;
349       break;
350     }
351   }
352   prop.Detach(value);
353   return S_OK;
354   COM_TRY_END
355 }
356 
357 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
358 {
359   *numItems = _items.Size();
360   return S_OK;
361 }
362 
363 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
364 {
365   COM_TRY_BEGIN
366   NCOM::CPropVariant prop;
367   const CItem &item = _items[index];
368   switch (propID)
369   {
370     case kpidPath:
371     {
372       AString s;
373       GetString(s, item.Name);
374       if (s.IsEmpty())
375         s.Add_UInt32(index);
376       AString type;
377       GetString(type, item.Type);
378       {
379         const char *ext = NDmg::Find_Apple_FS_Ext(type);
380         if (ext)
381           type = ext;
382       }
383       if (!type.IsEmpty())
384       {
385         s.Add_Dot();
386         s += type;
387       }
388       prop = s;
389       break;
390     }
391 /*
392     case kpidCpu:
393     {
394       AString s;
395       s.SetFrom_CalcLen(item.Processor, sizeof(item.Processor));
396       if (!s.IsEmpty())
397         prop = s;
398       break;
399     }
400 */
401     case kpidSize:
402     case kpidPackSize:
403       prop = BlocksToBytes(item.NumBlocks);
404       break;
405     case kpidOffset: prop = BlocksToBytes(item.StartBlock); break;
406     case kpidCharacts: FLAGS_TO_PROP(k_Flags, item.Flags, prop); break;
407   }
408   prop.Detach(value);
409   return S_OK;
410   COM_TRY_END
411 }
412 
413 static const Byte k_Signature[] = { kSig0, kSig1 };
414 
415 REGISTER_ARC_I(
416   "APM", "apm", NULL, 0xD4,
417   k_Signature,
418   0,
419   0,
420   IsArc_Apm)
421 
422 }}
423