VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DMGHDDCore.cpp@ 32536

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

Storage/VBoxHDD: replace custom open flags with regular IPRT file open flags, introduce user-providable filesystem access interface, eliminate dependency on PGM geometry structure, change pvBuffer/cbBuffer parameter ordering to the usual conventions, eliminate the remains of the old I/O code, make more plugin methods optional to reduce redundancy, lots of cleanups

Storage/DrvVD+testcases,Main/Medium+Frontends: adapt to VBoxHDD changes, logging fixes

Storage/VDI+VMDK+DMG+Raw+VHD+Parallels+VCI: made as similar to each other as possible, added inline VFS wrappers to improve readability, full VFS support, VDI files are now 4K aligned, eliminate the remains of the old I/O code, various more or less severe bugfixes, code sort

Storage/iSCSI: support disks bigger than 2T, streamline the code to be more similar to the file-based backends, memory leak fix, error code usage like file-based backends, code sort

log+err: added new error codes/log groups and eliminated unused old ones

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