VirtualBox

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

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

Copyright year updates by scm.

  • 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 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBoxDMG - Interpreter for Apple Disk Images (DMG).
4 */
5
6/*
7 * Copyright (C) 2010-2020 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 / RTVfsFileQuerySize 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 = RTVfsFileQuerySize(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 = RTVfsFileQuerySize(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