VirtualBox

source: vbox/trunk/src/VBox/Storage/DMG.cpp@ 79965

Last change on this file since 79965 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 78.7 KB
Line 
1/* $Id: DMG.cpp 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * VBoxDMG - Interpreter for Apple Disk Images (DMG).
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_DMG
23#include <VBox/vd-plugin.h>
24#include <VBox/vd-ifs.h>
25#include <VBox/log.h>
26#include <VBox/err.h>
27
28#include <iprt/asm.h>
29#include <iprt/alloca.h>
30#include <iprt/assert.h>
31#include <iprt/base64.h>
32#include <iprt/ctype.h>
33#include <iprt/mem.h>
34#include <iprt/string.h>
35#include <iprt/zip.h>
36#include <iprt/formats/xar.h>
37
38#include "VDBackends.h"
39#include "VDBackendsInline.h"
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45#if 0
46/** @def VBOX_WITH_DIRECT_XAR_ACCESS
47 * When defined, we will use RTVfs to access the XAR file instead of going
48 * the slightly longer way thru the VFS -> VD wrapper. */
49# define VBOX_WITH_DIRECT_XAR_ACCESS
50#endif
51
52/** Sector size, multiply with all sector counts to get number of bytes. */
53#define DMG_SECTOR_SIZE 512
54
55/** Convert block number/size to byte offset/size. */
56#define DMG_BLOCK2BYTE(u) ((uint64_t)(u) << 9)
57
58/** Convert byte offset/size to block number/size. */
59#define DMG_BYTE2BLOCK(u) ((u) >> 9)
60
61/**
62 * UDIF checksum structure.
63 */
64typedef struct DMGUDIFCKSUM
65{
66 uint32_t u32Kind; /**< The kind of checksum. */
67 uint32_t cBits; /**< The size of the checksum. */
68 union
69 {
70 uint8_t au8[128]; /**< 8-bit view. */
71 uint32_t au32[32]; /**< 32-bit view. */
72 } uSum; /**< The checksum. */
73} DMGUDIFCKSUM;
74AssertCompileSize(DMGUDIFCKSUM, 8 + 128);
75typedef DMGUDIFCKSUM *PDMGUDIFCKSUM;
76typedef const DMGUDIFCKSUM *PCDMGUDIFCKSUM;
77
78/** @name Checksum Kind (DMGUDIFCKSUM::u32Kind)
79 * @{ */
80/** No checksum. */
81#define DMGUDIFCKSUM_NONE UINT32_C(0)
82/** CRC-32. */
83#define DMGUDIFCKSUM_CRC32 UINT32_C(2)
84/** @} */
85
86/**
87 * UDIF ID.
88 * This is kind of like a UUID only it isn't, but we'll use the UUID
89 * representation of it for simplicity.
90 */
91typedef RTUUID DMGUDIFID;
92AssertCompileSize(DMGUDIFID, 16);
93typedef DMGUDIFID *PDMGUDIFID;
94typedef const DMGUDIFID *PCDMGUDIFID;
95
96/**
97 * UDIF footer used by Apple Disk Images (DMG).
98 *
99 * This is a footer placed 512 bytes from the end of the file. Typically a DMG
100 * file starts with the data, which is followed by the block table and then ends
101 * with this structure.
102 *
103 * All fields are stored in big endian format.
104 */
105#pragma pack(1)
106typedef struct DMGUDIF
107{
108 uint32_t u32Magic; /**< 0x000 - Magic, 'koly' (DMGUDIF_MAGIC). (fUDIFSignature) */
109 uint32_t u32Version; /**< 0x004 - The UDIF version (DMGUDIF_VER_CURRENT). (fUDIFVersion) */
110 uint32_t cbFooter; /**< 0x008 - The size of the this structure (512). (fUDIFHeaderSize) */
111 uint32_t fFlags; /**< 0x00c - Flags. (fUDIFFlags) */
112 uint64_t offRunData; /**< 0x010 - Where the running data fork starts (usually 0). (fUDIFRunningDataForkOffset) */
113 uint64_t offData; /**< 0x018 - Where the data fork starts (usually 0). (fUDIFDataForkOffset) */
114 uint64_t cbData; /**< 0x020 - Size of the data fork (in bytes). (fUDIFDataForkLength) */
115 uint64_t offRsrc; /**< 0x028 - Where the resource fork starts (usually cbData or 0). (fUDIFRsrcForkOffset) */
116 uint64_t cbRsrc; /**< 0x030 - The size of the resource fork. (fUDIFRsrcForkLength)*/
117 uint32_t iSegment; /**< 0x038 - The segment number of this file. (fUDIFSegmentNumber) */
118 uint32_t cSegments; /**< 0x03c - The number of segments. (fUDIFSegmentCount) */
119 DMGUDIFID SegmentId; /**< 0x040 - The segment ID. (fUDIFSegmentID) */
120 DMGUDIFCKSUM DataCkSum; /**< 0x050 - The data checksum. (fUDIFDataForkChecksum) */
121 uint64_t offXml; /**< 0x0d8 - The XML offset (.plist kind of data). (fUDIFXMLOffset) */
122 uint64_t cbXml; /**< 0x0e0 - The size of the XML. (fUDIFXMLSize) */
123 uint8_t abUnknown[120]; /**< 0x0e8 - Unknown stuff, hdiutil doesn't dump it... */
124 DMGUDIFCKSUM MasterCkSum; /**< 0x160 - The master checksum. (fUDIFMasterChecksum) */
125 uint32_t u32Type; /**< 0x1e8 - The image type. (fUDIFImageVariant) */
126 uint64_t cSectors; /**< 0x1ec - The sector count. Warning! Unaligned! (fUDISectorCount) */
127 uint32_t au32Unknown[3]; /**< 0x1f4 - Unknown stuff, hdiutil doesn't dump it... */
128} DMGUDIF;
129#pragma pack()
130AssertCompileSize(DMGUDIF, 512);
131AssertCompileMemberOffset(DMGUDIF, cbRsrc, 0x030);
132AssertCompileMemberOffset(DMGUDIF, cbXml, 0x0e0);
133AssertCompileMemberOffset(DMGUDIF, cSectors, 0x1ec);
134
135typedef DMGUDIF *PDMGUDIF;
136typedef const DMGUDIF *PCDMGUDIF;
137
138/** The UDIF magic 'koly' (DMGUDIF::u32Magic). */
139#define DMGUDIF_MAGIC UINT32_C(0x6b6f6c79)
140
141/** The current UDIF version (DMGUDIF::u32Version).
142 * This is currently the only we recognizes and will create. */
143#define DMGUDIF_VER_CURRENT 4
144
145/** @name UDIF flags (DMGUDIF::fFlags).
146 * @{ */
147/** Flatten image whatever that means.
148 * (hdiutil -debug calls it kUDIFFlagsFlattened.) */
149#define DMGUDIF_FLAGS_FLATTENED RT_BIT_32(0)
150/** Internet enabled image.
151 * (hdiutil -debug calls it kUDIFFlagsInternetEnabled) */
152#define DMGUDIF_FLAGS_INET_ENABLED RT_BIT_32(2)
153/** Mask of known bits. */
154#define DMGUDIF_FLAGS_KNOWN_MASK (RT_BIT_32(0) | RT_BIT_32(2))
155/** @} */
156
157/** @name UDIF Image Types (DMGUDIF::u32Type).
158 * @{ */
159/** Device image type. (kUDIFDeviceImageType) */
160#define DMGUDIF_TYPE_DEVICE 1
161/** Device image type. (kUDIFPartitionImageType) */
162#define DMGUDIF_TYPE_PARTITION 2
163/** @} */
164
165/**
166 * BLKX data.
167 *
168 * This contains the start offset and size of raw data stored in the image.
169 *
170 * All fields are stored in big endian format.
171 */
172#pragma pack(1)
173typedef struct DMGBLKX
174{
175 uint32_t u32Magic; /**< 0x000 - Magic, 'mish' (DMGBLKX_MAGIC). */
176 uint32_t u32Version; /**< 0x004 - The BLKX version (DMGBLKX_VER_CURRENT). */
177 uint64_t cSectornumberFirst; /**< 0x008 - The first sector number the block represents in the virtual device. */
178 uint64_t cSectors; /**< 0x010 - Number of sectors this block represents. */
179 uint64_t offDataStart; /**< 0x018 - Start offset for raw data. */
180 uint32_t cSectorsDecompress; /**< 0x020 - Size of the buffer in sectors needed to decompress. */
181 uint32_t u32BlocksDescriptor; /**< 0x024 - Blocks descriptor. */
182 uint8_t abReserved[24];
183 DMGUDIFCKSUM BlkxCkSum; /**< 0x03c - Checksum for the BLKX table. */
184 uint32_t cBlocksRunCount; /**< 0x - Number of entries in the blkx run table afterwards. */
185} DMGBLKX;
186#pragma pack()
187AssertCompileSize(DMGBLKX, 204);
188
189typedef DMGBLKX *PDMGBLKX;
190typedef const DMGBLKX *PCDMGBLKX;
191
192/** The BLKX magic 'mish' (DMGBLKX::u32Magic). */
193#define DMGBLKX_MAGIC UINT32_C(0x6d697368)
194/** BLKX version (DMGBLKX::u32Version). */
195#define DMGBLKX_VERSION UINT32_C(0x00000001)
196
197/** Blocks descriptor type: entire device. */
198#define DMGBLKX_DESC_ENTIRE_DEVICE UINT32_C(0xfffffffe)
199
200/**
201 * BLKX table descriptor.
202 *
203 * All fields are stored in big endian format.
204 */
205#pragma pack(1)
206typedef struct DMGBLKXDESC
207{
208 uint32_t u32Type; /**< 0x000 - Type of the descriptor. */
209 uint32_t u32Reserved; /**< 0x004 - Reserved, but contains +beg or +end in case thisi is a comment descriptor. */
210 uint64_t u64SectorStart; /**< 0x008 - First sector number in the block this entry describes. */
211 uint64_t u64SectorCount; /**< 0x010 - Number of sectors this entry describes. */
212 uint64_t offData; /**< 0x018 - Offset in the image where the data starts. */
213 uint64_t cbData; /**< 0x020 - Number of bytes in the image. */
214} DMGBLKXDESC;
215#pragma pack()
216AssertCompileSize(DMGBLKXDESC, 40);
217
218typedef DMGBLKXDESC *PDMGBLKXDESC;
219typedef const DMGBLKXDESC *PCDMGBLKXDESC;
220
221/** Raw image data type. */
222#define DMGBLKXDESC_TYPE_RAW 1
223/** Ignore type. */
224#define DMGBLKXDESC_TYPE_IGNORE 2
225/** Compressed with zlib type. */
226#define DMGBLKXDESC_TYPE_ZLIB UINT32_C(0x80000005)
227/** Comment type. */
228#define DMGBLKXDESC_TYPE_COMMENT UINT32_C(0x7ffffffe)
229/** Terminator type. */
230#define DMGBLKXDESC_TYPE_TERMINATOR UINT32_C(0xffffffff)
231
232/**
233 * UDIF Resource Entry.
234 */
235typedef struct DMGUDIFRSRCENTRY
236{
237 /** The ID. */
238 int32_t iId;
239 /** Attributes. */
240 uint32_t fAttributes;
241 /** The name. */
242 char *pszName;
243 /** The CoreFoundation name. Can be NULL. */
244 char *pszCFName;
245 /** The size of the data. */
246 size_t cbData;
247 /** The raw data. */
248 uint8_t *pbData;
249} DMGUDIFRSRCENTRY;
250/** Pointer to an UDIF resource entry. */
251typedef DMGUDIFRSRCENTRY *PDMGUDIFRSRCENTRY;
252/** Pointer to a const UDIF resource entry. */
253typedef DMGUDIFRSRCENTRY const *PCDMGUDIFRSRCENTRY;
254
255/**
256 * UDIF Resource Array.
257 */
258typedef struct DMGUDIFRSRCARRAY
259{
260 /** The array name. */
261 char szName[12];
262 /** The number of occupied entries. */
263 uint32_t cEntries;
264 /** The array entries.
265 * A lazy bird ASSUME there are no more than 4 entries in any DMG. Increase the
266 * size if DMGs with more are found.
267 * r=aeichner: Saw one with 6 here (image of a whole DVD) */
268 DMGUDIFRSRCENTRY aEntries[10];
269} DMGUDIFRSRCARRAY;
270/** Pointer to a UDIF resource array. */
271typedef DMGUDIFRSRCARRAY *PDMGUDIFRSRCARRAY;
272/** Pointer to a const UDIF resource array. */
273typedef DMGUDIFRSRCARRAY const *PCDMGUDIFRSRCARRAY;
274
275/**
276 * DMG extent types.
277 */
278typedef enum DMGEXTENTTYPE
279{
280 /** Null, never used. */
281 DMGEXTENTTYPE_NULL = 0,
282 /** Raw image data. */
283 DMGEXTENTTYPE_RAW,
284 /** Zero extent, reads return 0 and writes have no effect. */
285 DMGEXTENTTYPE_ZERO,
286 /** Compressed extent - compression method ZLIB. */
287 DMGEXTENTTYPE_COMP_ZLIB,
288 /** 32bit hack. */
289 DMGEXTENTTYPE_32BIT_HACK = 0x7fffffff
290} DMGEXTENTTYPE, *PDMGEXTENTTYPE;
291
292/**
293 * DMG extent mapping a virtual image block to real file offsets.
294 */
295typedef struct DMGEXTENT
296{
297 /** Extent type. */
298 DMGEXTENTTYPE enmType;
299 /** First sector this extent describes. */
300 uint64_t uSectorExtent;
301 /** Number of sectors this extent describes. */
302 uint64_t cSectorsExtent;
303 /** Start offset in the real file. */
304 uint64_t offFileStart;
305 /** Number of bytes for the extent data in the file. */
306 uint64_t cbFile;
307} DMGEXTENT;
308/** Pointer to an DMG extent. */
309typedef DMGEXTENT *PDMGEXTENT;
310
311/**
312 * VirtualBox Apple Disk Image (DMG) interpreter instance data.
313 */
314typedef struct DMGIMAGE
315{
316 /** Image name.
317 * Kept around for logging and delete-on-close purposes. */
318 const char *pszFilename;
319 /** Storage handle. */
320 PVDIOSTORAGE pStorage;
321
322 /** Pointer to the per-disk VD interface list. */
323 PVDINTERFACE pVDIfsDisk;
324 /** Pointer to the per-image VD interface list. */
325 PVDINTERFACE pVDIfsImage;
326 /** Error interface. */
327 PVDINTERFACEERROR pIfError;
328 /** I/O interface - careful accessing this because of hDmgFileInXar. */
329 PVDINTERFACEIOINT pIfIoXxx;
330
331
332 /** The VFS file handle for a DMG within a XAR archive. */
333 RTVFSFILE hDmgFileInXar;
334 /** XAR file system stream handle.
335 * Sitting on this isn't really necessary, but insurance against the XAR code
336 * changes making back references from child objects to the stream itself. */
337 RTVFSFSSTREAM hXarFss;
338
339 /** Flags the image was opened with. */
340 uint32_t uOpenFlags;
341 /** Image flags. */
342 unsigned uImageFlags;
343 /** Total size of the virtual image. */
344 uint64_t cbSize;
345 /** Size of the image. */
346 uint64_t cbFile;
347 /** Physical geometry of this image. */
348 VDGEOMETRY PCHSGeometry;
349 /** Logical geometry of this image. */
350 VDGEOMETRY LCHSGeometry;
351
352 /** The resources.
353 * A lazy bird ASSUME there are only two arrays in the resource-fork section in
354 * the XML, namely 'blkx' and 'plst'. These have been assigned fixed indexes. */
355 DMGUDIFRSRCARRAY aRsrcs[2];
356 /** The UDIF footer. */
357 DMGUDIF Ftr;
358
359 /** Number of valid extents in the array. */
360 unsigned cExtents;
361 /** Number of entries the array can hold. */
362 unsigned cExtentsMax;
363 /** Pointer to the extent array. */
364 PDMGEXTENT paExtents;
365 /** Index of the last accessed extent. */
366 unsigned idxExtentLast;
367
368 /** Extent which owns the data in the buffer. */
369 PDMGEXTENT pExtentDecomp;
370 /** Buffer holding the decompressed data for a extent. */
371 void *pvDecompExtent;
372 /** Size of the buffer. */
373 size_t cbDecompExtent;
374 /** The static region list. */
375 VDREGIONLIST RegionList;
376} DMGIMAGE;
377/** Pointer to an instance of the DMG Image Interpreter. */
378typedef DMGIMAGE *PDMGIMAGE;
379
380/** @name Resources indexes (into DMG::aRsrcs).
381 * @{ */
382#define DMG_RSRC_IDX_BLKX 0
383#define DMG_RSRC_IDX_PLST 1
384/** @} */
385
386/** State for the input callout of the inflate reader. */
387typedef struct DMGINFLATESTATE
388{
389 /* Image this operation relates to. */
390 PDMGIMAGE pImage;
391 /* Total size of the data to read. */
392 size_t cbSize;
393 /* Offset in the file to read. */
394 uint64_t uFileOffset;
395 /* Current read position. */
396 ssize_t iOffset;
397} DMGINFLATESTATE;
398
399
400/*********************************************************************************************************************************
401* Defined Constants And Macros *
402*********************************************************************************************************************************/
403/** @def DMG_PRINTF
404 * Wrapper for LogRel.
405 */
406#define DMG_PRINTF(a) LogRel(a)
407
408/** @def DMG_VALIDATE
409 * For validating a struct thing and log/print what's wrong.
410 */
411# define DMG_VALIDATE(expr, logstuff) \
412 do { \
413 if (!(expr)) \
414 { \
415 LogRel(("DMG: validation failed: %s\nDMG: ", #expr)); \
416 LogRel(logstuff); \
417 fRc = false; \
418 } \
419 } while (0)
420
421
422/*********************************************************************************************************************************
423* Static Variables *
424*********************************************************************************************************************************/
425
426/** NULL-terminated array of supported file extensions. */
427static const VDFILEEXTENSION s_aDmgFileExtensions[] =
428{
429 {"dmg", VDTYPE_OPTICAL_DISC},
430 {NULL, VDTYPE_INVALID}
431};
432
433
434/*********************************************************************************************************************************
435* Internal Functions *
436*********************************************************************************************************************************/
437#if 0 /* unused */
438static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif);
439#endif
440static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif);
441
442static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId);
443static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId);
444
445#if 0 /* unused */
446static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum);
447#endif
448static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum);
449static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix);
450
451
452
453/**
454 * vdIfIoIntFileReadSync / RTVfsFileReadAt wrapper.
455 */
456static int dmgWrapFileReadSync(PDMGIMAGE pThis, RTFOFF off, void *pvBuf, size_t cbToRead)
457{
458 int rc;
459 if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
460 rc = vdIfIoIntFileReadSync(pThis->pIfIoXxx, pThis->pStorage, off, pvBuf, cbToRead);
461 else
462 rc = RTVfsFileReadAt(pThis->hDmgFileInXar, off, pvBuf, cbToRead, NULL);
463 return rc;
464}
465
466/**
467 * vdIfIoIntFileReadUser / RTVfsFileReadAt wrapper.
468 */
469static int dmgWrapFileReadUser(PDMGIMAGE pThis, RTFOFF off, PVDIOCTX pIoCtx, size_t cbToRead)
470{
471 int rc;
472 if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
473 rc = vdIfIoIntFileReadUser(pThis->pIfIoXxx, pThis->pStorage, off, pIoCtx, cbToRead);
474 else
475 {
476 /*
477 * Alloate a temporary buffer on the stack or heap and use
478 * vdIfIoIntIoCtxCopyTo to work the context.
479 *
480 * The I/O context stuff seems too complicated and undocument that I'm
481 * not going to bother trying to implement this efficiently right now.
482 */
483 void *pvFree = NULL;
484 void *pvBuf;
485 if (cbToRead < _32K)
486 pvBuf = alloca(cbToRead);
487 else
488 pvFree = pvBuf = RTMemTmpAlloc(cbToRead);
489 if (pvBuf)
490 {
491 rc = RTVfsFileReadAt(pThis->hDmgFileInXar, off, pvBuf, cbToRead, NULL);
492 if (RT_SUCCESS(rc))
493 vdIfIoIntIoCtxCopyTo(pThis->pIfIoXxx, pIoCtx, pvBuf, cbToRead);
494 if (pvFree)
495 RTMemTmpFree(pvFree);
496 }
497 else
498 rc = VERR_NO_TMP_MEMORY;
499 }
500 return rc;
501}
502
503/**
504 * vdIfIoIntFileGetSize / RTVfsFileGetSize wrapper.
505 */
506static int dmgWrapFileGetSize(PDMGIMAGE pThis, uint64_t *pcbFile)
507{
508 int rc;
509 if (pThis->hDmgFileInXar == NIL_RTVFSFILE)
510 rc = vdIfIoIntFileGetSize(pThis->pIfIoXxx, pThis->pStorage, pcbFile);
511 else
512 rc = RTVfsFileGetSize(pThis->hDmgFileInXar, pcbFile);
513 return rc;
514}
515
516
517
518static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
519{
520 DMGINFLATESTATE *pInflateState = (DMGINFLATESTATE *)pvUser;
521
522 Assert(cbBuf);
523 if (pInflateState->iOffset < 0)
524 {
525 *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
526 if (pcbBuf)
527 *pcbBuf = 1;
528 pInflateState->iOffset = 0;
529 return VINF_SUCCESS;
530 }
531 cbBuf = RT_MIN(cbBuf, pInflateState->cbSize);
532 int rc = dmgWrapFileReadSync(pInflateState->pImage, pInflateState->uFileOffset, pvBuf, cbBuf);
533 if (RT_FAILURE(rc))
534 return rc;
535 pInflateState->uFileOffset += cbBuf;
536 pInflateState->iOffset += cbBuf;
537 pInflateState->cbSize -= cbBuf;
538 Assert(pcbBuf);
539 *pcbBuf = cbBuf;
540 return VINF_SUCCESS;
541}
542
543/**
544 * Internal: read from a file and inflate the compressed data,
545 * distinguishing between async and normal operation
546 */
547DECLINLINE(int) dmgFileInflateSync(PDMGIMAGE pImage, uint64_t uOffset, size_t cbToRead,
548 void *pvBuf, size_t cbBuf)
549{
550 int rc;
551 PRTZIPDECOMP pZip = NULL;
552 DMGINFLATESTATE InflateState;
553 size_t cbActuallyRead;
554
555 InflateState.pImage = pImage;
556 InflateState.cbSize = cbToRead;
557 InflateState.uFileOffset = uOffset;
558 InflateState.iOffset = -1;
559
560 rc = RTZipDecompCreate(&pZip, &InflateState, dmgFileInflateHelper);
561 if (RT_FAILURE(rc))
562 return rc;
563 rc = RTZipDecompress(pZip, pvBuf, cbBuf, &cbActuallyRead);
564 RTZipDecompDestroy(pZip);
565 if (RT_FAILURE(rc))
566 return rc;
567 if (cbActuallyRead != cbBuf)
568 rc = VERR_VD_VMDK_INVALID_FORMAT;
569 return rc;
570}
571
572/**
573 * Swaps endian.
574 * @param pUdif The structure.
575 */
576static void dmgSwapEndianUdif(PDMGUDIF pUdif)
577{
578#ifndef RT_BIG_ENDIAN
579 pUdif->u32Magic = RT_BSWAP_U32(pUdif->u32Magic);
580 pUdif->u32Version = RT_BSWAP_U32(pUdif->u32Version);
581 pUdif->cbFooter = RT_BSWAP_U32(pUdif->cbFooter);
582 pUdif->fFlags = RT_BSWAP_U32(pUdif->fFlags);
583 pUdif->offRunData = RT_BSWAP_U64(pUdif->offRunData);
584 pUdif->offData = RT_BSWAP_U64(pUdif->offData);
585 pUdif->cbData = RT_BSWAP_U64(pUdif->cbData);
586 pUdif->offRsrc = RT_BSWAP_U64(pUdif->offRsrc);
587 pUdif->cbRsrc = RT_BSWAP_U64(pUdif->cbRsrc);
588 pUdif->iSegment = RT_BSWAP_U32(pUdif->iSegment);
589 pUdif->cSegments = RT_BSWAP_U32(pUdif->cSegments);
590 pUdif->offXml = RT_BSWAP_U64(pUdif->offXml);
591 pUdif->cbXml = RT_BSWAP_U64(pUdif->cbXml);
592 pUdif->u32Type = RT_BSWAP_U32(pUdif->u32Type);
593 pUdif->cSectors = RT_BSWAP_U64(pUdif->cSectors);
594#endif
595}
596
597
598#if 0 /* unused */
599/**
600 * Swaps endian from host cpu to file.
601 * @param pUdif The structure.
602 */
603static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif)
604{
605 dmgSwapEndianUdif(pUdif);
606 dmgUdifIdHost2FileEndian(&pUdif->SegmentId);
607 dmgUdifCkSumHost2FileEndian(&pUdif->DataCkSum);
608 dmgUdifCkSumHost2FileEndian(&pUdif->MasterCkSum);
609}
610#endif
611
612
613/**
614 * Swaps endian from file to host cpu.
615 * @param pUdif The structure.
616 */
617static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif)
618{
619 dmgSwapEndianUdif(pUdif);
620 dmgUdifIdFile2HostEndian(&pUdif->SegmentId);
621 dmgUdifCkSumFile2HostEndian(&pUdif->DataCkSum);
622 dmgUdifCkSumFile2HostEndian(&pUdif->MasterCkSum);
623}
624
625/**
626 * Swaps endian from file to host cpu.
627 * @param pBlkx The blkx structure.
628 */
629static void dmgBlkxFile2HostEndian(PDMGBLKX pBlkx)
630{
631 pBlkx->u32Magic = RT_BE2H_U32(pBlkx->u32Magic);
632 pBlkx->u32Version = RT_BE2H_U32(pBlkx->u32Version);
633 pBlkx->cSectornumberFirst = RT_BE2H_U64(pBlkx->cSectornumberFirst);
634 pBlkx->cSectors = RT_BE2H_U64(pBlkx->cSectors);
635 pBlkx->offDataStart = RT_BE2H_U64(pBlkx->offDataStart);
636 pBlkx->cSectorsDecompress = RT_BE2H_U32(pBlkx->cSectorsDecompress);
637 pBlkx->u32BlocksDescriptor = RT_BE2H_U32(pBlkx->u32BlocksDescriptor);
638 pBlkx->cBlocksRunCount = RT_BE2H_U32(pBlkx->cBlocksRunCount);
639 dmgUdifCkSumFile2HostEndian(&pBlkx->BlkxCkSum);
640}
641
642/**
643 * Swaps endian from file to host cpu.
644 * @param pBlkxDesc The blkx descriptor structure.
645 */
646static void dmgBlkxDescFile2HostEndian(PDMGBLKXDESC pBlkxDesc)
647{
648 pBlkxDesc->u32Type = RT_BE2H_U32(pBlkxDesc->u32Type);
649 pBlkxDesc->u32Reserved = RT_BE2H_U32(pBlkxDesc->u32Reserved);
650 pBlkxDesc->u64SectorStart = RT_BE2H_U64(pBlkxDesc->u64SectorStart);
651 pBlkxDesc->u64SectorCount = RT_BE2H_U64(pBlkxDesc->u64SectorCount);
652 pBlkxDesc->offData = RT_BE2H_U64(pBlkxDesc->offData);
653 pBlkxDesc->cbData = RT_BE2H_U64(pBlkxDesc->cbData);
654}
655
656/**
657 * Validates an UDIF footer structure.
658 *
659 * @returns true if valid, false and LogRel()s on failure.
660 * @param pFtr The UDIF footer to validate.
661 * @param offFtr The offset of the structure.
662 */
663static bool dmgUdifFtrIsValid(PCDMGUDIF pFtr, uint64_t offFtr)
664{
665 bool fRc = true;
666
667 DMG_VALIDATE(!(pFtr->fFlags & ~DMGUDIF_FLAGS_KNOWN_MASK), ("fFlags=%#RX32 fKnown=%RX32\n", pFtr->fFlags, DMGUDIF_FLAGS_KNOWN_MASK));
668 DMG_VALIDATE(pFtr->offRunData < offFtr, ("offRunData=%#RX64\n", pFtr->offRunData));
669 DMG_VALIDATE(pFtr->cbData <= offFtr && pFtr->offData + pFtr->cbData <= offFtr, ("cbData=%#RX64 offData=%#RX64 offFtr=%#RX64\n", pFtr->cbData, pFtr->offData, offFtr));
670 DMG_VALIDATE(pFtr->offData < offFtr, ("offData=%#RX64\n", pFtr->offData));
671 DMG_VALIDATE(pFtr->cbRsrc <= offFtr && pFtr->offRsrc + pFtr->cbRsrc <= offFtr, ("cbRsrc=%#RX64 offRsrc=%#RX64 offFtr=%#RX64\n", pFtr->cbRsrc, pFtr->offRsrc, offFtr));
672 DMG_VALIDATE(pFtr->offRsrc < offFtr, ("offRsrc=%#RX64\n", pFtr->offRsrc));
673 DMG_VALIDATE(pFtr->cSegments <= 1, ("cSegments=%RU32\n", pFtr->cSegments));
674 DMG_VALIDATE(pFtr->iSegment == 0 || pFtr->iSegment == 1, ("iSegment=%RU32 cSegments=%RU32\n", pFtr->iSegment, pFtr->cSegments));
675 DMG_VALIDATE(pFtr->cbXml <= offFtr && pFtr->offXml + pFtr->cbXml <= offFtr, ("cbXml=%#RX64 offXml=%#RX64 offFtr=%#RX64\n", pFtr->cbXml, pFtr->offXml, offFtr));
676 DMG_VALIDATE(pFtr->offXml < offFtr, ("offXml=%#RX64\n", pFtr->offXml));
677 DMG_VALIDATE(pFtr->cbXml > 128, ("cbXml=%#RX64\n", pFtr->cbXml));
678 DMG_VALIDATE(pFtr->cbXml < 10 * _1M, ("cbXml=%#RX64\n", pFtr->cbXml));
679 DMG_VALIDATE(pFtr->u32Type == DMGUDIF_TYPE_DEVICE || pFtr->u32Type == DMGUDIF_TYPE_PARTITION, ("u32Type=%RU32\n", pFtr->u32Type));
680 DMG_VALIDATE(pFtr->cSectors != 0, ("cSectors=%#RX64\n", pFtr->cSectors));
681 fRc &= dmgUdifCkSumIsValid(&pFtr->DataCkSum, "DataCkSum");
682 fRc &= dmgUdifCkSumIsValid(&pFtr->MasterCkSum, "MasterCkSum");
683
684 return fRc;
685}
686
687
688static bool dmgBlkxIsValid(PCDMGBLKX pBlkx)
689{
690 bool fRc = true;
691
692 fRc &= dmgUdifCkSumIsValid(&pBlkx->BlkxCkSum, "BlkxCkSum");
693 DMG_VALIDATE(pBlkx->u32Magic == DMGBLKX_MAGIC, ("u32Magic=%#RX32 u32MagicExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_MAGIC));
694 DMG_VALIDATE(pBlkx->u32Version == DMGBLKX_VERSION, ("u32Version=%#RX32 u32VersionExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_VERSION));
695
696 return fRc;
697}
698
699/**
700 * Swaps endian from host cpu to file.
701 * @param pId The structure.
702 */
703static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId)
704{
705 NOREF(pId);
706}
707
708
709/**
710 * Swaps endian from file to host cpu.
711 * @param pId The structure.
712 */
713static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId)
714{
715 dmgUdifIdHost2FileEndian(pId);
716}
717
718
719/**
720 * Swaps endian.
721 * @param pCkSum The structure.
722 * @param u32Kind Kind of the checksum (CRC32, none)
723 * @param cBits Size of the checksum in bits.
724 */
725static void dmgSwapEndianUdifCkSum(PDMGUDIFCKSUM pCkSum, uint32_t u32Kind, uint32_t cBits)
726{
727#ifdef RT_BIG_ENDIAN
728 NOREF(pCkSum);
729 NOREF(u32Kind);
730 NOREF(cBits);
731#else
732 switch (u32Kind)
733 {
734 case DMGUDIFCKSUM_NONE:
735 /* nothing to do here */
736 break;
737
738 case DMGUDIFCKSUM_CRC32:
739 Assert(cBits == 32);
740 pCkSum->u32Kind = RT_BSWAP_U32(pCkSum->u32Kind);
741 pCkSum->cBits = RT_BSWAP_U32(pCkSum->cBits);
742 pCkSum->uSum.au32[0] = RT_BSWAP_U32(pCkSum->uSum.au32[0]);
743 break;
744
745 default:
746 AssertMsgFailed(("%x\n", u32Kind));
747 break;
748 }
749 NOREF(cBits);
750#endif
751}
752
753
754#if 0 /* unused */
755/**
756 * Swaps endian from host cpu to file.
757 * @param pCkSum The structure.
758 */
759static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum)
760{
761 dmgSwapEndianUdifCkSum(pCkSum, pCkSum->u32Kind, pCkSum->cBits);
762}
763#endif
764
765
766/**
767 * Swaps endian from file to host cpu.
768 * @param pCkSum The structure.
769 */
770static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum)
771{
772 dmgSwapEndianUdifCkSum(pCkSum, RT_BE2H_U32(pCkSum->u32Kind), RT_BE2H_U32(pCkSum->cBits));
773}
774
775
776/**
777 * Validates an UDIF checksum structure.
778 *
779 * @returns true if valid, false and LogRel()s on failure.
780 * @param pCkSum The checksum structure.
781 * @param pszPrefix The message prefix.
782 * @remarks This does not check the checksummed data.
783 */
784static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix)
785{
786 bool fRc = true;
787
788 switch (pCkSum->u32Kind)
789 {
790 case DMGUDIFCKSUM_NONE:
791 DMG_VALIDATE(pCkSum->cBits == 0, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
792 break;
793
794 case DMGUDIFCKSUM_CRC32:
795 DMG_VALIDATE(pCkSum->cBits == 32, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
796 break;
797
798 default:
799 DMG_VALIDATE(0, ("%s: u32Kind=%#RX32\n", pszPrefix, pCkSum->u32Kind));
800 break;
801 }
802 return fRc;
803}
804
805
806/**
807 * Internal. Flush image data to disk.
808 */
809static int dmgFlushImage(PDMGIMAGE pThis)
810{
811 int rc = VINF_SUCCESS;
812
813 if ( pThis
814 && (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE)
815 && !(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
816 {
817 /** @todo handle writable files, update checksums etc. */
818 }
819
820 return rc;
821}
822
823
824/**
825 * Internal. Free all allocated space for representing an image except pThis,
826 * and optionally delete the image from disk.
827 */
828static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
829{
830 int rc = VINF_SUCCESS;
831
832 /* Freeing a never allocated image (e.g. because the open failed) is
833 * not signalled as an error. After all nothing bad happens. */
834 if (pThis)
835 {
836 RTVfsFileRelease(pThis->hDmgFileInXar);
837 pThis->hDmgFileInXar = NIL_RTVFSFILE;
838
839 RTVfsFsStrmRelease(pThis->hXarFss);
840 pThis->hXarFss = NIL_RTVFSFSSTREAM;
841
842 if (pThis->pStorage)
843 {
844 /* No point updating the file that is deleted anyway. */
845 if (!fDelete)
846 dmgFlushImage(pThis);
847
848 rc = vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
849 pThis->pStorage = NULL;
850 }
851
852 for (unsigned iRsrc = 0; iRsrc < RT_ELEMENTS(pThis->aRsrcs); iRsrc++)
853 for (unsigned i = 0; i < pThis->aRsrcs[iRsrc].cEntries; i++)
854 {
855 if (pThis->aRsrcs[iRsrc].aEntries[i].pbData)
856 {
857 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pbData);
858 pThis->aRsrcs[iRsrc].aEntries[i].pbData = NULL;
859 }
860 if (pThis->aRsrcs[iRsrc].aEntries[i].pszName)
861 {
862 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszName);
863 pThis->aRsrcs[iRsrc].aEntries[i].pszName = NULL;
864 }
865 if (pThis->aRsrcs[iRsrc].aEntries[i].pszCFName)
866 {
867 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
868 pThis->aRsrcs[iRsrc].aEntries[i].pszCFName = NULL;
869 }
870 }
871
872 if (fDelete && pThis->pszFilename)
873 vdIfIoIntFileDelete(pThis->pIfIoXxx, pThis->pszFilename);
874
875 if (pThis->pvDecompExtent)
876 {
877 RTMemFree(pThis->pvDecompExtent);
878 pThis->pvDecompExtent = NULL;
879 pThis->cbDecompExtent = 0;
880 }
881
882 if (pThis->paExtents)
883 {
884 RTMemFree(pThis->paExtents);
885 pThis->paExtents = NULL;
886 }
887 }
888
889 LogFlowFunc(("returns %Rrc\n", rc));
890 return rc;
891}
892
893
894#define STARTS_WITH(pszString, szStart) \
895 ( strncmp(pszString, szStart, sizeof(szStart) - 1) == 0 )
896
897#define STARTS_WITH_WORD(pszString, szWord) \
898 ( STARTS_WITH(pszString, szWord) \
899 && !RT_C_IS_ALNUM((pszString)[sizeof(szWord) - 1]) )
900
901#define SKIP_AHEAD(psz, szWord) \
902 do { \
903 (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
904 } while (0)
905
906#define REQUIRE_WORD(psz, szWord) \
907 do { \
908 if (!STARTS_WITH_WORD(psz, szWord)) \
909 return psz; \
910 (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
911 } while (0)
912
913#define REQUIRE_TAG(psz, szTag) \
914 do { \
915 if (!STARTS_WITH(psz, "<" szTag ">")) \
916 return psz; \
917 (psz) = RTStrStripL((psz) + sizeof("<" szTag ">") - 1); \
918 } while (0)
919
920#define REQUIRE_TAG_NO_STRIP(psz, szTag) \
921 do { \
922 if (!STARTS_WITH(psz, "<" szTag ">")) \
923 return psz; \
924 (psz) += sizeof("<" szTag ">") - 1; \
925 } while (0)
926
927#define REQUIRE_END_TAG(psz, szTag) \
928 do { \
929 if (!STARTS_WITH(psz, "</" szTag ">")) \
930 return psz; \
931 (psz) = RTStrStripL((psz) + sizeof("</" szTag ">") - 1); \
932 } while (0)
933
934
935/**
936 * Finds the next tag end.
937 *
938 * @returns Pointer to a '>' or '\0'.
939 * @param pszCur The current position.
940 */
941static const char *dmgXmlFindTagEnd(const char *pszCur)
942{
943 /* Might want to take quoted '>' into account? */
944 char ch;
945 while ((ch = *pszCur) != '\0' && ch != '>')
946 pszCur++;
947 return pszCur;
948}
949
950
951/**
952 * Finds the end tag.
953 *
954 * Does not deal with @verbatim<tag attr="1"/>@endverbatim style tags.
955 *
956 * @returns Pointer to the first char in the end tag. NULL if another tag
957 * was encountered first or if we hit the end of the file.
958 * @param ppszCur The current position (IN/OUT).
959 * @param pszTag The tag name.
960 */
961static const char *dmgXmlFindEndTag(const char **ppszCur, const char *pszTag)
962{
963 const char *psz = *ppszCur;
964 char ch;
965 while ((ch = *psz))
966 {
967 if (ch == '<')
968 {
969 size_t const cchTag = strlen(pszTag);
970 if ( psz[1] == '/'
971 && !memcmp(&psz[2], pszTag, cchTag)
972 && psz[2 + cchTag] == '>')
973 {
974 *ppszCur = psz + 2 + cchTag + 1;
975 return psz;
976 }
977 break;
978 }
979 psz++;
980 }
981 return NULL;
982}
983
984
985/**
986 * Reads a signed 32-bit value.
987 *
988 * @returns NULL on success, pointer to the offending text on failure.
989 * @param ppszCur The text position (IN/OUT).
990 * @param pi32 Where to store the value.
991 */
992static const char *dmgXmlParseS32(const char **ppszCur, int32_t *pi32)
993{
994 const char *psz = *ppszCur;
995
996 /*
997 * <string>-1</string>
998 */
999 REQUIRE_TAG_NO_STRIP(psz, "string");
1000
1001 char *pszNext;
1002 int rc = RTStrToInt32Ex(psz, &pszNext, 0, pi32);
1003 if (rc != VWRN_TRAILING_CHARS)
1004 return *ppszCur;
1005 psz = pszNext;
1006
1007 REQUIRE_END_TAG(psz, "string");
1008 *ppszCur = psz;
1009 return NULL;
1010}
1011
1012
1013/**
1014 * Reads an unsigned 32-bit value.
1015 *
1016 * @returns NULL on success, pointer to the offending text on failure.
1017 * @param ppszCur The text position (IN/OUT).
1018 * @param pu32 Where to store the value.
1019 */
1020static const char *dmgXmlParseU32(const char **ppszCur, uint32_t *pu32)
1021{
1022 const char *psz = *ppszCur;
1023
1024 /*
1025 * <string>0x00ff</string>
1026 */
1027 REQUIRE_TAG_NO_STRIP(psz, "string");
1028
1029 char *pszNext;
1030 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
1031 if (rc != VWRN_TRAILING_CHARS)
1032 return *ppszCur;
1033 psz = pszNext;
1034
1035 REQUIRE_END_TAG(psz, "string");
1036 *ppszCur = psz;
1037 return NULL;
1038}
1039
1040
1041/**
1042 * Reads a string value.
1043 *
1044 * @returns NULL on success, pointer to the offending text on failure.
1045 * @param ppszCur The text position (IN/OUT).
1046 * @param ppszString Where to store the pointer to the string. The caller
1047 * must free this using RTMemFree.
1048 */
1049static const char *dmgXmlParseString(const char **ppszCur, char **ppszString)
1050{
1051 const char *psz = *ppszCur;
1052
1053 /*
1054 * <string>Driver Descriptor Map (DDM : 0)</string>
1055 */
1056 REQUIRE_TAG_NO_STRIP(psz, "string");
1057
1058 const char *pszStart = psz;
1059 const char *pszEnd = dmgXmlFindEndTag(&psz, "string");
1060 if (!pszEnd)
1061 return *ppszCur;
1062 psz = RTStrStripL(psz);
1063
1064 *ppszString = (char *)RTMemDupEx(pszStart, pszEnd - pszStart, 1);
1065 if (!*ppszString)
1066 return *ppszCur;
1067
1068 *ppszCur = psz;
1069 return NULL;
1070}
1071
1072
1073/**
1074 * Parses the BASE-64 coded data tags.
1075 *
1076 * @returns NULL on success, pointer to the offending text on failure.
1077 * @param ppszCur The text position (IN/OUT).
1078 * @param ppbData Where to store the pointer to the data we've read. The
1079 * caller must free this using RTMemFree.
1080 * @param pcbData The number of bytes we're returning.
1081 */
1082static const char *dmgXmlParseData(const char **ppszCur, uint8_t **ppbData, size_t *pcbData)
1083{
1084 const char *psz = *ppszCur;
1085
1086 /*
1087 * <data> AAAAA... </data>
1088 */
1089 REQUIRE_TAG(psz, "data");
1090
1091 const char *pszStart = psz;
1092 ssize_t cbData = RTBase64DecodedSize(pszStart, (char **)&psz);
1093 if (cbData == -1)
1094 return *ppszCur;
1095
1096 REQUIRE_END_TAG(psz, "data");
1097
1098 *ppbData = (uint8_t *)RTMemAlloc(cbData);
1099 if (!*ppbData)
1100 return *ppszCur;
1101 char *pszIgnored;
1102 int rc = RTBase64Decode(pszStart, *ppbData, cbData, pcbData, &pszIgnored);
1103 if (RT_FAILURE(rc))
1104 {
1105 RTMemFree(*ppbData);
1106 *ppbData = NULL;
1107 return *ppszCur;
1108 }
1109
1110 *ppszCur = psz;
1111 return NULL;
1112}
1113
1114
1115/**
1116 * Parses the XML resource-fork in a rather presumptive manner.
1117 *
1118 * This function is supposed to construct the DMG::aRsrcs instance data
1119 * parts.
1120 *
1121 * @returns NULL on success, pointer to the problematic text on failure.
1122 * @param pThis The DMG instance data.
1123 * @param pszXml The XML text to parse, UTF-8.
1124 */
1125static const char *dmgOpenXmlToRsrc(PDMGIMAGE pThis, char const *pszXml)
1126{
1127 const char *psz = pszXml;
1128
1129 /*
1130 * Verify the ?xml, !DOCTYPE and plist tags.
1131 */
1132 SKIP_AHEAD(psz, "");
1133
1134 /* <?xml version="1.0" encoding="UTF-8"?> */
1135 REQUIRE_WORD(psz, "<?xml");
1136 while (*psz != '?')
1137 {
1138 if (!*psz)
1139 return psz;
1140 if (STARTS_WITH_WORD(psz, "version="))
1141 {
1142 SKIP_AHEAD(psz, "version=");
1143 REQUIRE_WORD(psz, "\"1.0\"");
1144 }
1145 else if (STARTS_WITH_WORD(psz, "encoding="))
1146 {
1147 SKIP_AHEAD(psz, "encoding=");
1148 REQUIRE_WORD(psz, "\"UTF-8\"");
1149 }
1150 else
1151 return psz;
1152 }
1153 SKIP_AHEAD(psz, "?>");
1154
1155 /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
1156 REQUIRE_WORD(psz, "<!DOCTYPE");
1157 REQUIRE_WORD(psz, "plist");
1158 REQUIRE_WORD(psz, "PUBLIC");
1159 psz = dmgXmlFindTagEnd(psz);
1160 REQUIRE_WORD(psz, ">");
1161
1162 /* <plist version="1.0"> */
1163 REQUIRE_WORD(psz, "<plist");
1164 REQUIRE_WORD(psz, "version=");
1165 REQUIRE_WORD(psz, "\"1.0\"");
1166 REQUIRE_WORD(psz, ">");
1167
1168 /*
1169 * Descend down to the 'resource-fork' dictionary.
1170 * ASSUME it's the only top level dictionary.
1171 */
1172 /* <dict> <key>resource-fork</key> */
1173 REQUIRE_TAG(psz, "dict");
1174 REQUIRE_WORD(psz, "<key>resource-fork</key>");
1175
1176 /*
1177 * Parse the keys in the resource-fork dictionary.
1178 * ASSUME that there are just two, 'blkx' and 'plst'.
1179 */
1180 REQUIRE_TAG(psz, "dict");
1181 while (!STARTS_WITH_WORD(psz, "</dict>"))
1182 {
1183 /*
1184 * Parse the key and Create the resource-fork entry.
1185 */
1186 unsigned iRsrc;
1187 if (STARTS_WITH_WORD(psz, "<key>blkx</key>"))
1188 {
1189 REQUIRE_WORD(psz, "<key>blkx</key>");
1190 iRsrc = DMG_RSRC_IDX_BLKX;
1191 strcpy(&pThis->aRsrcs[iRsrc].szName[0], "blkx");
1192 }
1193 else if (STARTS_WITH_WORD(psz, "<key>plst</key>"))
1194 {
1195 REQUIRE_WORD(psz, "<key>plst</key>");
1196 iRsrc = DMG_RSRC_IDX_PLST;
1197 strcpy(&pThis->aRsrcs[iRsrc].szName[0], "plst");
1198 }
1199 else
1200 {
1201 SKIP_AHEAD(psz, "</array>");
1202 continue;
1203 }
1204
1205
1206 /*
1207 * Descend into the array and add the elements to the resource entry.
1208 */
1209 /* <array> */
1210 REQUIRE_TAG(psz, "array");
1211 while (!STARTS_WITH_WORD(psz, "</array>"))
1212 {
1213 REQUIRE_TAG(psz, "dict");
1214 uint32_t i = pThis->aRsrcs[iRsrc].cEntries;
1215 if (i == RT_ELEMENTS(pThis->aRsrcs[iRsrc].aEntries))
1216 return psz;
1217
1218 while (!STARTS_WITH_WORD(psz, "</dict>"))
1219 {
1220
1221 /* switch on the key. */
1222 const char *pszErr;
1223 if (STARTS_WITH_WORD(psz, "<key>Attributes</key>"))
1224 {
1225 REQUIRE_WORD(psz, "<key>Attributes</key>");
1226 pszErr = dmgXmlParseU32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].fAttributes);
1227 }
1228 else if (STARTS_WITH_WORD(psz, "<key>ID</key>"))
1229 {
1230 REQUIRE_WORD(psz, "<key>ID</key>");
1231 pszErr = dmgXmlParseS32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].iId);
1232 }
1233 else if (STARTS_WITH_WORD(psz, "<key>Name</key>"))
1234 {
1235 REQUIRE_WORD(psz, "<key>Name</key>");
1236 pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszName);
1237 }
1238 else if (STARTS_WITH_WORD(psz, "<key>CFName</key>"))
1239 {
1240 REQUIRE_WORD(psz, "<key>CFName</key>");
1241 pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
1242 }
1243 else if (STARTS_WITH_WORD(psz, "<key>Data</key>"))
1244 {
1245 REQUIRE_WORD(psz, "<key>Data</key>");
1246 pszErr = dmgXmlParseData(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pbData, &pThis->aRsrcs[iRsrc].aEntries[i].cbData);
1247 }
1248 else
1249 pszErr = psz;
1250 if (pszErr)
1251 return pszErr;
1252 } /* while not </dict> */
1253 REQUIRE_END_TAG(psz, "dict");
1254
1255 pThis->aRsrcs[iRsrc].cEntries++;
1256 } /* while not </array> */
1257 REQUIRE_END_TAG(psz, "array");
1258
1259 } /* while not </dict> */
1260 REQUIRE_END_TAG(psz, "dict");
1261
1262 /*
1263 * ASSUMING there is only the 'resource-fork', we'll now see the end of
1264 * the outer dict, plist and text.
1265 */
1266 /* </dict> </plist> */
1267 REQUIRE_END_TAG(psz, "dict");
1268 REQUIRE_END_TAG(psz, "plist");
1269
1270 /* the end */
1271 if (*psz)
1272 return psz;
1273
1274 return NULL;
1275}
1276
1277#undef REQUIRE_END_TAG
1278#undef REQUIRE_TAG_NO_STRIP
1279#undef REQUIRE_TAG
1280#undef REQUIRE_WORD
1281#undef SKIP_AHEAD
1282#undef STARTS_WITH_WORD
1283#undef STARTS_WITH
1284
1285/**
1286 * Returns the data attached to a resource.
1287 *
1288 * @returns VBox status code.
1289 * @param pThis The DMG instance data.
1290 * @param pcszRsrcName Name of the resource to get.
1291 * @param ppcRsrc Where to store the pointer to the resource data on success.
1292 */
1293static int dmgGetRsrcData(PDMGIMAGE pThis, const char *pcszRsrcName,
1294 PCDMGUDIFRSRCARRAY *ppcRsrc)
1295{
1296 int rc = VERR_NOT_FOUND;
1297
1298 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aRsrcs); i++)
1299 {
1300 if (!strcmp(pThis->aRsrcs[i].szName, pcszRsrcName))
1301 {
1302 *ppcRsrc = &pThis->aRsrcs[i];
1303 rc = VINF_SUCCESS;
1304 break;
1305 }
1306 }
1307
1308 return rc;
1309}
1310
1311/**
1312 * Creates a new extent from the given blkx descriptor.
1313 *
1314 * @returns VBox status code.
1315 * @param pThis DMG instance data.
1316 * @param uSectorPart First sector the partition owning the blkx descriptor has.
1317 * @param pBlkxDesc The blkx descriptor.
1318 */
1319static int dmgExtentCreateFromBlkxDesc(PDMGIMAGE pThis, uint64_t uSectorPart, PDMGBLKXDESC pBlkxDesc)
1320{
1321 int rc = VINF_SUCCESS;
1322 DMGEXTENTTYPE enmExtentTypeNew;
1323 PDMGEXTENT pExtentNew = NULL;
1324
1325 if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_RAW)
1326 enmExtentTypeNew = DMGEXTENTTYPE_RAW;
1327 else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_IGNORE)
1328 enmExtentTypeNew = DMGEXTENTTYPE_ZERO;
1329 else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_ZLIB)
1330 enmExtentTypeNew = DMGEXTENTTYPE_COMP_ZLIB;
1331 else
1332 {
1333 AssertMsgFailed(("This method supports only raw or zero extents!\n"));
1334 return VERR_NOT_SUPPORTED;
1335 }
1336
1337 /** @todo Merge raw extents if possible to save memory. */
1338#if 0
1339 pExtentNew = pThis->pExtentLast;
1340 if ( pExtentNew
1341 && pExtentNew->enmType == enmExtentTypeNew
1342 && enmExtentTypeNew == DMGEXTENTTYPE_RAW
1343 && pExtentNew->uSectorExtent + pExtentNew->cSectorsExtent == offDevice + pBlkxDesc->u64SectorStart * DMG_SECTOR_SIZE;
1344 && pExtentNew->offFileStart + pExtentNew->cbExtent == pBlkxDesc->offData)
1345 {
1346 /* Increase the last extent. */
1347 pExtentNew->cbExtent += pBlkxDesc->cbData;
1348 }
1349 else
1350#endif
1351 {
1352 if (pThis->cExtentsMax == pThis->cExtents)
1353 {
1354 pThis->cExtentsMax += 64;
1355
1356 /* Increase the array. */
1357 PDMGEXTENT paExtentsNew = (PDMGEXTENT)RTMemRealloc(pThis->paExtents, sizeof(DMGEXTENT) * pThis->cExtentsMax);
1358 if (!paExtentsNew)
1359 {
1360 rc = VERR_NO_MEMORY;
1361 pThis->cExtentsMax -= 64;
1362 }
1363 else
1364 pThis->paExtents = paExtentsNew;
1365 }
1366
1367 if (RT_SUCCESS(rc))
1368 {
1369 pExtentNew = &pThis->paExtents[pThis->cExtents++];
1370
1371 pExtentNew->enmType = enmExtentTypeNew;
1372 pExtentNew->uSectorExtent = uSectorPart + pBlkxDesc->u64SectorStart;
1373 pExtentNew->cSectorsExtent = pBlkxDesc->u64SectorCount;
1374 pExtentNew->offFileStart = pBlkxDesc->offData;
1375 pExtentNew->cbFile = pBlkxDesc->cbData;
1376 }
1377 }
1378
1379 return rc;
1380}
1381
1382/**
1383 * Find the extent for the given sector number.
1384 */
1385static PDMGEXTENT dmgExtentGetFromOffset(PDMGIMAGE pThis, uint64_t uSector)
1386{
1387 /*
1388 * We assume that the array is ordered from lower to higher sector
1389 * numbers.
1390 * This makes it possible to bisect the array to find the extent
1391 * faster than using a linked list.
1392 */
1393 PDMGEXTENT pExtent = NULL;
1394 unsigned idxCur = pThis->idxExtentLast;
1395 unsigned idxMax = pThis->cExtents;
1396 unsigned idxMin = 0;
1397
1398 while (idxMin < idxMax)
1399 {
1400 PDMGEXTENT pExtentCur = &pThis->paExtents[idxCur];
1401
1402 /* Determine the search direction. */
1403 if (uSector < pExtentCur->uSectorExtent)
1404 {
1405 /* Search left from the current extent. */
1406 idxMax = idxCur;
1407 }
1408 else if (uSector >= pExtentCur->uSectorExtent + pExtentCur->cSectorsExtent)
1409 {
1410 /* Search right from the current extent. */
1411 idxMin = idxCur;
1412 }
1413 else
1414 {
1415 /* The sector lies in the extent, stop searching. */
1416 pExtent = pExtentCur;
1417 break;
1418 }
1419
1420 idxCur = idxMin + (idxMax - idxMin) / 2;
1421 }
1422
1423 if (pExtent)
1424 pThis->idxExtentLast = idxCur;
1425
1426 return pExtent;
1427}
1428
1429/**
1430 * Goes through the BLKX structure and creates the necessary extents.
1431 */
1432static int dmgBlkxParse(PDMGIMAGE pThis, PDMGBLKX pBlkx)
1433{
1434 int rc = VINF_SUCCESS;
1435 PDMGBLKXDESC pBlkxDesc = (PDMGBLKXDESC)(pBlkx + 1);
1436
1437 for (unsigned i = 0; i < pBlkx->cBlocksRunCount; i++)
1438 {
1439 dmgBlkxDescFile2HostEndian(pBlkxDesc);
1440
1441 switch (pBlkxDesc->u32Type)
1442 {
1443 case DMGBLKXDESC_TYPE_RAW:
1444 case DMGBLKXDESC_TYPE_IGNORE:
1445 case DMGBLKXDESC_TYPE_ZLIB:
1446 {
1447 rc = dmgExtentCreateFromBlkxDesc(pThis, pBlkx->cSectornumberFirst, pBlkxDesc);
1448 break;
1449 }
1450 case DMGBLKXDESC_TYPE_COMMENT:
1451 case DMGBLKXDESC_TYPE_TERMINATOR:
1452 break;
1453 default:
1454 rc = VERR_VD_DMG_INVALID_HEADER;
1455 break;
1456 }
1457
1458 if ( pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_TERMINATOR
1459 || RT_FAILURE(rc))
1460 break;
1461
1462 pBlkxDesc++;
1463 }
1464
1465 return rc;
1466}
1467
1468
1469/**
1470 * Worker for dmgOpenImage that tries to open a DMG inside a XAR file.
1471 *
1472 * We'll select the first .dmg inside the archive that we can get a file
1473 * interface to.
1474 *
1475 * @returns VBox status code.
1476 * @param fOpen Flags for defining the open type.
1477 * @param pVDIfIoInt The internal VD I/O interface to use.
1478 * @param pvStorage The storage pointer that goes with @a pVDIfsIo.
1479 * @param pszFilename The input filename, optional.
1480 * @param phXarFss Where to return the XAR file system stream handle on
1481 * success
1482 * @param phDmgFileInXar Where to return the VFS handle to the DMG file
1483 * within the XAR image on success.
1484 *
1485 * @remarks Not using the PDMGIMAGE structure directly here because the function
1486 * is being in serveral places.
1487 */
1488static int dmgOpenImageWithinXar(uint32_t fOpen, PVDINTERFACEIOINT pVDIfIoInt, void *pvStorage, const char *pszFilename,
1489 PRTVFSFSSTREAM phXarFss, PRTVFSFILE phDmgFileInXar)
1490{
1491 /*
1492 * Open the XAR file stream.
1493 */
1494 RTVFSFILE hVfsFile;
1495#ifdef VBOX_WITH_DIRECT_XAR_ACCESS
1496 int rc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
1497#else
1498 int rc = VDIfCreateVfsFile(NULL, pVDIfIoInt, pvStorage, fOpen, &hVfsFile);
1499#endif
1500 if (RT_FAILURE(rc))
1501 return rc;
1502
1503 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
1504 RTVfsFileRelease(hVfsFile);
1505
1506 RTVFSFSSTREAM hXarFss;
1507 rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &hXarFss);
1508 RTVfsIoStrmRelease(hVfsIos);
1509 if (RT_FAILURE(rc))
1510 return rc;
1511
1512 /*
1513 * Look for a DMG in the stream that we can use.
1514 */
1515 for (;;)
1516 {
1517 char *pszName;
1518 RTVFSOBJTYPE enmType;
1519 RTVFSOBJ hVfsObj;
1520 rc = RTVfsFsStrmNext(hXarFss, &pszName, &enmType, &hVfsObj);
1521 if (RT_FAILURE(rc))
1522 break;
1523
1524 /* It must be a file object so it can be seeked, this also implies that
1525 it's uncompressed. Then it must have the .dmg suffix. */
1526 if (enmType == RTVFSOBJTYPE_FILE)
1527 {
1528 size_t cchName = strlen(pszName);
1529 const char *pszSuff = pszName + cchName - 4;
1530 if ( cchName >= 4
1531 && pszSuff[0] == '.'
1532 && (pszSuff[1] == 'd' || pszSuff[1] == 'D')
1533 && (pszSuff[2] == 'm' || pszSuff[2] == 'M')
1534 && (pszSuff[3] == 'g' || pszSuff[3] == 'G'))
1535 {
1536 RTVFSFILE hDmgFileInXar = RTVfsObjToFile(hVfsObj);
1537 AssertBreakStmt(hDmgFileInXar != NIL_RTVFSFILE, rc = VERR_INTERNAL_ERROR_3);
1538
1539 if (pszFilename)
1540 DMG_PRINTF(("DMG: Using '%s' within XAR file '%s'...\n", pszName, pszFilename));
1541 *phXarFss = hXarFss;
1542 *phDmgFileInXar = hDmgFileInXar;
1543
1544 RTStrFree(pszName);
1545 RTVfsObjRelease(hVfsObj);
1546
1547 return VINF_SUCCESS;
1548 }
1549 }
1550
1551 /* Release the current return values. */
1552 RTStrFree(pszName);
1553 RTVfsObjRelease(hVfsObj);
1554 }
1555
1556 /* Not found or some kind of error. */
1557 RTVfsFsStrmRelease(hXarFss);
1558 if (rc == VERR_EOF)
1559 rc = VERR_VD_DMG_NOT_FOUND_INSIDE_XAR;
1560 AssertStmt(RT_FAILURE_NP(rc), rc = VERR_INTERNAL_ERROR_4);
1561 return rc;
1562}
1563
1564
1565/**
1566 * Worker for dmgOpen that reads in and validates all the necessary
1567 * structures from the image.
1568 *
1569 * @returns VBox status code.
1570 * @param pThis The DMG instance data.
1571 * @param uOpenFlags Flags for defining the open type.
1572 */
1573static DECLCALLBACK(int) dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
1574{
1575 pThis->uOpenFlags = uOpenFlags;
1576
1577 pThis->pIfError = VDIfErrorGet(pThis->pVDIfsDisk);
1578 pThis->pIfIoXxx = VDIfIoIntGet(pThis->pVDIfsImage);
1579 pThis->hDmgFileInXar = NIL_RTVFSFILE;
1580 pThis->hXarFss = NIL_RTVFSFSSTREAM;
1581 AssertPtrReturn(pThis->pIfIoXxx, VERR_INVALID_PARAMETER);
1582
1583 int rc = vdIfIoIntFileOpen(pThis->pIfIoXxx, pThis->pszFilename,
1584 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
1585 &pThis->pStorage);
1586 if (RT_FAILURE(rc))
1587 {
1588 /* Do NOT signal an appropriate error here, as the VD layer has the
1589 * choice of retrying the open if it failed. */
1590 return rc;
1591 }
1592
1593 /*
1594 * Check for XAR archive.
1595 */
1596 uint32_t u32XarMagic;
1597 rc = dmgWrapFileReadSync(pThis, 0, &u32XarMagic, sizeof(u32XarMagic));
1598 if (RT_FAILURE(rc))
1599 return rc;
1600 if (u32XarMagic == XAR_HEADER_MAGIC)
1601 {
1602 rc = dmgOpenImageWithinXar(VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
1603 pThis->pIfIoXxx,
1604 pThis->pStorage,
1605 pThis->pszFilename,
1606 &pThis->hXarFss, &pThis->hDmgFileInXar);
1607 if (RT_FAILURE(rc))
1608 return rc;
1609#ifdef VBOX_WITH_DIRECT_XAR_ACCESS
1610 vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
1611 pThis->pStorage = NULL;
1612#endif
1613 }
1614#if 0 /* This is for testing whether the VFS wrappers actually works. */
1615 else
1616 {
1617 rc = RTVfsFileOpenNormal(pThis->pszFilename, VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
1618 &pThis->hDmgFileInXar);
1619 if (RT_FAILURE(rc))
1620 return rc;
1621 vdIfIoIntFileClose(pThis->pIfIoXxx, pThis->pStorage);
1622 pThis->pStorage = NULL;
1623 }
1624#endif
1625
1626 /*
1627 * Read the footer.
1628 */
1629 rc = dmgWrapFileGetSize(pThis, &pThis->cbFile);
1630 if (RT_FAILURE(rc))
1631 return rc;
1632 if (pThis->cbFile < 1024)
1633 return VERR_VD_DMG_INVALID_HEADER;
1634 rc = dmgWrapFileReadSync(pThis, pThis->cbFile - sizeof(pThis->Ftr), &pThis->Ftr, sizeof(pThis->Ftr));
1635 if (RT_FAILURE(rc))
1636 return rc;
1637 dmgUdifFtrFile2HostEndian(&pThis->Ftr);
1638
1639 /*
1640 * Do we recognize the footer structure? If so, is it valid?
1641 */
1642 if (pThis->Ftr.u32Magic != DMGUDIF_MAGIC)
1643 return VERR_VD_DMG_INVALID_HEADER;
1644 if (pThis->Ftr.u32Version != DMGUDIF_VER_CURRENT)
1645 return VERR_VD_DMG_INVALID_HEADER;
1646 if (pThis->Ftr.cbFooter != sizeof(pThis->Ftr))
1647 return VERR_VD_DMG_INVALID_HEADER;
1648
1649 if (!dmgUdifFtrIsValid(&pThis->Ftr, pThis->cbFile - sizeof(pThis->Ftr)))
1650 {
1651 DMG_PRINTF(("Bad DMG: '%s' cbFile=%RTfoff\n", pThis->pszFilename, pThis->cbFile));
1652 return VERR_VD_DMG_INVALID_HEADER;
1653 }
1654
1655 pThis->cbSize = pThis->Ftr.cSectors * DMG_SECTOR_SIZE;
1656
1657 /*
1658 * Read and parse the XML portion.
1659 */
1660 size_t cchXml = (size_t)pThis->Ftr.cbXml;
1661 char *pszXml = (char *)RTMemAlloc(cchXml + 1);
1662 if (!pszXml)
1663 return VERR_NO_MEMORY;
1664 rc = dmgWrapFileReadSync(pThis, pThis->Ftr.offXml, pszXml, cchXml);
1665 if (RT_SUCCESS(rc))
1666 {
1667 pszXml[cchXml] = '\0';
1668 const char *pszError = dmgOpenXmlToRsrc(pThis, pszXml);
1669 if (!pszError)
1670 {
1671 PCDMGUDIFRSRCARRAY pRsrcBlkx = NULL;
1672
1673 rc = dmgGetRsrcData(pThis, "blkx", &pRsrcBlkx);
1674 if (RT_SUCCESS(rc))
1675 {
1676 for (unsigned idxBlkx = 0; idxBlkx < pRsrcBlkx->cEntries; idxBlkx++)
1677 {
1678 PDMGBLKX pBlkx = NULL;
1679
1680 if (pRsrcBlkx->aEntries[idxBlkx].cbData < sizeof(DMGBLKX))
1681 {
1682 rc = VERR_VD_DMG_INVALID_HEADER;
1683 break;
1684 }
1685
1686 pBlkx = (PDMGBLKX)RTMemAllocZ(pRsrcBlkx->aEntries[idxBlkx].cbData);
1687 if (!pBlkx)
1688 {
1689 rc = VERR_NO_MEMORY;
1690 break;
1691 }
1692
1693 memcpy(pBlkx, pRsrcBlkx->aEntries[idxBlkx].pbData, pRsrcBlkx->aEntries[idxBlkx].cbData);
1694
1695 dmgBlkxFile2HostEndian(pBlkx);
1696
1697 if ( dmgBlkxIsValid(pBlkx)
1698 && pRsrcBlkx->aEntries[idxBlkx].cbData == pBlkx->cBlocksRunCount * sizeof(DMGBLKXDESC) + sizeof(DMGBLKX))
1699 rc = dmgBlkxParse(pThis, pBlkx);
1700 else
1701 rc = VERR_VD_DMG_INVALID_HEADER;
1702
1703 RTMemFree(pBlkx);
1704
1705 if (RT_FAILURE(rc))
1706 break;
1707 }
1708 }
1709 else
1710 rc = VERR_VD_DMG_INVALID_HEADER;
1711 }
1712 else
1713 {
1714 DMG_PRINTF(("**** XML DUMP BEGIN ***\n%s\n**** XML DUMP END ****\n", pszXml));
1715 DMG_PRINTF(("**** Bad XML at %#lx (%lu) ***\n%.256s\n**** Bad XML END ****\n",
1716 (unsigned long)(pszError - pszXml), (unsigned long)(pszError - pszXml), pszError));
1717 rc = VERR_VD_DMG_XML_PARSE_ERROR;
1718 }
1719 }
1720 RTMemFree(pszXml);
1721
1722 if (RT_SUCCESS(rc))
1723 {
1724 PVDREGIONDESC pRegion = &pThis->RegionList.aRegions[0];
1725 pThis->RegionList.fFlags = 0;
1726 pThis->RegionList.cRegions = 1;
1727
1728 pRegion->offRegion = 0; /* Disk start. */
1729 pRegion->cbBlock = 2048;
1730 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
1731 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
1732 pRegion->cbData = 2048;
1733 pRegion->cbMetadata = 0;
1734 pRegion->cRegionBlocksOrBytes = pThis->cbSize;
1735 }
1736 else
1737 dmgFreeImage(pThis, false);
1738 return rc;
1739}
1740
1741
1742/** @interface_method_impl{VDIMAGEBACKEND,pfnProbe} */
1743static DECLCALLBACK(int) dmgProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1744 PVDINTERFACE pVDIfsImage, VDTYPE enmDesiredType, VDTYPE *penmType)
1745{
1746 RT_NOREF(pVDIfsDisk, enmDesiredType);
1747 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
1748 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
1749
1750 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1751 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1752
1753 /*
1754 * Open the file and check for XAR.
1755 */
1756 PVDIOSTORAGE pStorage = NULL;
1757 int rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1758 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY, false /* fCreate */),
1759 &pStorage);
1760 if (RT_FAILURE(rc))
1761 {
1762 LogFlowFunc(("returns %Rrc (error opening file)\n", rc));
1763 return rc;
1764 }
1765
1766 /*
1767 * Check for XAR file.
1768 */
1769 RTVFSFSSTREAM hXarFss = NIL_RTVFSFSSTREAM;
1770 RTVFSFILE hDmgFileInXar = NIL_RTVFSFILE;
1771 uint32_t u32XarMagic;
1772 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &u32XarMagic, sizeof(u32XarMagic));
1773 if ( RT_SUCCESS(rc)
1774 && u32XarMagic == XAR_HEADER_MAGIC)
1775 {
1776 rc = dmgOpenImageWithinXar(RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE,
1777 pIfIo, pStorage, pszFilename,
1778 &hXarFss, &hDmgFileInXar);
1779 if (RT_FAILURE(rc))
1780 return rc;
1781 }
1782
1783 /*
1784 * Read the DMG footer.
1785 */
1786 uint64_t cbFile;
1787 if (hDmgFileInXar == NIL_RTVFSFILE)
1788 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1789 else
1790 rc = RTVfsFileGetSize(hDmgFileInXar, &cbFile);
1791 if ( RT_SUCCESS(rc)
1792 && cbFile >= sizeof(DMGUDIF))
1793 {
1794 DMGUDIF Ftr;
1795 uint64_t offFtr = cbFile - sizeof(Ftr);
1796 if (hDmgFileInXar == NIL_RTVFSFILE)
1797 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offFtr, &Ftr, sizeof(Ftr));
1798 else
1799 rc = RTVfsFileReadAt(hDmgFileInXar, offFtr, &Ftr, sizeof(Ftr), NULL);
1800 if (RT_SUCCESS(rc))
1801 {
1802 /*
1803 * Do we recognize this stuff? Does it look valid?
1804 */
1805 if ( Ftr.u32Magic == RT_H2BE_U32_C(DMGUDIF_MAGIC)
1806 && Ftr.u32Version == RT_H2BE_U32_C(DMGUDIF_VER_CURRENT)
1807 && Ftr.cbFooter == RT_H2BE_U32_C(sizeof(Ftr)))
1808 {
1809 dmgUdifFtrFile2HostEndian(&Ftr);
1810 if (dmgUdifFtrIsValid(&Ftr, offFtr))
1811 {
1812 rc = VINF_SUCCESS;
1813 *penmType = VDTYPE_OPTICAL_DISC;
1814 }
1815 else
1816 {
1817 DMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
1818 rc = VERR_VD_DMG_INVALID_HEADER;
1819 }
1820 }
1821 else
1822 rc = VERR_VD_DMG_INVALID_HEADER;
1823 }
1824 else
1825 rc = VERR_VD_DMG_INVALID_HEADER;
1826 }
1827 else
1828 rc = VERR_VD_DMG_INVALID_HEADER;
1829
1830 /* Clean up. */
1831 RTVfsFileRelease(hDmgFileInXar);
1832 RTVfsFsStrmRelease(hXarFss);
1833 vdIfIoIntFileClose(pIfIo, pStorage);
1834
1835 LogFlowFunc(("returns %Rrc\n", rc));
1836 return rc;
1837}
1838
1839/** @interface_method_impl{VDIMAGEBACKEND,pfnOpen} */
1840static DECLCALLBACK(int) dmgOpen(const char *pszFilename, unsigned uOpenFlags,
1841 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1842 VDTYPE enmType, void **ppBackendData)
1843{
1844 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1845
1846 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
1847
1848 /* Check open flags. All valid flags are (in principle) supported. */
1849 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1850
1851 /* Check remaining arguments. */
1852 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1853 AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
1854
1855 /*
1856 * Reject combinations we don't currently support.
1857 *
1858 * There is no point in being paranoid about the input here as we're just a
1859 * simple backend and can expect the caller to be the only user and already
1860 * have validate what it passes thru to us.
1861 */
1862 if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1863 || (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
1864 {
1865 LogFlowFunc(("Unsupported flag(s): %#x\n", uOpenFlags));
1866 return VERR_INVALID_PARAMETER;
1867 }
1868
1869 /*
1870 * Create the basic instance data structure and open the file,
1871 * then hand it over to a worker function that does all the rest.
1872 */
1873 int rc = VERR_NO_MEMORY;
1874 PDMGIMAGE pThis = (PDMGIMAGE)RTMemAllocZ(RT_UOFFSETOF(DMGIMAGE, RegionList.aRegions[1]));
1875 if (pThis)
1876 {
1877 pThis->pszFilename = pszFilename;
1878 pThis->pStorage = NULL;
1879 pThis->pVDIfsDisk = pVDIfsDisk;
1880 pThis->pVDIfsImage = pVDIfsImage;
1881
1882 rc = dmgOpenImage(pThis, uOpenFlags);
1883 if (RT_SUCCESS(rc))
1884 *ppBackendData = pThis;
1885 else
1886 RTMemFree(pThis);
1887 }
1888
1889 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1890 return rc;
1891}
1892
1893/** @interface_method_impl{VDIMAGEBACKEND,pfnCreate} */
1894static DECLCALLBACK(int) dmgCreate(const char *pszFilename, uint64_t cbSize,
1895 unsigned uImageFlags, const char *pszComment,
1896 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1897 PCRTUUID pUuid, unsigned uOpenFlags,
1898 unsigned uPercentStart, unsigned uPercentSpan,
1899 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1900 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1901 void **ppBackendData)
1902{
1903 RT_NOREF8(pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags);
1904 RT_NOREF7(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData);
1905 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p",
1906 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1907 int rc = VERR_NOT_SUPPORTED;
1908
1909 LogFlowFunc(("returns %Rrc\n", rc));
1910 return rc;
1911}
1912
1913/** @interface_method_impl{VDIMAGEBACKEND,pfnRename} */
1914static DECLCALLBACK(int) dmgRename(void *pBackendData, const char *pszFilename)
1915{
1916 RT_NOREF2(pBackendData, pszFilename);
1917 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1918 int rc = VERR_NOT_SUPPORTED;
1919
1920 LogFlowFunc(("returns %Rrc\n", rc));
1921 return rc;
1922}
1923
1924/** @interface_method_impl{VDIMAGEBACKEND,pfnClose} */
1925static DECLCALLBACK(int) dmgClose(void *pBackendData, bool fDelete)
1926{
1927 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1928 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
1929
1930 int rc = dmgFreeImage(pThis, fDelete);
1931 RTMemFree(pThis);
1932
1933 LogFlowFunc(("returns %Rrc\n", rc));
1934 return rc;
1935}
1936
1937/** @interface_method_impl{VDIMAGEBACKEND,pfnRead} */
1938static DECLCALLBACK(int) dmgRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1939 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1940{
1941 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1942 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1943 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
1944 PDMGEXTENT pExtent = NULL;
1945 int rc = VINF_SUCCESS;
1946
1947 AssertPtr(pThis);
1948 Assert(uOffset % DMG_SECTOR_SIZE == 0);
1949 Assert(cbToRead % DMG_SECTOR_SIZE == 0);
1950
1951 if ( uOffset + cbToRead > pThis->cbSize
1952 || cbToRead == 0)
1953 {
1954 LogFlowFunc(("returns VERR_INVALID_PARAMETER\n"));
1955 return VERR_INVALID_PARAMETER;
1956 }
1957
1958 pExtent = dmgExtentGetFromOffset(pThis, DMG_BYTE2BLOCK(uOffset));
1959
1960 if (pExtent)
1961 {
1962 uint64_t uExtentRel = DMG_BYTE2BLOCK(uOffset) - pExtent->uSectorExtent;
1963
1964 /* Remain in this extent. */
1965 cbToRead = RT_MIN(cbToRead, DMG_BLOCK2BYTE(pExtent->cSectorsExtent - uExtentRel));
1966
1967 switch (pExtent->enmType)
1968 {
1969 case DMGEXTENTTYPE_RAW:
1970 {
1971 rc = dmgWrapFileReadUser(pThis, pExtent->offFileStart + DMG_BLOCK2BYTE(uExtentRel), pIoCtx, cbToRead);
1972 break;
1973 }
1974 case DMGEXTENTTYPE_ZERO:
1975 {
1976 vdIfIoIntIoCtxSet(pThis->pIfIoXxx, pIoCtx, 0, cbToRead);
1977 break;
1978 }
1979 case DMGEXTENTTYPE_COMP_ZLIB:
1980 {
1981 if (pThis->pExtentDecomp != pExtent)
1982 {
1983 if (DMG_BLOCK2BYTE(pExtent->cSectorsExtent) > pThis->cbDecompExtent)
1984 {
1985 if (RT_LIKELY(pThis->pvDecompExtent))
1986 RTMemFree(pThis->pvDecompExtent);
1987
1988 pThis->pvDecompExtent = RTMemAllocZ(DMG_BLOCK2BYTE(pExtent->cSectorsExtent));
1989 if (!pThis->pvDecompExtent)
1990 rc = VERR_NO_MEMORY;
1991 else
1992 pThis->cbDecompExtent = DMG_BLOCK2BYTE(pExtent->cSectorsExtent);
1993 }
1994
1995 if (RT_SUCCESS(rc))
1996 {
1997 rc = dmgFileInflateSync(pThis, pExtent->offFileStart, pExtent->cbFile,
1998 pThis->pvDecompExtent,
1999 RT_MIN(pThis->cbDecompExtent, DMG_BLOCK2BYTE(pExtent->cSectorsExtent)));
2000 if (RT_SUCCESS(rc))
2001 pThis->pExtentDecomp = pExtent;
2002 }
2003 }
2004
2005 if (RT_SUCCESS(rc))
2006 vdIfIoIntIoCtxCopyTo(pThis->pIfIoXxx, pIoCtx,
2007 (uint8_t *)pThis->pvDecompExtent + DMG_BLOCK2BYTE(uExtentRel),
2008 cbToRead);
2009 break;
2010 }
2011 default:
2012 AssertMsgFailed(("Invalid extent type\n"));
2013 }
2014
2015 if (RT_SUCCESS(rc))
2016 *pcbActuallyRead = cbToRead;
2017 }
2018 else
2019 rc = VERR_INVALID_PARAMETER;
2020
2021 LogFlowFunc(("returns %Rrc\n", rc));
2022 return rc;
2023}
2024
2025/** @interface_method_impl{VDIMAGEBACKEND,pfnWrite} */
2026static DECLCALLBACK(int) dmgWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
2027 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
2028 size_t *pcbPostRead, unsigned fWrite)
2029{
2030 RT_NOREF7(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
2031 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
2032 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
2033 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2034 int rc = VERR_NOT_IMPLEMENTED;
2035
2036 AssertPtr(pThis);
2037 Assert(uOffset % 512 == 0);
2038 Assert(cbToWrite % 512 == 0);
2039
2040 if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2041 AssertMsgFailed(("Not implemented\n"));
2042 else
2043 rc = VERR_VD_IMAGE_READ_ONLY;
2044
2045 LogFlowFunc(("returns %Rrc\n", rc));
2046 return rc;
2047}
2048
2049/** @interface_method_impl{VDIMAGEBACKEND,pfnFlush} */
2050static DECLCALLBACK(int) dmgFlush(void *pBackendData, PVDIOCTX pIoCtx)
2051{
2052 RT_NOREF1(pIoCtx);
2053 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2054 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2055 int rc;
2056
2057 AssertPtr(pThis);
2058
2059 rc = dmgFlushImage(pThis);
2060
2061 LogFlowFunc(("returns %Rrc\n", rc));
2062 return rc;
2063}
2064
2065/** @interface_method_impl{VDIMAGEBACKEND,pfnGetVersion} */
2066static DECLCALLBACK(unsigned) dmgGetVersion(void *pBackendData)
2067{
2068 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2069 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2070
2071 AssertPtrReturn(pThis, 0);
2072
2073 return 1;
2074}
2075
2076/** @interface_method_impl{VDIMAGEBACKEND,pfnGetFileSize} */
2077static DECLCALLBACK(uint64_t) dmgGetFileSize(void *pBackendData)
2078{
2079 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2080 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2081
2082 AssertPtrReturn(pThis, 0);
2083
2084 uint64_t cbFile = 0;
2085 if (pThis->pStorage || pThis->hDmgFileInXar != NIL_RTVFSFILE)
2086 {
2087 int rc = dmgWrapFileGetSize(pThis, &cbFile);
2088 if (RT_FAILURE(rc))
2089 cbFile = 0; /* Make sure it is 0 */
2090 }
2091
2092 LogFlowFunc(("returns %lld\n", cbFile));
2093 return cbFile;
2094}
2095
2096/** @interface_method_impl{VDIMAGEBACKEND,pfnGetPCHSGeometry} */
2097static DECLCALLBACK(int) dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
2098{
2099 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
2100 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2101 int rc = VINF_SUCCESS;
2102
2103 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2104
2105 if (pThis->PCHSGeometry.cCylinders)
2106 *pPCHSGeometry = pThis->PCHSGeometry;
2107 else
2108 rc = VERR_VD_GEOMETRY_NOT_SET;
2109
2110 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2111 return rc;
2112}
2113
2114/** @interface_method_impl{VDIMAGEBACKEND,pfnSetPCHSGeometry} */
2115static DECLCALLBACK(int) dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
2116{
2117 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2118 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2119 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2120 int rc = VINF_SUCCESS;
2121
2122 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2123
2124 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2125 rc = VERR_VD_IMAGE_READ_ONLY;
2126 else
2127 pThis->PCHSGeometry = *pPCHSGeometry;
2128
2129 LogFlowFunc(("returns %Rrc\n", rc));
2130 return rc;
2131}
2132
2133/** @interface_method_impl{VDIMAGEBACKEND,pfnGetLCHSGeometry} */
2134static DECLCALLBACK(int) dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
2135{
2136 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2137 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2138 int rc = VINF_SUCCESS;
2139
2140 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2141
2142 if (pThis->LCHSGeometry.cCylinders)
2143 *pLCHSGeometry = pThis->LCHSGeometry;
2144 else
2145 rc = VERR_VD_GEOMETRY_NOT_SET;
2146
2147 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2148 return rc;
2149}
2150
2151/** @interface_method_impl{VDIMAGEBACKEND,pfnSetLCHSGeometry} */
2152static DECLCALLBACK(int) dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
2153{
2154 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2155 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2156 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2157 int rc = VINF_SUCCESS;
2158
2159 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2160
2161 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2162 rc = VERR_VD_IMAGE_READ_ONLY;
2163 else
2164 pThis->LCHSGeometry = *pLCHSGeometry;
2165
2166 LogFlowFunc(("returns %Rrc\n", rc));
2167 return rc;
2168}
2169
2170/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
2171static DECLCALLBACK(int) dmgQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
2172{
2173 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
2174 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2175
2176 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2177
2178 *ppRegionList = &pThis->RegionList;
2179 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
2180 return VINF_SUCCESS;
2181}
2182
2183/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
2184static DECLCALLBACK(void) dmgRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
2185{
2186 RT_NOREF1(pRegionList);
2187 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
2188 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2189 AssertPtr(pThis); RT_NOREF(pThis);
2190
2191 /* Nothing to do here. */
2192}
2193
2194/** @interface_method_impl{VDIMAGEBACKEND,pfnGetImageFlags} */
2195static DECLCALLBACK(unsigned) dmgGetImageFlags(void *pBackendData)
2196{
2197 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2198 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2199 AssertPtrReturn(pThis, 0);
2200
2201 LogFlowFunc(("returns %#x\n", pThis->uImageFlags));
2202 return pThis->uImageFlags;
2203}
2204
2205/** @interface_method_impl{VDIMAGEBACKEND,pfnGetOpenFlags} */
2206static DECLCALLBACK(unsigned) dmgGetOpenFlags(void *pBackendData)
2207{
2208 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2209 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2210
2211 AssertPtrReturn(pThis, 0);
2212
2213 LogFlowFunc(("returns %#x\n", pThis->uOpenFlags));
2214 return pThis->uOpenFlags;
2215}
2216
2217/** @interface_method_impl{VDIMAGEBACKEND,pfnSetOpenFlags} */
2218static DECLCALLBACK(int) dmgSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2219{
2220 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2221 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2222 int rc = VINF_SUCCESS;
2223
2224 /* Image must be opened and the new flags must be valid. */
2225 if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
2226 | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL
2227 | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2228 rc = VERR_INVALID_PARAMETER;
2229 else
2230 {
2231 /* Implement this operation via reopening the image. */
2232 rc = dmgFreeImage(pThis, false);
2233 if (RT_SUCCESS(rc))
2234 rc = dmgOpenImage(pThis, uOpenFlags);
2235 }
2236
2237 LogFlowFunc(("returns %Rrc\n", rc));
2238 return rc;
2239}
2240
2241/** @copydoc VDIMAGEBACKEND::pfnGetComment */
2242VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(dmgGetComment);
2243
2244/** @copydoc VDIMAGEBACKEND::pfnSetComment */
2245VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(dmgSetComment, PDMGIMAGE);
2246
2247/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
2248VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(dmgGetUuid);
2249
2250/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
2251VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(dmgSetUuid, PDMGIMAGE);
2252
2253/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
2254VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(dmgGetModificationUuid);
2255
2256/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
2257VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(dmgSetModificationUuid, PDMGIMAGE);
2258
2259/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
2260VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(dmgGetParentUuid);
2261
2262/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
2263VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(dmgSetParentUuid, PDMGIMAGE);
2264
2265/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
2266VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(dmgGetParentModificationUuid);
2267
2268/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
2269VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(dmgSetParentModificationUuid, PDMGIMAGE);
2270
2271/** @interface_method_impl{VDIMAGEBACKEND,pfnDump} */
2272static DECLCALLBACK(void) dmgDump(void *pBackendData)
2273{
2274 PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
2275
2276 AssertPtrReturnVoid(pThis);
2277 vdIfErrorMessage(pThis->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cSectors=%llu\n",
2278 pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors,
2279 pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors,
2280 pThis->cbSize / DMG_SECTOR_SIZE);
2281}
2282
2283
2284const VDIMAGEBACKEND g_DmgBackend =
2285{
2286 /* u32Version */
2287 VD_IMGBACKEND_VERSION,
2288 /* pszBackendName */
2289 "DMG",
2290 /* uBackendCaps */
2291 VD_CAP_FILE | VD_CAP_VFS,
2292 /* paFileExtensions */
2293 s_aDmgFileExtensions,
2294 /* paConfigInfo */
2295 NULL,
2296 /* pfnProbe */
2297 dmgProbe,
2298 /* pfnOpen */
2299 dmgOpen,
2300 /* pfnCreate */
2301 dmgCreate,
2302 /* pfnRename */
2303 dmgRename,
2304 /* pfnClose */
2305 dmgClose,
2306 /* pfnRead */
2307 dmgRead,
2308 /* pfnWrite */
2309 dmgWrite,
2310 /* pfnFlush */
2311 dmgFlush,
2312 /* pfnDiscard */
2313 NULL,
2314 /* pfnGetVersion */
2315 dmgGetVersion,
2316 /* pfnGetFileSize */
2317 dmgGetFileSize,
2318 /* pfnGetPCHSGeometry */
2319 dmgGetPCHSGeometry,
2320 /* pfnSetPCHSGeometry */
2321 dmgSetPCHSGeometry,
2322 /* pfnGetLCHSGeometry */
2323 dmgGetLCHSGeometry,
2324 /* pfnSetLCHSGeometry */
2325 dmgSetLCHSGeometry,
2326 /* pfnQueryRegions */
2327 dmgQueryRegions,
2328 /* pfnRegionListRelease */
2329 dmgRegionListRelease,
2330 /* pfnGetImageFlags */
2331 dmgGetImageFlags,
2332 /* pfnGetOpenFlags */
2333 dmgGetOpenFlags,
2334 /* pfnSetOpenFlags */
2335 dmgSetOpenFlags,
2336 /* pfnGetComment */
2337 dmgGetComment,
2338 /* pfnSetComment */
2339 dmgSetComment,
2340 /* pfnGetUuid */
2341 dmgGetUuid,
2342 /* pfnSetUuid */
2343 dmgSetUuid,
2344 /* pfnGetModificationUuid */
2345 dmgGetModificationUuid,
2346 /* pfnSetModificationUuid */
2347 dmgSetModificationUuid,
2348 /* pfnGetParentUuid */
2349 dmgGetParentUuid,
2350 /* pfnSetParentUuid */
2351 dmgSetParentUuid,
2352 /* pfnGetParentModificationUuid */
2353 dmgGetParentModificationUuid,
2354 /* pfnSetParentModificationUuid */
2355 dmgSetParentModificationUuid,
2356 /* pfnDump */
2357 dmgDump,
2358 /* pfnGetTimestamp */
2359 NULL,
2360 /* pfnGetParentTimestamp */
2361 NULL,
2362 /* pfnSetParentTimestamp */
2363 NULL,
2364 /* pfnGetParentFilename */
2365 NULL,
2366 /* pfnSetParentFilename */
2367 NULL,
2368 /* pfnComposeLocation */
2369 genericFileComposeLocation,
2370 /* pfnComposeName */
2371 genericFileComposeName,
2372 /* pfnCompact */
2373 NULL,
2374 /* pfnResize */
2375 NULL,
2376 /* pfnRepair */
2377 NULL,
2378 /* pfnTraverseMetadata */
2379 NULL,
2380 /* u32VersionEnd */
2381 VD_IMGBACKEND_VERSION
2382};
2383
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette