VirtualBox

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

Last change on this file since 58113 was 57388, checked in by vboxsync, 9 years ago

DECLCALLBACK

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