xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/VdiHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // VdiHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../../C/CpuArch.h"
8 
9 #include "../../Common/ComTry.h"
10 #include "../../Common/IntToString.h"
11 #include "../../Common/MyBuffer.h"
12 
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/PropVariantUtils.h"
15 
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamUtils.h"
18 
19 #include "HandlerCont.h"
20 
21 #define Get32(p) GetUi32(p)
22 #define Get64(p) GetUi64(p)
23 
24 using namespace NWindows;
25 
26 namespace NArchive {
27 namespace NVdi {
28 
29 static const Byte k_Signature[] = { 0x7F, 0x10, 0xDA, 0xBE };
30 
31 static const unsigned k_ClusterBits = 20;
32 static const UInt32 k_ClusterSize = (UInt32)1 << k_ClusterBits;
33 
34 
35 /*
36 VDI_IMAGE_BLOCK_FREE = (~0) // returns any random data
37 VDI_IMAGE_BLOCK_ZERO = (~1) // returns zeros
38 */
39 
40 // static const UInt32 k_ClusterType_Free  = 0xffffffff;
41 static const UInt32 k_ClusterType_Zero  = 0xfffffffe;
42 
43 #define IS_CLUSTER_ALLOCATED(v) ((UInt32)(v) < k_ClusterType_Zero)
44 
45 
46 // static const UInt32 kDiskType_Dynamic = 1;
47 // static const UInt32 kDiskType_Static = 2;
48 
49 static const char * const kDiskTypes[] =
50 {
51     "0"
52   , "Dynamic"
53   , "Static"
54   , "Undo"
55   , "Diff"
56 };
57 
58 
59 enum EGuidType
60 {
61   k_GuidType_Creat,
62   k_GuidType_Modif,
63   k_GuidType_Link,
64   k_GuidType_PModif
65 };
66 
67 static const unsigned kNumGuids = 4;
68 static const char * const kGuidNames[kNumGuids] =
69 {
70     "Creat "
71   , "Modif "
72   , "Link  "
73   , "PModif"
74 };
75 
IsEmptyGuid(const Byte * data)76 static bool IsEmptyGuid(const Byte *data)
77 {
78   for (unsigned i = 0; i < 16; i++)
79     if (data[i] != 0)
80       return false;
81   return true;
82 }
83 
84 
85 
86 Z7_class_CHandler_final: public CHandlerImg
87 {
88   UInt32 _dataOffset;
89   CByteBuffer _table;
90   UInt64 _phySize;
91   UInt32 _imageType;
92   bool _isArc;
93   bool _unsupported;
94 
95   Byte Guids[kNumGuids][16];
96 
97   HRESULT Seek2(UInt64 offset)
98   {
99     _posInArc = offset;
100     return InStream_SeekSet(Stream, offset);
101   }
102 
103   HRESULT InitAndSeek()
104   {
105     _virtPos = 0;
106     return Seek2(0);
107   }
108 
109   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback) Z7_override;
110 
111 public:
112   Z7_IFACE_COM7_IMP(IInArchive_Img)
113 
114   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
115   Z7_IFACE_COM7_IMP(ISequentialInStream)
116 };
117 
118 
119 Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
120 {
121   if (processedSize)
122     *processedSize = 0;
123   if (_virtPos >= _size)
124     return S_OK;
125   {
126     UInt64 rem = _size - _virtPos;
127     if (size > rem)
128       size = (UInt32)rem;
129     if (size == 0)
130       return S_OK;
131   }
132 
133   {
134     UInt64 cluster = _virtPos >> k_ClusterBits;
135     UInt32 lowBits = (UInt32)_virtPos & (k_ClusterSize - 1);
136     {
137       UInt32 rem = k_ClusterSize - lowBits;
138       if (size > rem)
139         size = rem;
140     }
141 
142     cluster <<= 2;
143     if (cluster < _table.Size())
144     {
145       const Byte *p = (const Byte *)_table + (size_t)cluster;
146       const UInt32 v = Get32(p);
147       if (IS_CLUSTER_ALLOCATED(v))
148       {
149         UInt64 offset = _dataOffset + ((UInt64)v << k_ClusterBits);
150         offset += lowBits;
151         if (offset != _posInArc)
152         {
153           RINOK(Seek2(offset))
154         }
155         HRESULT res = Stream->Read(data, size, &size);
156         _posInArc += size;
157         _virtPos += size;
158         if (processedSize)
159           *processedSize = size;
160         return res;
161       }
162     }
163 
164     memset(data, 0, size);
165     _virtPos += size;
166     if (processedSize)
167       *processedSize = size;
168     return S_OK;
169   }
170 }
171 
172 
173 static const Byte kProps[] =
174 {
175   kpidSize,
176   kpidPackSize
177 };
178 
179 static const Byte kArcProps[] =
180 {
181   kpidHeadersSize,
182   kpidMethod,
183   kpidComment,
184   kpidName
185 };
186 
187 IMP_IInArchive_Props
188 IMP_IInArchive_ArcProps
189 
190 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
191 {
192   COM_TRY_BEGIN
193   NCOM::CPropVariant prop;
194 
195   switch (propID)
196   {
197     case kpidMainSubfile: prop = (UInt32)0; break;
198     case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
199     case kpidHeadersSize: prop = _dataOffset; break;
200 
201     case kpidMethod:
202     {
203       TYPE_TO_PROP(kDiskTypes, _imageType, prop);
204       break;
205     }
206 
207     case kpidErrorFlags:
208     {
209       UInt32 v = 0;
210       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
211       if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
212       // if (_headerError) v |= kpv_ErrorFlags_HeadersError;
213       if (!Stream && v == 0 && _isArc)
214         v = kpv_ErrorFlags_HeadersError;
215       if (v != 0)
216         prop = v;
217       break;
218     }
219 
220     case kpidComment:
221     {
222       AString s;
223       for (unsigned i = 0; i < kNumGuids; i++)
224       {
225         const Byte *guid = Guids[i];
226         if (!IsEmptyGuid(guid))
227         {
228           s.Add_LF();
229           s += kGuidNames[i];
230           s += " : ";
231           char temp[64];
232           RawLeGuidToString_Braced(guid, temp);
233           MyStringLower_Ascii(temp);
234           s += temp;
235         }
236       }
237       if (!s.IsEmpty())
238         prop = s;
239       break;
240     }
241 
242     case kpidName:
243     {
244       const Byte *guid = Guids[k_GuidType_Creat];
245       if (!IsEmptyGuid(guid))
246       {
247         char temp[64];
248         RawLeGuidToString_Braced(guid, temp);
249         MyStringLower_Ascii(temp);
250         MyStringCat(temp, ".vdi");
251         prop = temp;
252       }
253       break;
254     }
255   }
256 
257   prop.Detach(value);
258   return S_OK;
259   COM_TRY_END
260 }
261 
262 
263 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
264 {
265   COM_TRY_BEGIN
266   NCOM::CPropVariant prop;
267 
268   switch (propID)
269   {
270     case kpidSize: prop = _size; break;
271     case kpidPackSize: prop = _phySize - _dataOffset; break;
272     case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
273   }
274 
275   prop.Detach(value);
276   return S_OK;
277   COM_TRY_END
278 }
279 
280 
281 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback * /* openCallback */)
282 {
283   const unsigned kHeaderSize = 512;
284   Byte buf[kHeaderSize];
285   RINOK(ReadStream_FALSE(stream, buf, kHeaderSize))
286 
287   if (memcmp(buf + 0x40, k_Signature, sizeof(k_Signature)) != 0)
288     return S_FALSE;
289 
290   const UInt32 version = Get32(buf + 0x44);
291   if (version >= 0x20000)
292     return S_FALSE;
293   if (version < 0x10000)
294   {
295     _unsupported = true;
296     return S_FALSE;
297   }
298 
299   const unsigned kHeaderOffset = 0x48;
300   const unsigned kGuidsOffsets = 0x188;
301   const UInt32 headerSize = Get32(buf + kHeaderOffset);
302   if (headerSize < kGuidsOffsets - kHeaderOffset || headerSize > 0x200 - kHeaderOffset)
303     return S_FALSE;
304 
305   _imageType = Get32(buf + 0x4C);
306   // Int32 flags = Get32(buf + 0x50);
307   // Byte Comment[0x100]
308 
309   const UInt32 tableOffset = Get32(buf + 0x154);
310   if (tableOffset < 0x200)
311     return S_FALSE;
312 
313   _dataOffset = Get32(buf + 0x158);
314 
315   // UInt32 geometry[3];
316 
317   const UInt32 sectorSize = Get32(buf + 0x168);
318   if (sectorSize != 0x200)
319     return S_FALSE;
320 
321   _size = Get64(buf + 0x170);
322   const UInt32 blockSize = Get32(buf + 0x178);
323   const UInt32 totalBlocks = Get32(buf + 0x180);
324   const UInt32 numAllocatedBlocks = Get32(buf + 0x184);
325 
326   _isArc = true;
327 
328   if (_dataOffset < tableOffset)
329     return S_FALSE;
330 
331   if (_imageType > 4)
332     _unsupported = true;
333 
334   if (blockSize != k_ClusterSize)
335   {
336     _unsupported = true;
337     return S_FALSE;
338   }
339 
340   if (headerSize >= kGuidsOffsets + kNumGuids * 16 - kHeaderOffset)
341   {
342     for (unsigned i = 0; i < kNumGuids; i++)
343       memcpy(Guids[i], buf + kGuidsOffsets + 16 * i, 16);
344 
345     if (!IsEmptyGuid(Guids[k_GuidType_Link]) ||
346         !IsEmptyGuid(Guids[k_GuidType_PModif]))
347       _unsupported = true;
348   }
349 
350   {
351     UInt64 size2 = (UInt64)totalBlocks << k_ClusterBits;
352     if (size2 < _size)
353     {
354       _unsupported = true;
355       return S_FALSE;
356     }
357     /*
358     if (size2 > _size)
359       _size = size2;
360     */
361   }
362 
363   {
364     UInt32 tableReserved = _dataOffset - tableOffset;
365     if ((tableReserved >> 2) < totalBlocks)
366       return S_FALSE;
367   }
368 
369   _phySize = _dataOffset + ((UInt64)numAllocatedBlocks << k_ClusterBits);
370 
371   const size_t numBytes = (size_t)totalBlocks * 4;
372   if ((numBytes >> 2) != totalBlocks)
373   {
374     _unsupported = true;
375     return E_OUTOFMEMORY;
376   }
377 
378   _table.Alloc(numBytes);
379   RINOK(InStream_SeekSet(stream, tableOffset))
380   RINOK(ReadStream_FALSE(stream, _table, numBytes))
381 
382   const Byte *data = _table;
383   for (UInt32 i = 0; i < totalBlocks; i++)
384   {
385     const UInt32 v = Get32(data + (size_t)i * 4);
386     if (!IS_CLUSTER_ALLOCATED(v))
387       continue;
388     if (v >= numAllocatedBlocks)
389     {
390       _unsupported = true;
391       return S_FALSE;
392     }
393   }
394 
395   Stream = stream;
396   return S_OK;
397 }
398 
399 
400 Z7_COM7F_IMF(CHandler::Close())
401 {
402   _table.Free();
403   _phySize = 0;
404   _isArc = false;
405   _unsupported = false;
406 
407   for (unsigned i = 0; i < kNumGuids; i++)
408     memset(Guids[i], 0, 16);
409 
410   // CHandlerImg:
411   Clear_HandlerImg_Vars();
412   Stream.Release();
413   return S_OK;
414 }
415 
416 
417 Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
418 {
419   COM_TRY_BEGIN
420   *stream = NULL;
421   if (_unsupported)
422     return S_FALSE;
423   CMyComPtr<ISequentialInStream> streamTemp = this;
424   RINOK(InitAndSeek())
425   *stream = streamTemp.Detach();
426   return S_OK;
427   COM_TRY_END
428 }
429 
430 
431 REGISTER_ARC_I(
432   "VDI", "vdi", NULL, 0xC9,
433   k_Signature,
434   0x40,
435   0,
436   NULL)
437 
438 }}
439