VirtualBox

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

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

Storage/VD: Add proper versioning of the backend structures instead of just relying on the structure size to make changing callback signatures possible in the future and still being able to reject incompatible plugins

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