VirtualBox

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

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

DMG: Fixes for making vbox-img convert work.

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