VirtualBox

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

Last change on this file since 48846 was 48840, checked in by vboxsync, 11 years ago

DMG.cpp: All callback method implementation shall be using DECLCALLBACK! Eliminated a few really stupid goto that would've made Dijkstra cry.

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