VirtualBox

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

Last change on this file since 38469 was 38469, checked in by vboxsync, 13 years ago

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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