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