VirtualBox

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

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

Misc: Shut up annoying gcc warnings on darwin.

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