VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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