DMG.cpp revision 35f5e9bef11abdf4f242ba8140229704fa063a0a
/* $Id$ */
/** @file
* VBoxDMG - Interpreter for Apple Disk Images (DMG).
*/
/*
* Copyright (C) 2010-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_VD_DMG
#include <VBox/vd-plugin.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Sector size, multiply with all sector counts to get number of bytes. */
#define DMG_SECTOR_SIZE 512
#define DMG_BYTE2BLOCK(u) ((u) >> 9)
/**
* UDIF checksum structure.
*/
typedef struct DMGUDIFCKSUM
{
union
{
} uSum; /**< The checksum. */
} DMGUDIFCKSUM;
typedef DMGUDIFCKSUM *PDMGUDIFCKSUM;
typedef const DMGUDIFCKSUM *PCDMGUDIFCKSUM;
/** @name Checksum Kind (DMGUDIFCKSUM::u32Kind)
* @{ */
/** No checksum. */
#define DMGUDIFCKSUM_NONE UINT32_C(0)
/** CRC-32. */
/** @} */
/**
* UDIF ID.
* This is kind of like a UUID only it isn't, but we'll use the UUID
* representation of it for simplicity.
*/
typedef DMGUDIFID *PDMGUDIFID;
typedef const DMGUDIFID *PCDMGUDIFID;
/**
* UDIF footer used by Apple Disk Images (DMG).
*
* This is a footer placed 512 bytes from the end of the file. Typically a DMG
* file starts with the data, which is followed by the block table and then ends
* with this structure.
*
* All fields are stored in big endian format.
*/
#pragma pack(1)
typedef struct DMGUDIF
{
uint64_t offRunData; /**< 0x010 - Where the running data fork starts (usually 0). (fUDIFRunningDataForkOffset) */
uint64_t offRsrc; /**< 0x028 - Where the resource fork starts (usually cbData or 0). (fUDIFRsrcForkOffset) */
} DMGUDIF;
#pragma pack()
/** The UDIF magic 'koly' (DMGUDIF::u32Magic). */
/** The current UDIF version (DMGUDIF::u32Version).
* This is currently the only we recognizes and will create. */
#define DMGUDIF_VER_CURRENT 4
/** @name UDIF flags (DMGUDIF::fFlags).
* @{ */
/** Flatten image whatever that means.
* (hdiutil -debug calls it kUDIFFlagsFlattened.) */
#define DMGUDIF_FLAGS_FLATTENED RT_BIT_32(0)
/** Internet enabled image.
* (hdiutil -debug calls it kUDIFFlagsInternetEnabled) */
/** Mask of known bits. */
/** @} */
/** @name UDIF Image Types (DMGUDIF::u32Type).
* @{ */
/** Device image type. (kUDIFDeviceImageType) */
#define DMGUDIF_TYPE_DEVICE 1
/** Device image type. (kUDIFPartitionImageType) */
#define DMGUDIF_TYPE_PARTITION 2
/** @} */
/**
* BLKX data.
*
* This contains the start offset and size of raw data stored in the image.
*
* All fields are stored in big endian format.
*/
#pragma pack(1)
typedef struct DMGBLKX
{
uint64_t cSectornumberFirst; /**< 0x008 - The first sector number the block represents in the virtual device. */
} DMGBLKX;
#pragma pack()
/** The BLKX magic 'mish' (DMGBLKX::u32Magic). */
/** BLKX version (DMGBLKX::u32Version). */
/** Blocks descriptor type: entire device. */
/**
* BLKX table descriptor.
*
* All fields are stored in big endian format.
*/
#pragma pack(1)
typedef struct DMGBLKXDESC
{
uint32_t u32Reserved; /**< 0x004 - Reserved, but contains +beg or +end in case thisi is a comment descriptor. */
} DMGBLKXDESC;
#pragma pack()
typedef DMGBLKXDESC *PDMGBLKXDESC;
typedef const DMGBLKXDESC *PCDMGBLKXDESC;
/** Raw image data type. */
#define DMGBLKXDESC_TYPE_RAW 1
/** Ignore type. */
#define DMGBLKXDESC_TYPE_IGNORE 2
/** Compressed with zlib type. */
/** Comment type. */
/** Terminator type. */
/**
* UDIF Resource Entry.
*/
typedef struct DMGUDIFRSRCENTRY
{
/** The ID. */
/** Attributes. */
/** The name. */
char *pszName;
/** The CoreFoundation name. Can be NULL. */
char *pszCFName;
/** The size of the data. */
/** The raw data. */
/** Pointer to an UDIF resource entry. */
typedef DMGUDIFRSRCENTRY *PDMGUDIFRSRCENTRY;
/** Pointer to a const UDIF resource entry. */
typedef DMGUDIFRSRCENTRY const *PCDMGUDIFRSRCENTRY;
/**
* UDIF Resource Array.
*/
typedef struct DMGUDIFRSRCARRAY
{
/** The array name. */
char szName[12];
/** The number of occupied entries. */
/** The array entries.
* A lazy bird ASSUME there are no more than 4 entries in any DMG. Increase the
* size if DMGs with more are found.
* r=aeichner: Saw one with 6 here (image of a whole DVD) */
/** Pointer to a UDIF resource array. */
typedef DMGUDIFRSRCARRAY *PDMGUDIFRSRCARRAY;
/** Pointer to a const UDIF resource array. */
typedef DMGUDIFRSRCARRAY const *PCDMGUDIFRSRCARRAY;
/**
* DMG extent types.
*/
typedef enum DMGEXTENTTYPE
{
/** Null, never used. */
DMGEXTENTTYPE_NULL = 0,
/** Raw image data. */
/** Zero extent, reads return 0 and writes have no effect. */
/** Compressed extent - compression method ZLIB. */
/** 32bit hack. */
DMGEXTENTTYPE_32BIT_HACK = 0x7fffffff
/**
* DMG extent mapping a virtual image block to real file offsets.
*/
typedef struct DMGEXTENT
{
/** Extent type. */
/** First sector this extent describes. */
/** Number of sectors this extent describes. */
/** Start offset in the real file. */
/** Number of bytes for the extent data in the file. */
} DMGEXTENT;
/** Pointer to an DMG extent. */
typedef DMGEXTENT *PDMGEXTENT;
/**
* VirtualBox Apple Disk Image (DMG) interpreter instance data.
*/
typedef struct DMGIMAGE
{
/** Image name.
* Kept around for logging and delete-on-close purposes. */
const char *pszFilename;
/** Storage handle. */
/** Pointer to the per-disk VD interface list. */
/** Pointer to the per-image VD interface list. */
/** Error interface. */
/** I/O interface. */
/** Flags the image was opened with. */
/** Image flags. */
unsigned uImageFlags;
/** Total size of the virtual image. */
/** Size of the image. */
/** Physical geometry of this image. */
/** Logical geometry of this image. */
/** The resources.
* A lazy bird ASSUME there are only two arrays in the resource-fork section in
* the XML, namely 'blkx' and 'plst'. These have been assigned fixed indexes. */
/** The UDIF footer. */
/** Number of valid extents in the array. */
unsigned cExtents;
/** Number of entries the array can hold. */
unsigned cExtentsMax;
/** Pointer to the extent array. */
/** Index of the last accessed extent. */
unsigned idxExtentLast;
/** Extent which owns the data in the buffer. */
/** Buffer holding the decompressed data for a extent. */
void *pvDecompExtent;
/** Size of the buffer. */
} DMGIMAGE;
/** Pointer to an instance of the DMG Image Interpreter. */
/** @name Resources indexes (into DMG::aRsrcs).
* @{ */
#define DMG_RSRC_IDX_BLKX 0
#define DMG_RSRC_IDX_PLST 1
/** @} */
/** State for the input callout of the inflate reader. */
typedef struct DMGINFLATESTATE
{
/* Image this operation relates to. */
/* Total size of the data to read. */
/* Offset in the file to read. */
/* Current read position. */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @def DMG_PRINTF
* Wrapper for LogRel.
*/
#define DMG_PRINTF(a) LogRel(a)
/** @def DMG_VALIDATE
*/
do { \
if (!(expr)) \
{ \
fRc = false; \
} \
} while (0)
/** VBoxDMG: Unable to parse the XML. */
#define VERR_VD_DMG_XML_PARSE_ERROR (-3280)
/*******************************************************************************
* Static Variables *
*******************************************************************************/
/** NULL-terminated array of supported file extensions. */
static const VDFILEEXTENSION s_aDmgFileExtensions[] =
{
{"dmg", VDTYPE_DVD},
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
{
if (pInflateState->iOffset < 0)
{
if (pcbBuf)
*pcbBuf = 1;
pInflateState->iOffset = 0;
return VINF_SUCCESS;
}
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Internal: read from a file and inflate the compressed data,
* distinguishing between async and normal operation
*/
{
int rc;
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (cbActuallyRead != cbBuf)
return rc;
}
/**
* Swaps endian.
* @param pUdif The structure.
*/
{
#ifndef RT_BIG_ENDIAN
#endif
}
/**
* Swaps endian from host cpu to file.
* @param pUdif The structure.
*/
{
}
/**
* Swaps endian from file to host cpu.
* @param pUdif The structure.
*/
{
}
/**
* Swaps endian from file to host cpu.
* @param pBlkx The blkx structure.
*/
{
}
/**
* Swaps endian from file to host cpu.
* @param pBlkxDesc The blkx descriptor structure.
*/
{
}
/**
* Validates an UDIF footer structure.
*
* @returns true if valid, false and LogRel()s on failure.
* @param pFtr The UDIF footer to validate.
* @param offFtr The offset of the structure.
*/
{
bool fRc = true;
DMG_VALIDATE(!(pFtr->fFlags & ~DMGUDIF_FLAGS_KNOWN_MASK), ("fFlags=%#RX32 fKnown=%RX32\n", pFtr->fFlags, DMGUDIF_FLAGS_KNOWN_MASK));
DMG_VALIDATE(pFtr->cbData <= offFtr && pFtr->offData + pFtr->cbData <= offFtr, ("cbData=%#RX64 offData=%#RX64 offFtr=%#RX64\n", pFtr->cbData, pFtr->offData, offFtr));
DMG_VALIDATE(pFtr->cbRsrc <= offFtr && pFtr->offRsrc + pFtr->cbRsrc <= offFtr, ("cbRsrc=%#RX64 offRsrc=%#RX64 offFtr=%#RX64\n", pFtr->cbRsrc, pFtr->offRsrc, offFtr));
DMG_VALIDATE(pFtr->iSegment == 0 || pFtr->iSegment == 1, ("iSegment=%RU32 cSegments=%RU32\n", pFtr->iSegment, pFtr->cSegments));
DMG_VALIDATE(pFtr->cbXml <= offFtr && pFtr->offXml + pFtr->cbXml <= offFtr, ("cbXml=%#RX64 offXml=%#RX64 offFtr=%#RX64\n", pFtr->cbXml, pFtr->offXml, offFtr));
DMG_VALIDATE(pFtr->u32Type == DMGUDIF_TYPE_DEVICE || pFtr->u32Type == DMGUDIF_TYPE_PARTITION, ("u32Type=%RU32\n", pFtr->u32Type));
return fRc;
}
{
bool fRc = true;
DMG_VALIDATE(pBlkx->u32Magic == DMGBLKX_MAGIC, ("u32Magic=%#RX32 u32MagicExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_MAGIC));
DMG_VALIDATE(pBlkx->u32Version == DMGBLKX_VERSION, ("u32Version=%#RX32 u32VersionExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_VERSION));
return fRc;
}
/**
* Swaps endian from host cpu to file.
* @param pId The structure.
*/
{
}
/**
* Swaps endian from file to host cpu.
* @param pId The structure.
*/
{
}
/**
* Swaps endian.
* @param pCkSum The structure.
*/
{
#ifdef RT_BIG_ENDIAN
#else
switch (u32Kind)
{
case DMGUDIFCKSUM_NONE:
/* nothing to do here */
break;
case DMGUDIFCKSUM_CRC32:
break;
default:
break;
}
#endif
}
/**
* Swaps endian from host cpu to file.
* @param pCkSum The structure.
*/
{
}
/**
* Swaps endian from file to host cpu.
* @param pCkSum The structure.
*/
{
}
/**
* Validates an UDIF checksum structure.
*
* @returns true if valid, false and LogRel()s on failure.
* @param pCkSum The checksum structure.
* @param pszPrefix The message prefix.
* @remarks This does not check the checksummed data.
*/
{
bool fRc = true;
{
case DMGUDIFCKSUM_NONE:
break;
case DMGUDIFCKSUM_CRC32:
break;
default:
break;
}
return fRc;
}
/**
* Internal. Flush image data to disk.
*/
{
int rc = VINF_SUCCESS;
{
/* @todo handle writable files, update checksums etc. */
}
return rc;
}
/**
* Internal. Free all allocated space for representing an image except pThis,
* and optionally delete the image from disk.
*/
{
int rc = VINF_SUCCESS;
/* Freeing a never allocated image (e.g. because the open failed) is
* not signalled as an error. After all nothing bad happens. */
if (pThis)
{
{
/* No point updating the file that is deleted anyway. */
if (!fDelete)
}
{
{
}
{
}
{
}
}
if (pThis->pvDecompExtent)
{
pThis->cbDecompExtent = 0;
}
}
return rc;
}
do { \
} while (0)
do { \
return psz; \
} while (0)
do { \
return psz; \
} while (0)
do { \
return psz; \
} while (0)
do { \
return psz; \
} while (0)
/**
* Finds the next tag end.
*
* @returns Pointer to a '>' or '\0'.
* @param pszCur The current position.
*/
static const char *dmgXmlFindTagEnd(const char *pszCur)
{
/* Might want to take quoted '>' into account? */
char ch;
pszCur++;
return pszCur;
}
/**
* Finds the end tag.
*
* Does not deal with '<tag attr="1"/>' style tags.
*
* @returns Pointer to the first char in the end tag. NULL if another tag
* was encountered first or if we hit the end of the file.
* @param pszTag The tag name.
*/
{
char ch;
{
if (ch == '<')
{
{
return psz;
}
break;
}
psz++;
}
return NULL;
}
/**
* Reads a signed 32-bit value.
*
* @returns NULL on success, pointer to the offending text on failure.
* @param pi32 Where to store the value.
*/
{
/*
* <string>-1</string>
*/
char *pszNext;
if (rc != VWRN_TRAILING_CHARS)
return *ppszCur;
return NULL;
}
/**
* Reads an unsigned 32-bit value.
*
* @returns NULL on success, pointer to the offending text on failure.
* @param pu32 Where to store the value.
*/
{
/*
* <string>0x00ff</string>
*/
char *pszNext;
if (rc != VWRN_TRAILING_CHARS)
return *ppszCur;
return NULL;
}
/**
* Reads a string value.
*
* @returns NULL on success, pointer to the offending text on failure.
* @param ppszString Where to store the pointer to the string. The caller
* must free this using RTMemFree.
*/
{
/*
* <string>Driver Descriptor Map (DDM : 0)</string>
*/
if (!pszEnd)
return *ppszCur;
if (!*ppszString)
return *ppszCur;
return NULL;
}
/**
* Parses the BASE-64 coded data tags.
*
* @returns NULL on success, pointer to the offending text on failure.
* @param ppbData Where to store the pointer to the data we've read. The
* caller must free this using RTMemFree.
* @param pcbData The number of bytes we're returning.
*/
{
/*
* <data> AAAAA... </data>
*/
if (cbData == -1)
return *ppszCur;
if (!*ppbData)
return *ppszCur;
char *pszIgnored;
if (RT_FAILURE(rc))
{
return *ppszCur;
}
return NULL;
}
/**
* Parses the XML resource-fork in a rather presumptive manner.
*
* This function is supposed to construct the DMG::aRsrcs instance data
* parts.
*
* @returns NULL on success, pointer to the problematic text on failure.
* @param pThis The DMG instance data.
* @param pszXml The XML text to parse, UTF-8.
* @param cch The size of the XML text.
*/
{
/*
* Verify the ?xml, !DOCTYPE and plist tags.
*/
/* <?xml version="1.0" encoding="UTF-8"?> */
while (*psz != '?')
{
if (!*psz)
return psz;
{
}
{
}
else
return psz;
}
/* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
/* <plist version="1.0"> */
/*
* Descend down to the 'resource-fork' dictionary.
* ASSUME it's the only top level dictionary.
*/
/* <dict> <key>resource-fork</key> */
/*
* Parse the keys in the resource-fork dictionary.
* ASSUME that there are just two, 'blkx' and 'plst'.
*/
{
/*
* Parse the key and Create the resource-fork entry.
*/
unsigned iRsrc;
{
}
{
}
else
{
continue;
}
/*
* Descend into the array and add the elements to the resource entry.
*/
/* <array> */
{
return psz;
{
/* switch on the key. */
const char *pszErr;
{
}
{
}
{
}
{
}
{
pszErr = dmgXmlParseData(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pbData, &pThis->aRsrcs[iRsrc].aEntries[i].cbData);
}
else
if (pszErr)
return pszErr;
} /* while not </dict> */
} /* while not </array> */
} /* while not </dict> */
/*
* ASSUMING there is only the 'resource-fork', we'll now see the end of
* the outer dict, plist and text.
*/
/* </dict> </plist> */
/* the end */
if (*psz)
return psz;
return NULL;
}
/**
* Returns the data attached to a resource.
*
* @returns VBox status code.
* @param pThis The DMG instance data.
* @param pcszRsrcName Name of the resource to get.
*/
{
int rc = VERR_NOT_FOUND;
{
{
rc = VINF_SUCCESS;
break;
}
}
return rc;
}
/**
* Creates a new extent from the given blkx descriptor.
*
* @returns VBox status code.
* @param pThis DMG instance data.
* @param uSectorPart First sector the partition owning the blkx descriptor has.
* @param pBlkxDesc The blkx descriptor.
*/
static int dmgExtentCreateFromBlkxDesc(PDMGIMAGE pThis, uint64_t uSectorPart, PDMGBLKXDESC pBlkxDesc)
{
int rc = VINF_SUCCESS;
else
{
AssertMsgFailed(("This method supports only raw or zero extents!\n"));
return VERR_NOT_SUPPORTED;
}
/** @todo: Merge raw extents if possible to save memory. */
#if 0
if ( pExtentNew
&& pExtentNew->uSectorExtent + pExtentNew->cSectorsExtent == offDevice + pBlkxDesc->u64SectorStart * DMG_SECTOR_SIZE;
{
/* Increase the last extent. */
}
else
#endif
{
{
/* Increase the array. */
PDMGEXTENT paExtentsNew = (PDMGEXTENT)RTMemRealloc(pThis->paExtents, sizeof(DMGEXTENT) * pThis->cExtentsMax);
if (!paExtentsNew)
{
rc = VERR_NO_MEMORY;
}
else
}
if (RT_SUCCESS(rc))
{
}
}
return rc;
}
/**
* Find the extent for the given sector number.
*/
{
/*
* We assume that the array is ordered from lower to higher sector
* numbers.
* This makes it possible to bisect the array to find the extent
* faster than using a linked list.
*/
unsigned idxMin = 0;
{
/* Determine the search direction. */
{
/* Search left from the current extent. */
}
{
/* Search right from the current extent. */
}
else
{
/* The sector lies in the extent, stop searching. */
break;
}
}
if (pExtent)
return pExtent;
}
/**
* Goes through the BLKX structure and creates the necessary extents.
*/
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < pBlkx->cBlocksRunCount; i++)
{
{
case DMGBLKXDESC_TYPE_RAW:
case DMGBLKXDESC_TYPE_IGNORE:
case DMGBLKXDESC_TYPE_ZLIB:
{
break;
}
case DMGBLKXDESC_TYPE_COMMENT:
break;
default:
break;
}
|| RT_FAILURE(rc))
break;
pBlkxDesc++;
}
return rc;
}
/**
* Worker for dmgOpen that reads in and validates all the necessary
* structures from the image.
*
* @returns VBox status code.
* @param pThis The DMG instance data.
* @param uOpenFlags Flags for defining the open type.
*/
{
false /* fCreate */),
if (RT_FAILURE(rc))
{
/* Do NOT signal an appropriate error here, as the VD layer has the
* choice of retrying the open if it failed. */
return rc;
}
/*
* Read the footer.
*/
if (RT_FAILURE(rc))
return rc;
return VERR_VD_DMG_INVALID_HEADER;
if (RT_FAILURE(rc))
return rc;
/*
* Do we recognize the footer structure? If so, is it valid?
*/
return VERR_VD_DMG_INVALID_HEADER;
return VERR_VD_DMG_INVALID_HEADER;
return VERR_VD_DMG_INVALID_HEADER;
{
return VERR_VD_DMG_INVALID_HEADER;
}
/*
* Read and parse the XML portion.
*/
if (!pszXml)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
if (!pszError)
{
if (RT_SUCCESS(rc))
{
{
{
break;
}
if (!pBlkx)
{
rc = VERR_NO_MEMORY;
break;
}
if ( dmgBlkxIsValid(pBlkx)
&& pRsrcBlkx->aEntries[idxBlkx].cbData == pBlkx->cBlocksRunCount * sizeof(DMGBLKXDESC) + sizeof(DMGBLKX))
else
if (RT_FAILURE(rc))
break;
}
}
else
}
else
{
DMG_PRINTF(("**** Bad XML at %#lx (%lu) ***\n%.256s\n**** Bad XML END ****\n",
}
}
if (RT_FAILURE(rc))
dmgFreeImage(pThis, false);
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
{
LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
int rc;
/*
* Open the file and read the footer.
*/
false /* fCreate */),
&pStorage);
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
}
else
if (RT_SUCCESS(rc))
{
/*
* Do we recognize this stuff? Does it look valid?
*/
{
{
rc = VINF_SUCCESS;
*penmType = VDTYPE_DVD;
}
else
{
}
}
else
}
if (pStorage)
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnOpen */
{
LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
int rc = VINF_SUCCESS;
/* Check open flags. All valid flags are (in principle) supported. */
if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
{
goto out;
}
/* Check remaining arguments. */
if ( !VALID_PTR(pszFilename)
|| !*pszFilename)
{
goto out;
}
/*
* Reject combinations we don't currently support.
*
* There is no point in being paranoid about the input here as we're just a
* simple backend and can expect the caller to be the only user and already
* have validate what it passes thru to us.
*/
if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
|| (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
{
goto out;
}
/*
* Create the basic instance data structure and open the file,
* then hand it over to a worker function that does all the rest.
*/
if (!pThis)
{
rc = VERR_NO_MEMORY;
goto out;
}
if (RT_SUCCESS(rc))
*ppBackendData = pThis;
else
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnCreate */
unsigned uImageFlags, const char *pszComment,
unsigned uPercentStart, unsigned uPercentSpan,
{
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));
int rc = VERR_NOT_SUPPORTED;
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnRename */
{
int rc = VERR_NOT_SUPPORTED;
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnClose */
{
int rc;
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnRead */
{
LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
int rc = VINF_SUCCESS;
|| cbToRead == 0)
{
goto out;
}
if (pExtent)
{
/* Remain in this extent. */
{
case DMGEXTENTTYPE_RAW:
{
break;
}
case DMGEXTENTTYPE_ZERO:
{
break;
}
case DMGEXTENTTYPE_COMP_ZLIB:
{
{
{
if (!pThis->pvDecompExtent)
rc = VERR_NO_MEMORY;
else
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
}
if (RT_SUCCESS(rc))
cbToRead);
break;
}
default:
AssertMsgFailed(("Invalid extent type\n"));
}
if (RT_SUCCESS(rc))
}
else
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnWrite */
{
LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
int rc = VERR_NOT_IMPLEMENTED;
{
goto out;
}
AssertMsgFailed(("Not implemented\n"));
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnFlush */
{
int rc;
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
static unsigned dmgGetVersion(void *pBackendData)
{
if (pThis)
return 1;
else
return 0;
}
/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
{
cb = 2048;
return cb;
}
/** @copydoc VBOXHDDBACKEND::pfnGetSize */
{
return cb;
}
/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
{
if (pThis)
{
{
if (RT_SUCCESS(rc))
}
}
return cb;
}
/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
{
int rc;
if (pThis)
{
{
rc = VINF_SUCCESS;
}
else
}
else
LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
{
LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
int rc;
if (pThis)
{
{
goto out;
}
rc = VINF_SUCCESS;
}
else
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
{
int rc;
if (pThis)
{
{
rc = VINF_SUCCESS;
}
else
}
else
LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
{
LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
int rc;
if (pThis)
{
{
goto out;
}
rc = VINF_SUCCESS;
}
else
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
static unsigned dmgGetImageFlags(void *pBackendData)
{
unsigned uImageFlags;
if (pThis)
else
uImageFlags = 0;
return uImageFlags;
}
/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
static unsigned dmgGetOpenFlags(void *pBackendData)
{
unsigned uOpenFlags;
if (pThis)
else
uOpenFlags = 0;
return uOpenFlags;
}
/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
{
int rc;
/* Image must be opened and the new flags must be valid. */
{
goto out;
}
/* Implement this operation via reopening the image. */
if (RT_FAILURE(rc))
goto out;
out:
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetComment */
{
LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
int rc;
if (pThis)
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetComment */
{
int rc;
if (pImage)
{
else
}
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
{
int rc;
if (pThis)
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
{
int rc;
if (pThis)
{
else
}
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
{
int rc;
if (pThis)
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
{
int rc;
if (pThis)
{
else
}
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
{
int rc;
if (pThis)
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
{
int rc;
if (pThis)
{
else
}
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
{
int rc;
if (pThis)
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
{
int rc;
if (pThis)
{
else
}
else
return rc;
}
/** @copydoc VBOXHDDBACKEND::pfnDump */
static void dmgDump(void *pBackendData)
{
if (pThis)
{
}
}
{
/* pszBackendName */
"DMG",
/* cbSize */
sizeof(VBOXHDDBACKEND),
/* uBackendCaps */
/* paFileExtensions */
/* paConfigInfo */
NULL,
/* hPlugin */
/* pfnCheckIfValid */
/* pfnOpen */
/* pfnCreate */
/* pfnRename */
/* pfnClose */
/* pfnRead */
/* pfnWrite */
/* pfnFlush */
/* pfnDiscard */
NULL,
/* pfnGetVersion */
/* pfnGetSectorSize */
/* pfnGetSize */
/* pfnGetFileSize */
/* pfnGetPCHSGeometry */
/* pfnSetPCHSGeometry */
/* pfnGetLCHSGeometry */
/* pfnSetLCHSGeometry */
/* pfnGetImageFlags */
/* pfnGetOpenFlags */
/* pfnSetOpenFlags */
/* pfnGetComment */
/* pfnSetComment */
/* pfnGetUuid */
/* pfnSetUuid */
/* pfnGetModificationUuid */
/* pfnSetModificationUuid */
/* pfnGetParentUuid */
/* pfnSetParentUuid */
/* pfnGetParentModificationUuid */
/* pfnSetParentModificationUuid */
/* pfnDump */
/* pfnGetTimeStamp */
NULL,
/* pfnGetParentTimeStamp */
NULL,
/* pfnSetParentTimeStamp */
NULL,
/* pfnGetParentFilename */
NULL,
/* pfnSetParentFilename */
NULL,
/* pfnComposeLocation */
/* pfnComposeName */
/* pfnCompact */
NULL,
/* pfnResize */
NULL,
/* pfnRepair */
};