VirtualBox

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

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

Storage: warnings.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use