diff options
Diffstat (limited to 'src/VBox/Runtime/common/dvm/dvmmbr.cpp')
-rw-r--r-- | src/VBox/Runtime/common/dvm/dvmmbr.cpp | 721 |
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 +}; + |