VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDDFormats/AppleDiskImage/VBoxDMG.cpp@ 33400

Last change on this file since 33400 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: VBoxDMG.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * VBoxDMG - Intepreter for Apple Disk Images (DMG).
4 */
5
6/*
7 * Copyright (C) 2009 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/VBoxHDD.h>
23#include <VBox/log.h>
24#include <VBox/err.h>
25#include <iprt/file.h>
26#include <iprt/assert.h>
27#include <iprt/asm.h>
28#include <iprt/mem.h>
29#include <iprt/ctype.h>
30#include <iprt/string.h>
31#include <iprt/base64.h>
32#ifdef VBOXDMG_TESTING
33# include <iprt/initterm.h>
34# include <iprt/stream.h>
35#endif
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41/**
42 * UDIF checksum structure.
43 */
44typedef struct VBOXUDIFCKSUM
45{
46 uint32_t u32Kind; /**< The kind of checksum. */
47 uint32_t cBits; /**< The size of the checksum. */
48 union
49 {
50 uint8_t au8[128]; /**< 8-bit view. */
51 uint32_t au32[32]; /**< 32-bit view. */
52 } uSum; /**< The checksum. */
53} VBOXUDIFCKSUM;
54AssertCompileSize(VBOXUDIFCKSUM, 8 + 128);
55typedef VBOXUDIFCKSUM *PVBOXUDIFCKSUM;
56typedef const VBOXUDIFCKSUM *PCVBOXUDIFCKSUM;
57
58/** @name Checksum Kind (VBOXUDIFCKSUM::u32Kind)
59 * @{ */
60/** No checksum. */
61#define VBOXUDIFCKSUM_NONE UINT32_C(0)
62/** CRC-32. */
63#define VBOXUDIFCKSUM_CRC32 UINT32_C(2)
64/** @} */
65
66/**
67 * UDIF ID.
68 * This is kind of like a UUID only it isn't, but we'll use the UUID
69 * representation of it for simplicity.
70 */
71typedef RTUUID VBOXUDIFID;
72AssertCompileSize(VBOXUDIFID, 16);
73typedef VBOXUDIFID *PVBOXUDIFID;
74typedef const VBOXUDIFID *PCVBOXUDIFID;
75
76/**
77 * UDIF footer used by Apple Disk Images (DMG).
78 *
79 * This is a footer placed 512 bytes from the end of the file. Typically a DMG
80 * file starts with the data, which is followed by the block table and then ends
81 * with this structure.
82 *
83 * All fields are stored in big endian format.
84 */
85#pragma pack(1)
86typedef struct VBOXUDIF
87{
88 uint32_t u32Magic; /**< 0x000 - Magic, 'koly' (VBOXUDIF_MAGIC). (fUDIFSignature) */
89 uint32_t u32Version; /**< 0x004 - The UDIF version (VBOXUDIF_VER_CURRENT). (fUDIFVersion) */
90 uint32_t cbFooter; /**< 0x008 - The size of the this structure (512). (fUDIFHeaderSize) */
91 uint32_t fFlags; /**< 0x00c - Flags. (fUDIFFlags) */
92 uint64_t offRunData; /**< 0x010 - Where the running data fork starts (usually 0). (fUDIFRunningDataForkOffset) */
93 uint64_t offData; /**< 0x018 - Where the data fork starts (usually 0). (fUDIFDataForkOffset) */
94 uint64_t cbData; /**< 0x020 - Size of the data fork (in bytes). (fUDIFDataForkLength) */
95 uint64_t offRsrc; /**< 0x028 - Where the resource fork starts (usually cbData or 0). (fUDIFRsrcForkOffset) */
96 uint64_t cbRsrc; /**< 0x030 - The size of the resource fork. (fUDIFRsrcForkLength)*/
97 uint32_t iSegment; /**< 0x038 - The segment number of this file. (fUDIFSegmentNumber) */
98 uint32_t cSegments; /**< 0x03c - The number of segments. (fUDIFSegmentCount) */
99 VBOXUDIFID SegmentId; /**< 0x040 - The segment ID. (fUDIFSegmentID) */
100 VBOXUDIFCKSUM DataCkSum; /**< 0x050 - The data checksum. (fUDIFDataForkChecksum) */
101 uint64_t offXml; /**< 0x0d8 - The XML offset (.plist kind of data). (fUDIFXMLOffset) */
102 uint64_t cbXml; /**< 0x0e0 - The size of the XML. (fUDIFXMLSize) */
103 uint8_t abUnknown[120]; /**< 0x0e8 - Unknown stuff, hdiutil doesn't dump it... */
104 VBOXUDIFCKSUM MasterCkSum; /**< 0x160 - The master checksum. (fUDIFMasterChecksum) */
105 uint32_t u32Type; /**< 0x1e8 - The image type. (fUDIFImageVariant) */
106 uint64_t cSectors; /**< 0x1ec - The sector count. Warning! Unaligned! (fUDISectorCount) */
107 uint32_t au32Unknown[3]; /**< 0x1f4 - Unknown stuff, hdiutil doesn't dump it... */
108} VBOXUDIF;
109#pragma pack(0)
110AssertCompileSize(VBOXUDIF, 512);
111AssertCompileMemberOffset(VBOXUDIF, cbRsrc, 0x030);
112AssertCompileMemberOffset(VBOXUDIF, cbXml, 0x0e0);
113AssertCompileMemberOffset(VBOXUDIF, cSectors, 0x1ec);
114
115typedef VBOXUDIF *PVBOXUDIF;
116typedef const VBOXUDIF *PCVBOXUDIF;
117
118/** The UDIF magic 'koly' (VBOXUDIF::u32Magic). */
119#define VBOXUDIF_MAGIC UINT32_C(0x6b6f6c79)
120
121/** The current UDIF version (VBOXUDIF::u32Version).
122 * This is currently the only we recognizes and will create. */
123#define VBOXUDIF_VER_CURRENT 4
124
125/** @name UDIF flags (VBOXUDIF::fFlags).
126 * @{ */
127/** Flatten image whatever that means.
128 * (hdiutil -debug calls it kUDIFFlagsFlattened.) */
129#define VBOXUDIF_FLAGS_FLATTENED RT_BIT_32(0)
130/** Internet enabled image.
131 * (hdiutil -debug calls it kUDIFFlagsInternetEnabled) */
132#define VBOXUDIF_FLAGS_INET_ENABLED RT_BIT_32(2)
133/** Mask of known bits. */
134#define VBOXUDIF_FLAGS_KNOWN_MASK (RT_BIT_32(0) | RT_BIT_32(2))
135/** @} */
136
137/** @name UDIF Image Types (VBOXUDIF::u32Type).
138 * @{ */
139/** Device image type. (kUDIFDeviceImageType) */
140#define VBOXUDIF_TYPE_DEVICE 1
141/** Device image type. (kUDIFPartitionImageType) */
142#define VBOXUDIF_TYPE_PARTITION 2
143/** @} */
144
145/**
146 * UDIF Resource Entry.
147 */
148typedef struct VBOXUDIFRSRCENTRY
149{
150 /** The ID. */
151 int32_t iId;
152 /** Attributes. */
153 uint32_t fAttributes;
154 /** The name. */
155 char *pszName;
156 /** The CoreFoundation name. Can be NULL. */
157 char *pszCFName;
158 /** The size of the data. */
159 size_t cbData;
160 /** The raw data. */
161 uint8_t *pbData;
162} VBOXUDIFRSRCENTRY;
163/** Pointer to an UDIF resource entry. */
164typedef VBOXUDIFRSRCENTRY *PVBOXUDIFRSRCENTRY;
165/** Pointer to a const UDIF resource entry. */
166typedef VBOXUDIFRSRCENTRY const *PCVBOXUDIFRSRCENTRY;
167
168/**
169 * UDIF Resource Array.
170 */
171typedef struct VBOXUDIFRSRCARRAY
172{
173 /** The array name. */
174 char szName[12];
175 /** The number of occupied entries. */
176 uint32_t cEntries;
177 /** The array entries.
178 * A lazy bird ASSUME there are no more than 4 entries in any DMG. Increase the
179 * size if DMGs with more are found. */
180 VBOXUDIFRSRCENTRY aEntries[4];
181} VBOXUDIFRSRCARRAY;
182/** Pointer to a UDIF resource array. */
183typedef VBOXUDIFRSRCARRAY *PVBOXUDIFRSRCARRAY;
184/** Pointer to a const UDIF resource array. */
185typedef VBOXUDIFRSRCARRAY const *PCVBOXUDIFRSRCARRAY;
186
187
188
189/**
190 * VirtualBox Apple Disk Image (DMG) interpreter instance data.
191 */
192typedef struct VBOXDMG
193{
194 /** The open image file. */
195 RTFILE hFile;
196 /** The current file size. */
197 uint64_t cbFile;
198 /** Flags the image was opened with. */
199 uint32_t fOpenFlags;
200 /** The filename.
201 * Kept around for logging and delete-on-close purposes. */
202 const char *pszFilename;
203 /** The resources.
204 * A lazy bird ASSUME there are only two arrays in the resource-fork section in
205 * the XML, namely 'blkx' and 'plst'. These have been assigned fixed indexes. */
206 VBOXUDIFRSRCARRAY aRsrcs[2];
207 /** The UDIF footer. */
208 VBOXUDIF Ftr;
209} VBOXDMG;
210/** Pointer to an instance of the DMG Image Interpreter. */
211typedef VBOXDMG *PVBOXDMG;
212
213/** @name Resources indexes (into VBOXDMG::aRsrcs).
214 * @{ */
215#define VBOXDMG_RSRC_IDX_BLKX 0
216#define VBOXDMG_RSRC_IDX_PLST 1
217/** @} */
218
219
220/*******************************************************************************
221* Defined Constants And Macros *
222*******************************************************************************/
223/** @def VBOXDMG_PRINTF
224 * Wrapper for RTPrintf/LogRel.
225 */
226#ifdef VBOXDMG_TESTING
227# define VBOXDMG_PRINTF(a) RTPrintf a
228#else
229# define VBOXDMG_PRINTF(a) LogRel(a)
230#endif
231
232/** @def VBOXDMG_VALIDATE
233 * For validating a struct thing and log/print what's wrong.
234 */
235#ifdef VBOXDMG_TESTING
236# define VBOXDMG_VALIDATE(expr, logstuff) \
237 do { \
238 if (!(expr)) \
239 { \
240 RTPrintf("tstVBoxDMG: validation failed: %s\ntstVBoxDMG: ", #expr); \
241 RTPrintf logstuff; \
242 fRc = false; \
243 } \
244 } while (0)
245#else
246# define VBOXDMG_VALIDATE(expr, logstuff) \
247 do { \
248 if (!(expr)) \
249 { \
250 LogRel(("vboxdmg: validation failed: %s\nvboxdmg: ", #expr)); \
251 LogRel(logstuff); \
252 fRc = false; \
253 } \
254 } while (0)
255#endif
256
257
258/** VBoxDMG: Unable to parse the XML. */
259#define VERR_VD_DMG_XML_PARSE_ERROR (-3280)
260
261
262/*******************************************************************************
263* Internal Functions *
264*******************************************************************************/
265static void vboxdmgUdifFtrHost2FileEndian(PVBOXUDIF pUdif);
266static void vboxdmgUdifFtrFile2HostEndian(PVBOXUDIF pUdif);
267
268static void vboxdmgUdifIdHost2FileEndian(PVBOXUDIFID pId);
269static void vboxdmgUdifIdFile2HostEndian(PVBOXUDIFID pId);
270
271static void vboxdmgUdifCkSumHost2FileEndian(PVBOXUDIFCKSUM pCkSum);
272static void vboxdmgUdifCkSumFile2HostEndian(PVBOXUDIFCKSUM pCkSum);
273static bool vboxdmgUdifCkSumIsValid(PCVBOXUDIFCKSUM pCkSum, const char *pszPrefix);
274
275
276/**
277 * Swaps endian.
278 * @param pUdif The structure.
279 */
280static void vboxdmgSwapEndianUdif(PVBOXUDIF pUdif)
281{
282#ifndef RT_BIG_ENDIAN
283 pUdif->u32Magic = RT_BSWAP_U32(pUdif->u32Magic);
284 pUdif->u32Version = RT_BSWAP_U32(pUdif->u32Version);
285 pUdif->cbFooter = RT_BSWAP_U32(pUdif->cbFooter);
286 pUdif->fFlags = RT_BSWAP_U32(pUdif->fFlags);
287 pUdif->offRunData = RT_BSWAP_U64(pUdif->offRunData);
288 pUdif->offData = RT_BSWAP_U64(pUdif->offData);
289 pUdif->cbData = RT_BSWAP_U64(pUdif->cbData);
290 pUdif->offRsrc = RT_BSWAP_U64(pUdif->offRsrc);
291 pUdif->cbRsrc = RT_BSWAP_U64(pUdif->cbRsrc);
292 pUdif->iSegment = RT_BSWAP_U32(pUdif->iSegment);
293 pUdif->cSegments = RT_BSWAP_U32(pUdif->cSegments);
294 pUdif->offXml = RT_BSWAP_U64(pUdif->offXml);
295 pUdif->cbXml = RT_BSWAP_U64(pUdif->cbXml);
296 pUdif->u32Type = RT_BSWAP_U32(pUdif->u32Type);
297 pUdif->cSectors = RT_BSWAP_U64(pUdif->cSectors);
298#endif
299}
300
301
302/**
303 * Swaps endian from host cpu to file.
304 * @param pUdif The structure.
305 */
306static void vboxdmgUdifFtrHost2FileEndian(PVBOXUDIF pUdif)
307{
308 vboxdmgSwapEndianUdif(pUdif);
309 vboxdmgUdifIdHost2FileEndian(&pUdif->SegmentId);
310 vboxdmgUdifCkSumHost2FileEndian(&pUdif->DataCkSum);
311 vboxdmgUdifCkSumHost2FileEndian(&pUdif->MasterCkSum);
312}
313
314
315/**
316 * Swaps endian from file to host cpu.
317 * @param pUdif The structure.
318 */
319static void vboxdmgUdifFtrFile2HostEndian(PVBOXUDIF pUdif)
320{
321 vboxdmgSwapEndianUdif(pUdif);
322 vboxdmgUdifIdFile2HostEndian(&pUdif->SegmentId);
323 vboxdmgUdifCkSumFile2HostEndian(&pUdif->DataCkSum);
324 vboxdmgUdifCkSumFile2HostEndian(&pUdif->MasterCkSum);
325}
326
327
328/**
329 * Validates an UDIF footer structure.
330 *
331 * @returns true if valid, false and LogRel()s on failure.
332 * @param pFtr The UDIF footer to validate.
333 * @param offFtr The offset of the structure.
334 */
335static bool vboxdmgUdifFtrIsValid(PCVBOXUDIF pFtr, uint64_t offFtr)
336{
337 bool fRc = true;
338
339 VBOXDMG_VALIDATE(!(pFtr->fFlags & ~VBOXUDIF_FLAGS_KNOWN_MASK), ("fFlags=%#RX32 fKnown=%RX32\n", pFtr->fFlags, VBOXUDIF_FLAGS_KNOWN_MASK));
340 VBOXDMG_VALIDATE(pFtr->offRunData < offFtr, ("offRunData=%#RX64\n", pFtr->offRunData));
341 VBOXDMG_VALIDATE(pFtr->cbData <= offFtr && pFtr->offData + pFtr->cbData <= offFtr, ("cbData=%#RX64 offData=%#RX64 offFtr=%#RX64\n", pFtr->cbData, pFtr->offData, offFtr));
342 VBOXDMG_VALIDATE(pFtr->offData < offFtr, ("offData=%#RX64\n", pFtr->offData));
343 VBOXDMG_VALIDATE(pFtr->cbRsrc <= offFtr && pFtr->offRsrc + pFtr->cbRsrc <= offFtr, ("cbRsrc=%#RX64 offRsrc=%#RX64 offFtr=%#RX64\n", pFtr->cbRsrc, pFtr->offRsrc, offFtr));
344 VBOXDMG_VALIDATE(pFtr->offRsrc < offFtr, ("offRsrc=%#RX64\n", pFtr->offRsrc));
345 VBOXDMG_VALIDATE(pFtr->cSegments <= 1, ("cSegments=%RU32\n", pFtr->cSegments));
346 VBOXDMG_VALIDATE(pFtr->iSegment == 0, ("iSegment=%RU32 cSegments=%RU32\n", pFtr->iSegment, pFtr->cSegments));
347 VBOXDMG_VALIDATE(pFtr->cbXml <= offFtr && pFtr->offXml + pFtr->cbXml <= offFtr, ("cbXml=%#RX64 offXml=%#RX64 offFtr=%#RX64\n", pFtr->cbXml, pFtr->offXml, offFtr));
348 VBOXDMG_VALIDATE(pFtr->offXml < offFtr, ("offXml=%#RX64\n", pFtr->offXml));
349 VBOXDMG_VALIDATE(pFtr->cbXml > 128, ("cbXml=%#RX64\n", pFtr->cbXml));
350 VBOXDMG_VALIDATE(pFtr->cbXml < _1M, ("cbXml=%#RX64\n", pFtr->cbXml));
351 VBOXDMG_VALIDATE(pFtr->u32Type == VBOXUDIF_TYPE_DEVICE || pFtr->u32Type == VBOXUDIF_TYPE_PARTITION, ("u32Type=%RU32\n", pFtr->u32Type));
352 VBOXDMG_VALIDATE(pFtr->cSectors != 0, ("cSectors=%#RX64\n", pFtr->cSectors));
353 vboxdmgUdifCkSumIsValid(&pFtr->DataCkSum, "DataCkSum");
354 vboxdmgUdifCkSumIsValid(&pFtr->MasterCkSum, "MasterCkSum");
355
356 return fRc;
357}
358
359
360/**
361 * Swaps endian from host cpu to file.
362 * @param pId The structure.
363 */
364static void vboxdmgUdifIdHost2FileEndian(PVBOXUDIFID pId)
365{
366 NOREF(pId);
367}
368
369
370/**
371 * Swaps endian from file to host cpu.
372 * @param pId The structure.
373 */
374static void vboxdmgUdifIdFile2HostEndian(PVBOXUDIFID pId)
375{
376 vboxdmgUdifIdHost2FileEndian(pId);
377}
378
379
380/**
381 * Swaps endian.
382 * @param pCkSum The structure.
383 */
384static void vboxdmgSwapEndianUdifCkSum(PVBOXUDIFCKSUM pCkSum, uint32_t u32Kind, uint32_t cBits)
385{
386#ifdef RT_BIG_ENDIAN
387 NOREF(pCkSum);
388 NOREF(u32Kind);
389 NOREF(cBits);
390#else
391 switch (u32Kind)
392 {
393 case VBOXUDIFCKSUM_NONE:
394 /* nothing to do here */
395 break;
396
397 case VBOXUDIFCKSUM_CRC32:
398 Assert(cBits == 32);
399 pCkSum->u32Kind = RT_BSWAP_U32(pCkSum->u32Kind);
400 pCkSum->cBits = RT_BSWAP_U32(pCkSum->cBits);
401 pCkSum->uSum.au32[0] = RT_BSWAP_U32(pCkSum->uSum.au32[0]);
402 break;
403
404 default:
405 AssertMsgFailed(("%x\n", u32Kind));
406 break;
407 }
408 NOREF(cBits);
409#endif
410}
411
412
413/**
414 * Swaps endian from host cpu to file.
415 * @param pCkSum The structure.
416 */
417static void vboxdmgUdifCkSumHost2FileEndian(PVBOXUDIFCKSUM pCkSum)
418{
419 vboxdmgSwapEndianUdifCkSum(pCkSum, pCkSum->u32Kind, pCkSum->cBits);
420}
421
422
423/**
424 * Swaps endian from file to host cpu.
425 * @param pCkSum The structure.
426 */
427static void vboxdmgUdifCkSumFile2HostEndian(PVBOXUDIFCKSUM pCkSum)
428{
429 vboxdmgSwapEndianUdifCkSum(pCkSum, RT_BE2H_U32(pCkSum->u32Kind), RT_BE2H_U32(pCkSum->cBits));
430}
431
432
433/**
434 * Validates an UDIF checksum structure.
435 *
436 * @returns true if valid, false and LogRel()s on failure.
437 * @param pCkSum The checksum structure.
438 * @param pszPrefix The message prefix.
439 * @remarks This does not check the checksummed data.
440 */
441static bool vboxdmgUdifCkSumIsValid(PCVBOXUDIFCKSUM pCkSum, const char *pszPrefix)
442{
443 bool fRc = true;
444
445 switch (pCkSum->u32Kind)
446 {
447 case VBOXUDIFCKSUM_NONE:
448 VBOXDMG_VALIDATE(pCkSum->cBits == 0, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
449 break;
450
451 case VBOXUDIFCKSUM_CRC32:
452 VBOXDMG_VALIDATE(pCkSum->cBits == 32, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
453 break;
454
455 default:
456 VBOXDMG_VALIDATE(0, ("%s: u32Kind=%#RX32\n", pszPrefix, pCkSum->u32Kind));
457 break;
458 }
459 return fRc;
460}
461
462
463/** @copydoc VBOXHDDBACKEND::pfnClose */
464static DECLCALLBACK(int) vboxdmgClose(void *pvBackendData, bool fDelete)
465{
466 PVBOXDMG pThis = (PVBOXDMG)pvBackendData;
467
468 if (pThis->hFile != NIL_RTFILE)
469 {
470 /*
471 * If writable, flush changes, fix checksums, ++.
472 */
473 /** @todo writable images. */
474
475 /*
476 * Close the file.
477 */
478 RTFileClose(pThis->hFile);
479 pThis->hFile = NIL_RTFILE;
480 }
481
482 /*
483 * Delete the file if requested, then free the remaining resources.
484 */
485 int rc = VINF_SUCCESS;
486 if (fDelete)
487 rc = RTFileDelete(pThis->pszFilename);
488
489 for (unsigned iRsrc = 0; iRsrc < RT_ELEMENTS(pThis->aRsrcs); iRsrc++)
490 for (unsigned i = 0; i < pThis->aRsrcs[iRsrc].cEntries; i++)
491 {
492 if (pThis->aRsrcs[iRsrc].aEntries[i].pbData)
493 {
494 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pbData);
495 pThis->aRsrcs[iRsrc].aEntries[i].pbData = NULL;
496 }
497 if (pThis->aRsrcs[iRsrc].aEntries[i].pszName)
498 {
499 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszName);
500 pThis->aRsrcs[iRsrc].aEntries[i].pszName = NULL;
501 }
502 if (pThis->aRsrcs[iRsrc].aEntries[i].pszCFName)
503 {
504 RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
505 pThis->aRsrcs[iRsrc].aEntries[i].pszCFName = NULL;
506 }
507 }
508 RTMemFree(pThis);
509
510 return rc;
511}
512
513
514#define STARTS_WITH(pszString, szStart) \
515 ( strncmp(pszString, szStart, sizeof(szStart) - 1) == 0 )
516
517#define STARTS_WITH_WORD(pszString, szWord) \
518 ( STARTS_WITH(pszString, szWord) \
519 && !RT_C_IS_ALNUM((pszString)[sizeof(szWord) - 1]) )
520
521#define SKIP_AHEAD(psz, szWord) \
522 do { \
523 (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
524 } while (0)
525
526#define REQUIRE_WORD(psz, szWord) \
527 do { \
528 if (!STARTS_WITH_WORD(psz, szWord)) \
529 return psz; \
530 (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
531 } while (0)
532
533#define REQUIRE_TAG(psz, szTag) \
534 do { \
535 if (!STARTS_WITH(psz, "<" szTag ">")) \
536 return psz; \
537 (psz) = RTStrStripL((psz) + sizeof("<" szTag ">") - 1); \
538 } while (0)
539
540#define REQUIRE_TAG_NO_STRIP(psz, szTag) \
541 do { \
542 if (!STARTS_WITH(psz, "<" szTag ">")) \
543 return psz; \
544 (psz) += sizeof("<" szTag ">") - 1; \
545 } while (0)
546
547#define REQUIRE_END_TAG(psz, szTag) \
548 do { \
549 if (!STARTS_WITH(psz, "</" szTag ">")) \
550 return psz; \
551 (psz) = RTStrStripL((psz) + sizeof("</" szTag ">") - 1); \
552 } while (0)
553
554
555/**
556 * Finds the next tag end.
557 *
558 * @returns Pointer to a '>' or '\0'.
559 * @param pszCur The current position.
560 */
561static const char *vboxdmgXmlFindTagEnd(const char *pszCur)
562{
563 /* Might want to take quoted '>' into account? */
564 char ch;
565 while ((ch = *pszCur) != '\0' && ch != '>')
566 pszCur++;
567 return pszCur;
568}
569
570
571/**
572 * Finds the end tag.
573 *
574 * Does not deal with '<tag attr="1"/>' style tags.
575 *
576 * @returns Pointer to the first char in the end tag. NULL if another tag
577 * was encountered first or if we hit the end of the file.
578 * @param ppszCur The current position (IN/OUT).
579 * @param pszTag The tag name.
580 */
581static const char *vboxdmgXmlFindEndTag(const char **ppszCur, const char *pszTag)
582{
583 const char *psz = *ppszCur;
584 char ch;
585 while ((ch = *psz))
586 {
587 if (ch == '<')
588 {
589 size_t const cchTag = strlen(pszTag);
590 if ( psz[1] == '/'
591 && !memcmp(&psz[2], pszTag, cchTag)
592 && psz[2 + cchTag] == '>')
593 {
594 *ppszCur = psz + 2 + cchTag + 1;
595 return psz;
596 }
597 break;
598 }
599 psz++;
600 }
601 return NULL;
602}
603
604
605/**
606 * Reads a signed 32-bit value.
607 *
608 * @returns NULL on success, pointer to the offending text on failure.
609 * @param ppszCur The text position (IN/OUT).
610 * @param pi32 Where to store the value.
611 */
612static const char *vboxdmgXmlParseS32(const char **ppszCur, int32_t *pi32)
613{
614 const char *psz = *ppszCur;
615
616 /*
617 * <string>-1</string>
618 */
619 REQUIRE_TAG_NO_STRIP(psz, "string");
620
621 char *pszNext;
622 int rc = RTStrToInt32Ex(psz, &pszNext, 0, pi32);
623 if (rc != VWRN_TRAILING_CHARS)
624 return *ppszCur;
625 psz = pszNext;
626
627 REQUIRE_END_TAG(psz, "string");
628 *ppszCur = psz;
629 return NULL;
630}
631
632
633/**
634 * Reads an unsigned 32-bit value.
635 *
636 * @returns NULL on success, pointer to the offending text on failure.
637 * @param ppszCur The text position (IN/OUT).
638 * @param pu32 Where to store the value.
639 */
640static const char *vboxdmgXmlParseU32(const char **ppszCur, uint32_t *pu32)
641{
642 const char *psz = *ppszCur;
643
644 /*
645 * <string>0x00ff</string>
646 */
647 REQUIRE_TAG_NO_STRIP(psz, "string");
648
649 char *pszNext;
650 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
651 if (rc != VWRN_TRAILING_CHARS)
652 return *ppszCur;
653 psz = pszNext;
654
655 REQUIRE_END_TAG(psz, "string");
656 *ppszCur = psz;
657 return NULL;
658}
659
660
661/**
662 * Reads a string value.
663 *
664 * @returns NULL on success, pointer to the offending text on failure.
665 * @param ppszCur The text position (IN/OUT).
666 * @param ppszString Where to store the pointer to the string. The caller
667 * must free this using RTMemFree.
668 */
669static const char *vboxdmgXmlParseString(const char **ppszCur, char **ppszString)
670{
671 const char *psz = *ppszCur;
672
673 /*
674 * <string>Driver Descriptor Map (DDM : 0)</string>
675 */
676 REQUIRE_TAG_NO_STRIP(psz, "string");
677
678 const char *pszStart = psz;
679 const char *pszEnd = vboxdmgXmlFindEndTag(&psz, "string");
680 if (!pszEnd)
681 return *ppszCur;
682 psz = RTStrStripL(psz);
683
684 *ppszString = (char *)RTMemDupEx(pszStart, pszEnd - pszStart, 1);
685 if (!*ppszString)
686 return *ppszCur;
687
688 *ppszCur = psz;
689 return NULL;
690}
691
692
693/**
694 * Parses the BASE-64 coded data tags.
695 *
696 * @returns NULL on success, pointer to the offending text on failure.
697 * @param ppszCur The text position (IN/OUT).
698 * @param ppbData Where to store the pointer to the data we've read. The
699 * caller must free this using RTMemFree.
700 * @param pcbData The number of bytes we're returning.
701 */
702static const char *vboxdmgXmlParseData(const char **ppszCur, uint8_t **ppbData, size_t *pcbData)
703{
704 const char *psz = *ppszCur;
705
706 /*
707 * <data> AAAAA... </data>
708 */
709 REQUIRE_TAG(psz, "data");
710
711 const char *pszStart = psz;
712 ssize_t cbData = RTBase64DecodedSize(pszStart, (char **)&psz);
713 if (cbData == -1)
714 return *ppszCur;
715 const char *pszEnd = psz;
716
717 REQUIRE_END_TAG(psz, "data");
718
719 *ppbData = (uint8_t *)RTMemAlloc(cbData);
720 if (!*ppbData)
721 return *ppszCur;
722 char *pszIgnored;
723 int rc = RTBase64Decode(pszStart, *ppbData, cbData, pcbData, &pszIgnored);
724 if (RT_FAILURE(rc))
725 {
726 RTMemFree(*ppbData);
727 *ppbData = NULL;
728 return *ppszCur;
729 }
730
731 *ppszCur = psz;
732 return NULL;
733}
734
735
736/**
737 * Parses the XML resource-fork in a rather presumptive manner.
738 *
739 * This function is supposed to construct the VBOXDMG::aRsrcs instance data
740 * parts.
741 *
742 * @returns NULL on success, pointer to the problematic text on failure.
743 * @param pThis The DMG instance data.
744 * @param pszXml The XML text to parse, UTF-8.
745 * @param cch The size of the the XML text.
746 */
747static const char *vboxdmgOpenXmlToRsrc(PVBOXDMG pThis, char const *pszXml)
748{
749 const char *psz = pszXml;
750
751 /*
752 * Verify the ?xml, !DOCTYPE and plist tags.
753 */
754 SKIP_AHEAD(psz, "");
755
756 /* <?xml version="1.0" encoding="UTF-8"?> */
757 REQUIRE_WORD(psz, "<?xml");
758 while (*psz != '?')
759 {
760 if (!*psz)
761 return psz;
762 if (STARTS_WITH_WORD(psz, "version="))
763 {
764 SKIP_AHEAD(psz, "version=");
765 REQUIRE_WORD(psz, "\"1.0\"");
766 }
767 else if (STARTS_WITH_WORD(psz, "encoding="))
768 {
769 SKIP_AHEAD(psz, "encoding=");
770 REQUIRE_WORD(psz, "\"UTF-8\"");
771 }
772 else
773 return psz;
774 }
775 SKIP_AHEAD(psz, "?>");
776
777 /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
778 REQUIRE_WORD(psz, "<!DOCTYPE");
779 REQUIRE_WORD(psz, "plist");
780 REQUIRE_WORD(psz, "PUBLIC");
781 psz = vboxdmgXmlFindTagEnd(psz);
782 REQUIRE_WORD(psz, ">");
783
784 /* <plist version="1.0"> */
785 REQUIRE_WORD(psz, "<plist");
786 REQUIRE_WORD(psz, "version=");
787 REQUIRE_WORD(psz, "\"1.0\"");
788 REQUIRE_WORD(psz, ">");
789
790 /*
791 * Decend down to the 'resource-fork' dictionary.
792 * ASSUME it's the only top level dictionary.
793 */
794 /* <dict> <key>resource-fork</key> */
795 REQUIRE_TAG(psz, "dict");
796 REQUIRE_WORD(psz, "<key>resource-fork</key>");
797
798 /*
799 * Parse the keys in the resource-fork dictionary.
800 * ASSUME that there are just two, 'blkx' and 'plst'.
801 */
802 REQUIRE_TAG(psz, "dict");
803 while (!STARTS_WITH_WORD(psz, "</dict>"))
804 {
805 /*
806 * Parse the key and Create the resource-fork entry.
807 */
808 unsigned iRsrc;
809 if (STARTS_WITH_WORD(psz, "<key>blkx</key>"))
810 {
811 REQUIRE_WORD(psz, "<key>blkx</key>");
812 iRsrc = VBOXDMG_RSRC_IDX_BLKX;
813 strcpy(&pThis->aRsrcs[iRsrc].szName[0], "blkx");
814 }
815 else if (STARTS_WITH_WORD(psz, "<key>plst</key>"))
816 {
817 REQUIRE_WORD(psz, "<key>plst</key>");
818 iRsrc = VBOXDMG_RSRC_IDX_PLST;
819 strcpy(&pThis->aRsrcs[iRsrc].szName[0], "plst");
820 }
821 else
822 return psz;
823
824 /*
825 * Decend into the array and add the elements to the resource entry.
826 */
827 /* <array> */
828 REQUIRE_TAG(psz, "array");
829 while (!STARTS_WITH_WORD(psz, "</array>"))
830 {
831 REQUIRE_TAG(psz, "dict");
832 uint32_t i = pThis->aRsrcs[iRsrc].cEntries;
833 if (i == RT_ELEMENTS(pThis->aRsrcs[iRsrc].aEntries))
834 return psz;
835
836 while (!STARTS_WITH_WORD(psz, "</dict>"))
837 {
838
839 /* switch on the key. */
840 const char *pszErr;
841 if (STARTS_WITH_WORD(psz, "<key>Attributes</key>"))
842 {
843 REQUIRE_WORD(psz, "<key>Attributes</key>");
844 pszErr = vboxdmgXmlParseU32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].fAttributes);
845 }
846 else if (STARTS_WITH_WORD(psz, "<key>ID</key>"))
847 {
848 REQUIRE_WORD(psz, "<key>ID</key>");
849 pszErr = vboxdmgXmlParseS32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].iId);
850 }
851 else if (STARTS_WITH_WORD(psz, "<key>Name</key>"))
852 {
853 REQUIRE_WORD(psz, "<key>Name</key>");
854 pszErr = vboxdmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszName);
855 }
856 else if (STARTS_WITH_WORD(psz, "<key>CFName</key>"))
857 {
858 REQUIRE_WORD(psz, "<key>CFName</key>");
859 pszErr = vboxdmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
860 }
861 else if (STARTS_WITH_WORD(psz, "<key>Data</key>"))
862 {
863 REQUIRE_WORD(psz, "<key>Data</key>");
864 pszErr = vboxdmgXmlParseData(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pbData, &pThis->aRsrcs[iRsrc].aEntries[i].cbData);
865 }
866 else
867 pszErr = psz;
868 if (pszErr)
869 return pszErr;
870 } /* while not </dict> */
871 REQUIRE_END_TAG(psz, "dict");
872
873 } /* while not </array> */
874 REQUIRE_END_TAG(psz, "array");
875
876 } /* while not </dict> */
877 REQUIRE_END_TAG(psz, "dict");
878
879 /*
880 * ASSUMING there is only the 'resource-fork', we'll now see the end of
881 * the outer dict, plist and text.
882 */
883 /* </dict> </plist> */
884 REQUIRE_END_TAG(psz, "dict");
885 REQUIRE_END_TAG(psz, "plist");
886
887 /* the end */
888 if (*psz)
889 return psz;
890
891 return NULL;
892}
893
894#undef REQUIRE_END_TAG
895#undef REQUIRE_TAG_NO_STRIP
896#undef REQUIRE_TAG
897#undef REQUIRE_WORD
898#undef SKIP_AHEAD
899#undef STARTS_WITH_WORD
900#undef STARTS_WITH
901
902
903/**
904 * Worker for vboxdmgOpen that reads in and validates all the necessary
905 * structures from the image.
906 *
907 * This worker is really just a construct to avoid gotos and do-break-while-zero
908 * uglyness.
909 *
910 * @returns VBox status code.
911 * @param pThis The DMG instance data.
912 */
913static int vboxdmgOpenWorker(PVBOXDMG pThis)
914{
915 /*
916 * Read the footer.
917 */
918 int rc = RTFileGetSize(pThis->hFile, &pThis->cbFile);
919 if (RT_FAILURE(rc))
920 return rc;
921 if (pThis->cbFile < 1024)
922 return VERR_VD_GEN_INVALID_HEADER;
923 rc = RTFileReadAt(pThis->hFile, pThis->cbFile - sizeof(pThis->Ftr), &pThis->Ftr, sizeof(pThis->Ftr), NULL);
924 if (RT_FAILURE(rc))
925 return rc;
926 vboxdmgUdifFtrFile2HostEndian(&pThis->Ftr);
927
928 /*
929 * Do we recognize the footer structure? If so, is it valid?
930 */
931 if (pThis->Ftr.u32Magic != VBOXUDIF_MAGIC)
932 return VERR_VD_GEN_INVALID_HEADER;
933 if (pThis->Ftr.u32Version != VBOXUDIF_VER_CURRENT)
934 return VERR_VD_GEN_INVALID_HEADER;
935 if (pThis->Ftr.cbFooter != sizeof(pThis->Ftr))
936 return VERR_VD_GEN_INVALID_HEADER;
937
938 if (!vboxdmgUdifFtrIsValid(&pThis->Ftr, pThis->cbFile - sizeof(pThis->Ftr)))
939 {
940 VBOXDMG_PRINTF(("Bad DMG: '%s' cbFile=%RTfoff\n", pThis->pszFilename, pThis->cbFile));
941 return VERR_VD_GEN_INVALID_HEADER;
942 }
943
944 /*
945 * Read and parse the XML portion.
946 */
947 size_t cchXml = (size_t)pThis->Ftr.cbXml;
948 char *pszXml = (char *)RTMemAlloc(cchXml + 1);
949 if (!pszXml)
950 return VERR_NO_MEMORY;
951 rc = RTFileReadAt(pThis->hFile, pThis->Ftr.offXml, pszXml, cchXml, NULL);
952 if (RT_SUCCESS(rc))
953 {
954 pszXml[cchXml] = '\0';
955 const char *pszError = vboxdmgOpenXmlToRsrc(pThis, pszXml);
956 if (!pszError)
957 {
958 /*
959 * What is next?
960 */
961 }
962 else
963 {
964 VBOXDMG_PRINTF(("**** XML DUMP BEGIN ***\n%s\n**** XML DUMP END ****\n", pszXml));
965 VBOXDMG_PRINTF(("**** Bad XML at %#lx (%lu) ***\n%.256s\n**** Bad XML END ****\n",
966 (unsigned long)(pszError - pszXml), (unsigned long)(pszError - pszXml), pszError));
967 rc = VERR_VD_DMG_XML_PARSE_ERROR;
968 }
969 }
970 RTMemFree(pszXml);
971 if (RT_FAILURE(rc))
972 return rc;
973
974
975 return VINF_SUCCESS;
976}
977
978
979
980/** @copydoc VBOXHDDBACKEND::pfnOpen */
981static DECLCALLBACK(int) vboxdmgOpen(const char *pszFilename, unsigned fOpenFlags,
982 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
983 void **ppvBackendData)
984{
985 /*
986 * Reject combinations we don't currently support.
987 *
988 * There is no point in being paranoid about the input here as we're just a
989 * simple backend and can expect the caller to be the only user and already
990 * have validate what it passes thru to us.
991 */
992 if (!(fOpenFlags & VD_OPEN_FLAGS_READONLY))
993 return VERR_NOT_SUPPORTED;
994
995 /*
996 * Create the basic instance data structure and open the file,
997 * then hand it over to a worker function that does all the rest.
998 */
999 PVBOXDMG pThis = (PVBOXDMG)RTMemAllocZ(sizeof(*pThis));
1000 if (!pThis)
1001 return VERR_NO_MEMORY;
1002 pThis->fOpenFlags = fOpenFlags;
1003 pThis->pszFilename = pszFilename;
1004 pThis->hFile = NIL_RTFILE;
1005 //pThis->cbFile = 0;
1006
1007 int rc = RTFileOpen(&pThis->hFile, pszFilename,
1008 (fOpenFlags & VD_OPEN_FLAGS_READONLY ? RTFILE_O_READ : RTFILE_O_READWRITE)
1009 | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1010 if (RT_SUCCESS(rc))
1011 {
1012 rc = vboxdmgOpenWorker(pThis);
1013 if (RT_SUCCESS(rc))
1014 {
1015 *ppvBackendData = pThis;
1016 return rc;
1017 }
1018 }
1019
1020 /* We failed, let the close method clean up. */
1021 vboxdmgClose(pThis, false /* fDelete */);
1022 return rc;
1023}
1024
1025
1026/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1027static DECLCALLBACK(int) vboxdmgCheckIfValid(const char *pszFilename)
1028{
1029 /*
1030 * Open the file and read the footer.
1031 */
1032 RTFILE hFile;
1033 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1034 if (RT_FAILURE(rc))
1035 return rc;
1036
1037 VBOXUDIF Ftr;
1038 uint64_t offFtr;
1039 rc = RTFileSeek(hFile, -(RTFOFF)sizeof(Ftr), RTFILE_SEEK_END, &offFtr);
1040 if (RT_SUCCESS(rc))
1041 {
1042 rc = RTFileRead(hFile, &Ftr, sizeof(Ftr), NULL);
1043 if (RT_SUCCESS(rc))
1044 {
1045 vboxdmgUdifFtrFile2HostEndian(&Ftr);
1046
1047 /*
1048 * Do we recognize this stuff? Does it look valid?
1049 */
1050 if ( Ftr.u32Magic == VBOXUDIF_MAGIC
1051 && Ftr.u32Version == VBOXUDIF_VER_CURRENT
1052 && Ftr.cbFooter == sizeof(Ftr))
1053 {
1054 if (vboxdmgUdifFtrIsValid(&Ftr, offFtr))
1055 rc = VINF_SUCCESS;
1056 else
1057 {
1058 VBOXDMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
1059 rc = VERR_VD_GEN_INVALID_HEADER;
1060 }
1061
1062 }
1063 else
1064 rc = VERR_VD_GEN_INVALID_HEADER;
1065 }
1066 }
1067
1068 RTFileClose(hFile);
1069 return rc;
1070}
1071
1072
1073
1074
1075#ifdef VBOXDMG_TESTING
1076int main(int argc, char **argv)
1077{
1078 RTR3Init();
1079
1080 const char *psz = argv[1];
1081 if (!psz || argv[2])
1082 {
1083 RTPrintf("syntax: tstVBoxDMG <image>\n");
1084 return 1;
1085 }
1086
1087 RTPrintf("tstVBoxDMG: TESTING '%s'...\n", psz);
1088
1089 /* test 1 - vboxdmgCheckIfValid */
1090 int rc = vboxdmgCheckIfValid(psz);
1091 if (RT_FAILURE(rc))
1092 {
1093 RTPrintf("tstVBoxDMG: vboxdmgCheckIfValid failed, rc=%Rrc\n", rc);
1094 return 1;
1095 }
1096 RTPrintf("tstVBoxDMG: vboxdmgCheckIfValid succeeded (rc=%Rrc)\n", rc);
1097
1098
1099 /* test 1 - vboxdmgOpen(RDONLY) & Close. */
1100 void *pvOpaque = NULL;
1101 rc = vboxdmgOpen(psz, VD_OPEN_FLAGS_READONLY, NULL, NULL, &pvOpaque);
1102 if (RT_FAILURE(rc))
1103 {
1104 RTPrintf("tstVBoxDMG: vboxdmgOpen(RDONLY) failed, rc=%Rrc\n", rc);
1105 return 1;
1106 }
1107
1108 rc = vboxdmgClose(pvOpaque, false /* fDelete */);
1109 if (RT_FAILURE(rc))
1110 {
1111 RTPrintf("tstVBoxDMG: vboxdmgClose(RDONLY) failed, rc=%Rrc\n", rc);
1112 return 1;
1113 }
1114
1115
1116 RTPrintf("tstVBoxDMG: SUCCESS\n");
1117 return 0;
1118}
1119#endif
1120
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