VirtualBox

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

Last change on this file since 33682 was 33567, checked in by vboxsync, 14 years ago

VD: Move the generic virtual disk framework + backends to src/VBox/Storage and rename the files to get rid of the HDD part because it supports floppy and DVD images too

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