VirtualBox

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

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

Storage/VD: Cleanup VD plugin handling. One shared object can now support an arbitrary number of image backends instead of just one like before

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