summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dvm/dvmmbr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/dvm/dvmmbr.cpp')
-rw-r--r--src/VBox/Runtime/common/dvm/dvmmbr.cpp721
1 files changed, 721 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dvm/dvmmbr.cpp b/src/VBox/Runtime/common/dvm/dvmmbr.cpp
new file mode 100644
index 00000000..464223c4
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmmbr.cpp
@@ -0,0 +1,721 @@
+/* $Id: dvmmbr.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - MBR format backend.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/dvm.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include "internal/dvm.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Checks if the partition type is an extended partition container. */
+#define RTDVMMBR_IS_EXTENDED(a_bType) ((a_bType) == 0x05 || (a_bType) == 0x0f)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a MBR sector. */
+typedef struct RTDVMMBRSECTOR *PRTDVMMBRSECTOR;
+
+/**
+ * MBR entry.
+ */
+typedef struct RTDVMMBRENTRY
+{
+ /** Our entry in the in-use partition entry list (RTDVMMBRENTRY). */
+ RTLISTNODE ListEntry;
+ /** Pointer to the MBR sector containing this entry. */
+ PRTDVMMBRSECTOR pSector;
+ /** Pointer to the next sector in the extended partition table chain. */
+ PRTDVMMBRSECTOR pChain;
+ /** The byte offset of the start of the partition (relative to disk). */
+ uint64_t offPart;
+ /** Number of bytes for this partition. */
+ uint64_t cbPart;
+ /** The partition/filesystem type. */
+ uint8_t bType;
+ /** The partition flags. */
+ uint8_t fFlags;
+ /** Bad entry. */
+ bool fBad;
+} RTDVMMBRENTRY;
+/** Pointer to an MBR entry. */
+typedef RTDVMMBRENTRY *PRTDVMMBRENTRY;
+
+/**
+ * A MBR sector.
+ */
+typedef struct RTDVMMBRSECTOR
+{
+ /** Internal representation of the entries. */
+ RTDVMMBRENTRY aEntries[4];
+ /** The byte offset of this MBR sector (relative to disk).
+ * We keep this for detecting cycles now, but it will be needed if we start
+ * updating the partition table at some point. */
+ uint64_t offOnDisk;
+ /** Pointer to the previous sector if this isn't a primary one. */
+ PRTDVMMBRENTRY pPrevSector;
+ /** Set if this is the primary MBR, cleared if an extended. */
+ bool fIsPrimary;
+ /** Number of used entries. */
+ uint8_t cUsed;
+ /** Number of extended entries. */
+ uint8_t cExtended;
+ /** The extended entry we're following (we only follow one, except when
+ * fIsPrimary is @c true). UINT8_MAX if none. */
+ uint8_t idxExtended;
+ /** The raw data. */
+ uint8_t abData[512];
+} RTDVMMBRSECTOR;
+
+/**
+ * MBR volume manager data.
+ */
+typedef struct RTDVMFMTINTERNAL
+{
+ /** Pointer to the underlying disk. */
+ PCRTDVMDISK pDisk;
+ /** Head of the list of in-use RTDVMMBRENTRY structures. This excludes
+ * extended partition table entries. */
+ RTLISTANCHOR PartitionHead;
+ /** The total number of partitions, not counting extended ones. */
+ uint32_t cPartitions;
+ /** The actual primary MBR sector. */
+ RTDVMMBRSECTOR Primary;
+} RTDVMFMTINTERNAL;
+/** Pointer to the MBR volume manager. */
+typedef RTDVMFMTINTERNAL *PRTDVMFMTINTERNAL;
+
+/**
+ * MBR volume data.
+ */
+typedef struct RTDVMVOLUMEFMTINTERNAL
+{
+ /** Pointer to the volume manager. */
+ PRTDVMFMTINTERNAL pVolMgr;
+ /** The MBR entry. */
+ PRTDVMMBRENTRY pEntry;
+} RTDVMVOLUMEFMTINTERNAL;
+/** Pointer to an MBR volume. */
+typedef RTDVMVOLUMEFMTINTERNAL *PRTDVMVOLUMEFMTINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Mapping of FS types to DVM volume types.
+ *
+ * @see https://en.wikipedia.org/wiki/Partition_type
+ * @see http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+ */
+static const struct RTDVMMBRFS2VOLTYPE
+{
+ /** MBR FS Id. */
+ uint8_t bFsId;
+ /** DVM volume type. */
+ RTDVMVOLTYPE enmVolType;
+} g_aFs2DvmVolTypes[] =
+{
+ { 0x01, RTDVMVOLTYPE_FAT12 },
+ { 0x04, RTDVMVOLTYPE_FAT16 },
+ { 0x06, RTDVMVOLTYPE_FAT16 }, /* big FAT16 */
+ { 0x07, RTDVMVOLTYPE_NTFS }, /* Simplification: Used for HPFS, exFAT, ++, too but NTFS is the more common one. */
+ { 0x0b, RTDVMVOLTYPE_FAT32 },
+ { 0x0c, RTDVMVOLTYPE_FAT32 },
+ { 0x0e, RTDVMVOLTYPE_FAT16 },
+
+ /* Hidden variants of the above: */
+ { 0x11, RTDVMVOLTYPE_FAT12 },
+ { 0x14, RTDVMVOLTYPE_FAT16 },
+ { 0x16, RTDVMVOLTYPE_FAT16 },
+ { 0x17, RTDVMVOLTYPE_NTFS },
+ { 0x1b, RTDVMVOLTYPE_FAT32 },
+ { 0x1c, RTDVMVOLTYPE_FAT32 },
+ { 0x1e, RTDVMVOLTYPE_FAT16 },
+
+ { 0x82, RTDVMVOLTYPE_LINUX_SWAP },
+ { 0x83, RTDVMVOLTYPE_LINUX_NATIVE },
+ { 0x8e, RTDVMVOLTYPE_LINUX_LVM },
+ { 0xa5, RTDVMVOLTYPE_FREEBSD },
+ { 0xa9, RTDVMVOLTYPE_NETBSD },
+ { 0xa6, RTDVMVOLTYPE_OPENBSD },
+ { 0xaf, RTDVMVOLTYPE_DARWIN_HFS },
+ { 0xbf, RTDVMVOLTYPE_SOLARIS },
+ { 0xfd, RTDVMVOLTYPE_LINUX_SOFTRAID }
+};
+
+static DECLCALLBACK(int) rtDvmFmtMbrProbe(PCRTDVMDISK pDisk, uint32_t *puScore)
+{
+ int rc = VINF_SUCCESS;
+ *puScore = RTDVM_MATCH_SCORE_UNSUPPORTED;
+ if (pDisk->cbDisk >= 512)
+ {
+ /* Read from the disk and check for the 0x55aa signature at the end. */
+ uint8_t abMbr[512];
+ rc = rtDvmDiskRead(pDisk, 0, &abMbr[0], sizeof(abMbr));
+ if ( RT_SUCCESS(rc)
+ && abMbr[510] == 0x55
+ && abMbr[511] == 0xaa)
+ *puScore = RTDVM_MATCH_SCORE_SUPPORTED; /* Not perfect because GPTs have a protective MBR. */
+ }
+
+ return rc;
+}
+
+
+static void rtDvmFmtMbrDestroy(PRTDVMFMTINTERNAL pThis)
+{
+ /*
+ * Delete chains of extended partitions.
+ */
+ for (unsigned i = 0; i < 4; i++)
+ {
+ PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
+ while (pCur)
+ {
+ PRTDVMMBRSECTOR pNext = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL;
+
+ RT_ZERO(pCur->aEntries);
+ pCur->pPrevSector = NULL;
+ RTMemFree(pCur);
+
+ pCur = pNext;
+ }
+ }
+
+ /*
+ * Now kill this.
+ */
+ pThis->pDisk = NULL;
+ RT_ZERO(pThis->Primary.aEntries);
+ RTMemFree(pThis);
+}
+
+
+static int rtDvmFmtMbrReadExtended(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pPrimaryEntry)
+{
+ uint64_t const cbExt = pPrimaryEntry->cbPart;
+ uint64_t const offExtBegin = pPrimaryEntry->offPart;
+
+ uint64_t offCurBegin = offExtBegin;
+ PRTDVMMBRENTRY pCurEntry = pPrimaryEntry;
+ for (unsigned cTables = 1; ; cTables++)
+ {
+ /*
+ * Do some sanity checking.
+ */
+ /* Check the address of the partition table. */
+ if (offCurBegin - offExtBegin >= cbExt)
+ {
+ LogRel(("rtDvmFmtMbrReadExtended: offCurBegin=%#RX64 is outside the extended partition: %#RX64..%#RX64 (LB %#RX64)\n",
+ offCurBegin, offExtBegin, offExtBegin + cbExt - 1, cbExt));
+ pCurEntry->fBad = true;
+ return -VERR_OUT_OF_RANGE;
+ }
+
+ /* Limit the chain length. */
+ if (cTables > 64)
+ {
+ LogRel(("rtDvmFmtMbrReadExtended: offCurBegin=%#RX64 is the %uth table, we stop here.\n", offCurBegin, cTables));
+ pCurEntry->fBad = true;
+ return -VERR_TOO_MANY_SYMLINKS;
+ }
+
+ /* Check for obvious cycles. */
+ for (PRTDVMMBRENTRY pPrev = pCurEntry->pSector->pPrevSector; pPrev != NULL; pPrev = pPrev->pSector->pPrevSector)
+ if (pPrev->offPart == offCurBegin)
+ {
+ LogRel(("rtDvmFmtMbrReadExtended: Cycle! We've seen offCurBegin=%#RX64 before\n", offCurBegin));
+ pCurEntry->fBad = true;
+ return -VERR_TOO_MANY_SYMLINKS;
+ }
+
+ /*
+ * Allocate a new sector entry and read the sector with the table.
+ */
+ PRTDVMMBRSECTOR pNext = (PRTDVMMBRSECTOR)RTMemAllocZ(sizeof(*pNext));
+ if (!pNext)
+ return VERR_NO_MEMORY;
+ pNext->offOnDisk = offCurBegin;
+ pNext->pPrevSector = pCurEntry;
+ //pNext->fIsPrimary = false;
+ //pNext->cUsed = 0;
+ //pNext->cExtended = 0;
+ pNext->idxExtended = UINT8_MAX;
+
+ int rc = rtDvmDiskRead(pThis->pDisk, pNext->offOnDisk, &pNext->abData[0], sizeof(pNext->abData));
+ if ( RT_FAILURE(rc)
+ || pNext->abData[510] != 0x55
+ || pNext->abData[511] != 0xaa)
+ {
+ if (RT_FAILURE(rc))
+ LogRel(("rtDvmFmtMbrReadExtended: Error reading extended partition table at sector %#RX64: %Rrc\n", offCurBegin, rc));
+ else
+ LogRel(("rtDvmFmtMbrReadExtended: Extended partition table at sector %#RX64 does not have a valid DOS signature: %#x %#x\n",
+ offCurBegin, pNext->abData[510], pNext->abData[511]));
+ RTMemFree(pNext);
+ pCurEntry->fBad = true;
+ return rc;
+ }
+ pCurEntry->pChain = pNext;
+
+ /*
+ * Process the table, taking down the first forward entry.
+ *
+ * As noted in the caller of this function, we only deal with one extended
+ * partition entry at this level since noone really ever put more than one
+ * here anyway.
+ */
+ PRTDVMMBRENTRY pEntry = &pNext->aEntries[0];
+ uint8_t *pbMbrEntry = &pNext->abData[446];
+ for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
+ {
+ uint8_t const bType = pbMbrEntry[4];
+ pEntry->pSector = pNext;
+ RTListInit(&pEntry->ListEntry);
+ if (bType != 0)
+ {
+ pEntry->bType = bType;
+ pEntry->fFlags = pbMbrEntry[0];
+ pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08],
+ pbMbrEntry[0x08 + 1],
+ pbMbrEntry[0x08 + 2],
+ pbMbrEntry[0x08 + 3]);
+ pEntry->offPart *= 512;
+ pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c],
+ pbMbrEntry[0x0c + 1],
+ pbMbrEntry[0x0c + 2],
+ pbMbrEntry[0x0c + 3]);
+ pEntry->cbPart *= 512;
+ if (!RTDVMMBR_IS_EXTENDED(bType))
+ {
+ pEntry->offPart += offCurBegin;
+ pThis->cPartitions++;
+ RTListAppend(&pThis->PartitionHead, &pEntry->ListEntry);
+ Log2(("rtDvmFmtMbrReadExtended: %#012RX64::%u: vol%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
+ offCurBegin, i, pThis->cPartitions - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
+ }
+ else
+ {
+ pEntry->offPart += offExtBegin;
+ pNext->cExtended++;
+ if (pNext->idxExtended == UINT8_MAX)
+ pNext->idxExtended = (uint8_t)i;
+ else
+ {
+ pEntry->fBad = true;
+ LogRel(("rtDvmFmtMbrReadExtended: Warning! Both #%u and #%u are extended partition table entries! Only following the former\n",
+ i, pNext->idxExtended));
+ }
+ Log2(("rtDvmFmtMbrReadExtended: %#012RX64::%u: ext%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
+ offCurBegin, i, pNext->cExtended - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
+ }
+ pNext->cUsed++;
+
+ }
+ /* else: unused */
+ }
+
+ /*
+ * We're done if we didn't find any extended partition table entry.
+ * Otherwise, advance to the next one.
+ */
+ if (!pNext->cExtended)
+ return VINF_SUCCESS;
+ pCurEntry = &pNext->aEntries[pNext->idxExtended];
+ offCurBegin = pCurEntry->offPart;
+ }
+}
+
+
+static DECLCALLBACK(int) rtDvmFmtMbrOpen(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ int rc;
+ PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMFMTINTERNAL));
+ if (pThis)
+ {
+ pThis->pDisk = pDisk;
+ //pThis->cPartitions = 0;
+ RTListInit(&pThis->PartitionHead);
+ //pThis->Primary.offOnDisk = 0;
+ //pThis->Primary.pPrevSector = NULL;
+ pThis->Primary.fIsPrimary = true;
+ //pThis->Primary.cUsed = 0;
+ //pThis->Primary.cExtended = 0;
+ pThis->Primary.idxExtended = UINT8_MAX;
+
+ /*
+ * Read the primary MBR.
+ */
+ rc = rtDvmDiskRead(pDisk, 0, &pThis->Primary.abData[0], sizeof(pThis->Primary.abData));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pThis->Primary.abData[510] == 0x55 && pThis->Primary.abData[511] == 0xaa);
+
+ /*
+ * Setup basic data for the 4 entries.
+ */
+ PRTDVMMBRENTRY pEntry = &pThis->Primary.aEntries[0];
+ uint8_t *pbMbrEntry = &pThis->Primary.abData[446];
+ for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
+ {
+ pEntry->pSector = &pThis->Primary;
+ RTListInit(&pEntry->ListEntry);
+
+ uint8_t const bType = pbMbrEntry[4];
+ if (bType != 0)
+ {
+ pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
+ pbMbrEntry[0x08 + 1],
+ pbMbrEntry[0x08 + 2],
+ pbMbrEntry[0x08 + 3]);
+ pEntry->offPart *= 512;
+ pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
+ pbMbrEntry[0x0c + 1],
+ pbMbrEntry[0x0c + 2],
+ pbMbrEntry[0x0c + 3]);
+ pEntry->cbPart *= 512;
+ pEntry->bType = bType;
+ pEntry->fFlags = pbMbrEntry[0];
+ if (!RTDVMMBR_IS_EXTENDED(bType))
+ {
+ pThis->cPartitions++;
+ RTListAppend(&pThis->PartitionHead, &pEntry->ListEntry);
+ Log2(("rtDvmFmtMbrOpen: %u: vol%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
+ i, pThis->cPartitions - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
+ }
+ else
+ {
+ pThis->Primary.cExtended++;
+ Log2(("rtDvmFmtMbrOpen: %u: ext%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
+ i, pThis->Primary.cExtended - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
+ }
+ pThis->Primary.cUsed++;
+ }
+ /* else: unused */
+ }
+
+ /*
+ * Now read any extended partitions. Since it's no big deal for us, we allow
+ * the primary partition table to have more than one extended partition. However
+ * in the extended tables we only allow a single forward link to avoid having to
+ * deal with recursion.
+ */
+ if (pThis->Primary.cExtended > 0)
+ for (unsigned i = 0; i < 4; i++)
+ if (RTDVMMBR_IS_EXTENDED(pThis->Primary.aEntries[i].bType))
+ {
+ if (pThis->Primary.idxExtended == UINT8_MAX)
+ pThis->Primary.idxExtended = (uint8_t)i;
+ rc = rtDvmFmtMbrReadExtended(pThis, &pThis->Primary.aEntries[i]);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *phVolMgrFmt = pThis;
+ return rc;
+ }
+
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ int rc;
+ PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMFMTINTERNAL));
+ if (pThis)
+ {
+ pThis->pDisk = pDisk;
+ //pThis->cPartitions = 0;
+ RTListInit(&pThis->PartitionHead);
+ //pThis->Primary.offOnDisk = 0
+ //pThis->Primary.pPrevSector = NULL;
+ pThis->Primary.fIsPrimary = true;
+ //pThis->Primary.cUsed = 0;
+ //pThis->Primary.cExtended = 0;
+ pThis->Primary.idxExtended = UINT8_MAX;
+
+ /* Setup a new MBR and write it to the disk. */
+ pThis->Primary.abData[510] = 0x55;
+ pThis->Primary.abData[511] = 0xaa;
+ rc = rtDvmDiskWrite(pDisk, 0, &pThis->Primary.abData[0], sizeof(pThis->Primary.abData));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pDisk = pDisk;
+ *phVolMgrFmt = pThis;
+ }
+ else
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(void) rtDvmFmtMbrClose(RTDVMFMT hVolMgrFmt)
+{
+ rtDvmFmtMbrDestroy(hVolMgrFmt);
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrQueryRangeUse(RTDVMFMT hVolMgrFmt, uint64_t off, uint64_t cbRange, bool *pfUsed)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ /*
+ * The MBR definitely uses the first 512 bytes, but we consider anything up
+ * to 1MB of alignment padding / cylinder gap to be considered in use too.
+ *
+ * The cylinder gap has been used by several boot managers and boot loaders
+ * to store code and data.
+ */
+ if (off < (uint64_t)_1M)
+ {
+ *pfUsed = true;
+ return VINF_SUCCESS;
+ }
+
+ /* Ditto for any extended partition tables. */
+ for (uint32_t iPrimary = 0; iPrimary < 4; iPrimary++)
+ {
+ PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[iPrimary].pChain;
+ while (pCur)
+ {
+ if ( off < pCur->offOnDisk + _1M
+ && off + cbRange > pCur->offOnDisk)
+ {
+ *pfUsed = true;
+ return VINF_SUCCESS;
+ }
+
+
+ if (pCur->idxExtended == UINT8_MAX)
+ break;
+ pCur = pCur->aEntries[pCur->idxExtended].pChain;
+ }
+
+ }
+
+ /* Not in use. */
+ *pfUsed = false;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetValidVolumes(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ return pThis->cPartitions;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetMaxVolumes(RTDVMFMT hVolMgrFmt)
+{
+ NOREF(hVolMgrFmt);
+ return 4; /** @todo Add support for EBR? */
+}
+
+/**
+ * Creates a new volume.
+ *
+ * @returns IPRT status code.
+ * @param pThis The MBR volume manager data.
+ * @param pEntry The MBR entry to create a volume handle for.
+ * @param phVolFmt Where to store the volume data on success.
+ */
+static int rtDvmFmtMbrVolumeCreate(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pEntry, PRTDVMVOLUMEFMT phVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = (PRTDVMVOLUMEFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEFMTINTERNAL));
+ if (pVol)
+ {
+ pVol->pVolMgr = pThis;
+ pVol->pEntry = pEntry;
+ *phVolFmt = pVol;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrQueryFirstVolume(RTDVMFMT hVolMgrFmt, PRTDVMVOLUMEFMT phVolFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ if (pThis->cPartitions != 0)
+ return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmt);
+ return VERR_DVM_MAP_EMPTY;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrQueryNextVolume(RTDVMFMT hVolMgrFmt, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUMEFMT phVolFmtNext)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ PRTDVMVOLUMEFMTINTERNAL pCurVol = hVolFmt;
+ if (pCurVol)
+ {
+ PRTDVMMBRENTRY pNextEntry = RTListGetNext(&pThis->PartitionHead, pCurVol->pEntry, RTDVMMBRENTRY, ListEntry);
+ if (pNextEntry)
+ return rtDvmFmtMbrVolumeCreate(pThis, pNextEntry, phVolFmtNext);
+ return VERR_DVM_MAP_NO_VOLUME;
+ }
+ if (pThis->cPartitions != 0)
+ return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmtNext);
+ return VERR_DVM_MAP_EMPTY;
+}
+
+static DECLCALLBACK(void) rtDvmFmtMbrVolumeClose(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ pVol->pVolMgr = NULL;
+ pVol->pEntry = NULL;
+
+ RTMemFree(pVol);
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetSize(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ return pVol->pEntry->cbPart;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryName(RTDVMVOLUMEFMT hVolFmt, char **ppszVolName)
+{
+ NOREF(hVolFmt); NOREF(ppszVolName);
+ return VERR_NOT_SUPPORTED;
+}
+
+static DECLCALLBACK(RTDVMVOLTYPE) rtDvmFmtMbrVolumeGetType(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ uint8_t const bType = pVol->pEntry->bType;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aFs2DvmVolTypes); i++)
+ if (g_aFs2DvmVolTypes[i].bFsId == bType)
+ return g_aFs2DvmVolTypes[i].enmVolType;
+
+ return RTDVMVOLTYPE_UNKNOWN;
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetFlags(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ uint64_t fFlags = 0;
+ if (pVol->pEntry->bType & 0x80)
+ fFlags |= DVMVOLUME_FLAGS_BOOTABLE | DVMVOLUME_FLAGS_ACTIVE;
+
+ return fFlags;
+}
+
+static DECLCALLBACK(bool) rtDvmFmtMbrVolumeIsRangeIntersecting(RTDVMVOLUMEFMT hVolFmt, uint64_t offStart, size_t cbRange,
+ uint64_t *poffVol, uint64_t *pcbIntersect)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ if (RTDVM_RANGE_IS_INTERSECTING(pVol->pEntry->offPart, pVol->pEntry->cbPart, offStart))
+ {
+ *poffVol = offStart - pVol->pEntry->offPart;
+ *pcbIntersect = RT_MIN(cbRange, pVol->pEntry->offPart + pVol->pEntry->cbPart - offStart);
+ return true;
+ }
+ return false;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeRead(RTDVMVOLUMEFMT hVolFmt, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbRead <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskRead(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbRead);
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeWrite(RTDVMVOLUMEFMT hVolFmt, uint64_t off, const void *pvBuf, size_t cbWrite)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbWrite <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskWrite(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbWrite);
+}
+
+RTDVMFMTOPS g_rtDvmFmtMbr =
+{
+ /* pszFmt */
+ "MBR",
+ /* enmFormat */
+ RTDVMFORMATTYPE_MBR,
+ /* pfnProbe */
+ rtDvmFmtMbrProbe,
+ /* pfnOpen */
+ rtDvmFmtMbrOpen,
+ /* pfnInitialize */
+ rtDvmFmtMbrInitialize,
+ /* pfnClose */
+ rtDvmFmtMbrClose,
+ /* pfnQueryRangeUse */
+ rtDvmFmtMbrQueryRangeUse,
+ /* pfnGetValidVolumes */
+ rtDvmFmtMbrGetValidVolumes,
+ /* pfnGetMaxVolumes */
+ rtDvmFmtMbrGetMaxVolumes,
+ /* pfnQueryFirstVolume */
+ rtDvmFmtMbrQueryFirstVolume,
+ /* pfnQueryNextVolume */
+ rtDvmFmtMbrQueryNextVolume,
+ /* pfnVolumeClose */
+ rtDvmFmtMbrVolumeClose,
+ /* pfnVolumeGetSize */
+ rtDvmFmtMbrVolumeGetSize,
+ /* pfnVolumeQueryName */
+ rtDvmFmtMbrVolumeQueryName,
+ /* pfnVolumeGetType */
+ rtDvmFmtMbrVolumeGetType,
+ /* pfnVolumeGetFlags */
+ rtDvmFmtMbrVolumeGetFlags,
+ /* pfnVOlumeIsRangeIntersecting */
+ rtDvmFmtMbrVolumeIsRangeIntersecting,
+ /* pfnVolumeRead */
+ rtDvmFmtMbrVolumeRead,
+ /* pfnVolumeWrite */
+ rtDvmFmtMbrVolumeWrite
+};
+