VirtualBox

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

Last change on this file since 49971 was 48871, checked in by vboxsync, 11 years ago

DMG: Fixes.

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