VirtualBox

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

Last change on this file since 66486 was 66486, checked in by vboxsync, 8 years ago

Storage/VD: Convert all backends to use the region list callbacks, remove the pfnGetSize and pfnGetSectorSize callbacks because they are covered by the region lists

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