VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use