diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/common/dvm/dvm.cpp | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dvm/dvm.cpp b/src/VBox/Runtime/common/dvm/dvm.cpp new file mode 100644 index 00000000..eaf1f5ac --- /dev/null +++ b/src/VBox/Runtime/common/dvm/dvm.cpp @@ -0,0 +1,940 @@ +/* $Id: dvm.cpp $ */ +/** @file + * IPRT Disk Volume Management API (DVM) - generic code. + */ + +/* + * Copyright (C) 2011-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/types.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/dvm.h> +#include <iprt/err.h> +#include <iprt/asm.h> +#include <iprt/string.h> +#include <iprt/list.h> +#include "internal/dvm.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * The internal volume manager structure. + */ +typedef struct RTDVMINTERNAL +{ + /** The DVM magic (RTDVM_MAGIC). */ + uint32_t u32Magic; + /** The disk descriptor. */ + RTDVMDISK DvmDisk; + /** Pointer to the backend operations table after a successful probe. */ + PCRTDVMFMTOPS pDvmFmtOps; + /** The format specific volume manager data. */ + RTDVMFMT hVolMgrFmt; + /** Flags passed on manager creation. */ + uint32_t fFlags; + /** Reference counter. */ + uint32_t volatile cRefs; + /** List of recognised volumes (RTDVMVOLUMEINTERNAL). */ + RTLISTANCHOR VolumeList; +} RTDVMINTERNAL; +/** Pointer to an internal volume manager. */ +typedef RTDVMINTERNAL *PRTDVMINTERNAL; + +/** + * The internal volume structure. + */ +typedef struct RTDVMVOLUMEINTERNAL +{ + /** The DVM volume magic (RTDVMVOLUME_MAGIC). */ + uint32_t u32Magic; + /** Node for the volume list. */ + RTLISTNODE VolumeNode; + /** Pointer to the owning volume manager. */ + PRTDVMINTERNAL pVolMgr; + /** Format specific volume data. */ + RTDVMVOLUMEFMT hVolFmt; + /** Set block status.callback */ + PFNDVMVOLUMEQUERYBLOCKSTATUS pfnQueryBlockStatus; + /** Opaque user data. */ + void *pvUser; + /** Reference counter. */ + uint32_t volatile cRefs; +} RTDVMVOLUMEINTERNAL; +/** Pointer to an internal volume. */ +typedef RTDVMVOLUMEINTERNAL *PRTDVMVOLUMEINTERNAL; + + +/********************************************************************************************************************************* +* Global variables * +*********************************************************************************************************************************/ +/** + * Supported volume formats. + */ +static PCRTDVMFMTOPS const g_aDvmFmts[] = +{ + &g_rtDvmFmtMbr, + &g_rtDvmFmtGpt, + &g_rtDvmFmtBsdLbl +}; + +/** + * Descriptions of the volume types. + * + * This is indexed by RTDVMVOLTYPE. + */ +static const char * const g_apszDvmVolTypes[] = +{ + "Invalid", + "Unknown", + "NTFS", + "FAT12", + "FAT16", + "FAT32", + + "EFI system partition", + + "Mac OS X HFS or HFS+", + "Mac OS X APFS", + + "Linux swap", + "Linux native", + "Linux LVM", + "Linux SoftRaid", + + "FreeBSD", + "NetBSD", + "OpenBSD", + "Solaris", + + "Basic data partition", + "Microsoft reserved partition", + "Windows LDM metadata", + "Windows LDM data", + "Windows recovery partition", + "Windows storage spaces", + + "IBM GPFS", + + "OS/2", +}; +AssertCompile(RT_ELEMENTS(g_apszDvmVolTypes) == RTDVMVOLTYPE_END); + + +/** + * Read from the disk at the given offset, neither the offset nor the size is + * necessary sector aligned. + * + * @returns IPRT status code. + * @param pDisk The disk descriptor to read from. + * @param off Start offset. + * @param pvBuf Destination buffer. + * @param cbRead How much to read. + */ +DECLHIDDEN(int) rtDvmDiskReadUnaligned(PCRTDVMDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead) +{ + size_t const cbSector = (size_t)pDisk->cbSector; + size_t const offDelta = off % cbSector; + size_t const cbDelta = cbRead % cbSector; + if (!cbDelta && !offDelta) + return rtDvmDiskRead(pDisk, off, pvBuf, cbRead); + + int rc; + size_t cbExtra = offDelta + (cbDelta ? cbSector - cbDelta: 0); + uint8_t *pbTmpBuf = (uint8_t *)RTMemTmpAlloc(cbRead + cbExtra); + if (pbTmpBuf) + { + rc = rtDvmDiskRead(pDisk, off - offDelta, pbTmpBuf, cbRead + cbExtra); + if (RT_SUCCESS(rc)) + memcpy(pvBuf, &pbTmpBuf[offDelta], cbRead); + else + RT_BZERO(pvBuf, cbRead); + RTMemTmpFree(pbTmpBuf); + } + else + rc = VERR_NO_TMP_MEMORY; + return rc; +} + + +/** + * Creates a new volume. + * + * @returns IPRT status code. + * @param pThis The DVM map instance. + * @param hVolFmt The format specific volume handle. + * @param phVol Where to store the generic volume handle on success. + */ +static int rtDvmVolumeCreate(PRTDVMINTERNAL pThis, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUME phVol) +{ + PRTDVMVOLUMEINTERNAL pVol = (PRTDVMVOLUMEINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEINTERNAL)); + if (pVol) + { + pVol->u32Magic = RTDVMVOLUME_MAGIC; + pVol->cRefs = 0; + pVol->pVolMgr = pThis; + pVol->hVolFmt = hVolFmt; + + *phVol = pVol; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + +/** + * Destroys a volume handle. + * + * @param pThis The volume manager instance. + * @param pVol The volume to destroy. + */ +static void rtDvmVolumeDestroy(PRTDVMINTERNAL pThis, PRTDVMVOLUMEINTERNAL pVol) +{ + AssertPtr(pThis); + AssertPtr(pThis->pDvmFmtOps); + Assert(pVol->pVolMgr == pThis); + + /* Close the volume. */ + pThis->pDvmFmtOps->pfnVolumeClose(pVol->hVolFmt); + + pVol->u32Magic = RTDVMVOLUME_MAGIC_DEAD; + pVol->pVolMgr = NULL; + pVol->hVolFmt = NIL_RTDVMVOLUMEFMT; + RTMemFree(pVol); +} + + +RTDECL(int) RTDvmCreate(PRTDVM phVolMgr, RTVFSFILE hVfsFile, uint32_t cbSector, uint32_t fFlags) +{ + AssertMsgReturn(!(fFlags & ~DVM_FLAGS_VALID_MASK), ("Invalid flags given %#x\n", fFlags), VERR_INVALID_FLAGS); + uint32_t cRefs = RTVfsFileRetain(hVfsFile); + AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE); + + uint64_t cbDisk; + int rc = RTVfsFileQuerySize(hVfsFile, &cbDisk); + if (RT_SUCCESS(rc)) + { + PRTDVMINTERNAL pThis = (PRTDVMINTERNAL)RTMemAllocZ(sizeof(RTDVMINTERNAL)); + if (pThis) + { + pThis->u32Magic = RTDVM_MAGIC; + pThis->DvmDisk.cbDisk = cbDisk; + pThis->DvmDisk.cbSector = cbSector; + pThis->DvmDisk.hVfsFile = hVfsFile; + + pThis->pDvmFmtOps = NULL; + pThis->hVolMgrFmt = NIL_RTDVMFMT; + pThis->fFlags = fFlags; + pThis->cRefs = 1; + RTListInit(&pThis->VolumeList); + + *phVolMgr = pThis; + return VINF_SUCCESS; + } + rc = VERR_NO_MEMORY; + } + RTVfsFileRelease(hVfsFile); + return rc; +} + + +RTDECL(uint32_t) RTDvmRetain(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + return cRefs; +} + +/** + * Destroys a volume manager handle. + * + * @param pThis The volume manager to destroy. + */ +static void rtDvmDestroy(PRTDVMINTERNAL pThis) +{ + pThis->u32Magic = RTDVM_MAGIC_DEAD; + + if (pThis->hVolMgrFmt != NIL_RTDVMFMT) + { + AssertPtr(pThis->pDvmFmtOps); + + /* */ + PRTDVMVOLUMEINTERNAL pItNext, pIt; + RTListForEachSafe(&pThis->VolumeList, pIt, pItNext, RTDVMVOLUMEINTERNAL, VolumeNode) + { + RTListNodeRemove(&pIt->VolumeNode); + rtDvmVolumeDestroy(pThis, pIt); + } + + /* Let the backend do it's own cleanup first. */ + pThis->pDvmFmtOps->pfnClose(pThis->hVolMgrFmt); + pThis->hVolMgrFmt = NIL_RTDVMFMT; + pThis->pDvmFmtOps = NULL; + } + + pThis->DvmDisk.cbDisk = 0; + pThis->DvmDisk.cbSector = 0; + if (pThis->DvmDisk.hVfsFile != NIL_RTVFSFILE) + { + RTVfsFileRelease(pThis->DvmDisk.hVfsFile); + pThis->DvmDisk.hVfsFile = NIL_RTVFSFILE; + } + + RTMemFree(pThis); +} + +RTDECL(uint32_t) RTDvmRelease(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + if (pThis == NIL_RTDVM) + return 0; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + if (cRefs == 0) + rtDvmDestroy(pThis); + return cRefs; +} + +RTDECL(int) RTDvmMapOpen(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt == NIL_RTDVMFMT, VERR_WRONG_ORDER); + + Assert(!pThis->pDvmFmtOps); + + /* + * Let each format backend have a go at the disk, pick the one which scores the highest. + */ + int rc = VINF_SUCCESS; + uint32_t uScoreMax = RTDVM_MATCH_SCORE_UNSUPPORTED; + PCRTDVMFMTOPS pDvmFmtOpsMatch = NULL; + for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++) + { + uint32_t uScore = 0; + PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i]; + + rc = pDvmFmtOps->pfnProbe(&pThis->DvmDisk, &uScore); + if (RT_SUCCESS(rc)) + { + if (uScore > uScoreMax) + { + pDvmFmtOpsMatch = pDvmFmtOps; + uScoreMax = uScore; + } + } + else + return rc; + } + if (uScoreMax > RTDVM_MATCH_SCORE_UNSUPPORTED) + { + AssertPtr(pDvmFmtOpsMatch); + + /* + * Open the format. + */ + rc = pDvmFmtOpsMatch->pfnOpen(&pThis->DvmDisk, &pThis->hVolMgrFmt); + if (RT_SUCCESS(rc)) + { + pThis->pDvmFmtOps = pDvmFmtOpsMatch; + + /* + * Construct volume list (we're done if none). + */ + uint32_t cVols = pThis->pDvmFmtOps->pfnGetValidVolumes(pThis->hVolMgrFmt); + if (cVols == 0) + return VINF_SUCCESS; + + /* First volume. */ + RTDVMVOLUMEFMT hVolFmt = NIL_RTDVMVOLUMEFMT; + rc = pThis->pDvmFmtOps->pfnQueryFirstVolume(pThis->hVolMgrFmt, &hVolFmt); + if (RT_SUCCESS(rc)) + { + for (;;) + { + PRTDVMVOLUMEINTERNAL pVol = NULL; + rc = rtDvmVolumeCreate(pThis, hVolFmt, &pVol); + if (RT_FAILURE(rc)) + { + pThis->pDvmFmtOps->pfnVolumeClose(hVolFmt); + break; + } + RTListAppend(&pThis->VolumeList, &pVol->VolumeNode); + + /* Done?*/ + cVols--; + if (cVols < 1) + return VINF_SUCCESS; + + /* Next volume. */ + rc = pThis->pDvmFmtOps->pfnQueryNextVolume(pThis->hVolMgrFmt, pVol->hVolFmt, &hVolFmt); + if (RT_FAILURE(rc)) + break; + } + + /* Bail out. */ + PRTDVMVOLUMEINTERNAL pItNext, pIt; + RTListForEachSafe(&pThis->VolumeList, pIt, pItNext, RTDVMVOLUMEINTERNAL, VolumeNode) + { + RTListNodeRemove(&pIt->VolumeNode); + rtDvmVolumeDestroy(pThis, pIt); + } + } + + pDvmFmtOpsMatch->pfnClose(pThis->hVolMgrFmt); + } + } + else + rc = VERR_NOT_SUPPORTED; + return rc; +} + +RTDECL(int) RTDvmMapInitialize(RTDVM hVolMgr, const char *pszFmt) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertPtrReturn(pszFmt, VERR_INVALID_POINTER); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt == NIL_RTDVMFMT, VERR_WRONG_ORDER); + + for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++) + { + PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i]; + if (!RTStrCmp(pDvmFmtOps->pszFmt, pszFmt)) + { + int rc = pDvmFmtOps->pfnInitialize(&pThis->DvmDisk, &pThis->hVolMgrFmt); + if (RT_SUCCESS(rc)) + pThis->pDvmFmtOps = pDvmFmtOps; + return rc; + } + } + return VERR_NOT_SUPPORTED; +} + +RTDECL(const char *) RTDvmMapGetFormatName(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, NULL); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, NULL); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, NULL); + + return pThis->pDvmFmtOps->pszFmt; +} + +RTDECL(RTDVMFORMATTYPE) RTDvmMapGetFormatType(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, RTDVMFORMATTYPE_INVALID); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, RTDVMFORMATTYPE_INVALID); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, RTDVMFORMATTYPE_INVALID); + + return pThis->pDvmFmtOps->enmFormat; +} + +RTDECL(int) RTDvmMapQueryDiskUuid(RTDVM hVolMgr, PRTUUID pUuid) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE); + AssertPtrReturn(pUuid, VERR_INVALID_POINTER); + + if (pThis->pDvmFmtOps->pfnQueryDiskUuid) + return pThis->pDvmFmtOps->pfnQueryDiskUuid(pThis->hVolMgrFmt, pUuid); + return VERR_NOT_SUPPORTED; +} + +RTDECL(uint32_t) RTDvmMapGetValidVolumes(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, UINT32_MAX); + + return pThis->pDvmFmtOps->pfnGetValidVolumes(pThis->hVolMgrFmt); +} + +RTDECL(uint32_t) RTDvmMapGetMaxVolumes(RTDVM hVolMgr) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, UINT32_MAX); + + return pThis->pDvmFmtOps->pfnGetMaxVolumes(pThis->hVolMgrFmt); +} + +RTDECL(int) RTDvmMapQueryFirstVolume(RTDVM hVolMgr, PRTDVMVOLUME phVol) +{ + PRTDVMINTERNAL pThis = hVolMgr; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE); + AssertPtrReturn(phVol, VERR_INVALID_POINTER); + + int rc = VERR_DVM_MAP_EMPTY; + PRTDVMVOLUMEINTERNAL pVol = RTListGetFirst(&pThis->VolumeList, RTDVMVOLUMEINTERNAL, VolumeNode); + if (pVol) + { + rc = VINF_SUCCESS; + RTDvmVolumeRetain(pVol); + *phVol = pVol; + } + + return rc; +} + +RTDECL(int) RTDvmMapQueryNextVolume(RTDVM hVolMgr, RTDVMVOLUME hVol, PRTDVMVOLUME phVolNext) +{ + PRTDVMINTERNAL pThis = hVolMgr; + PRTDVMVOLUMEINTERNAL pVol = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE); + AssertPtrReturn(pVol, VERR_INVALID_HANDLE); + AssertReturn(pVol->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(phVolNext, VERR_INVALID_POINTER); + + int rc = VERR_DVM_MAP_NO_VOLUME; + PRTDVMVOLUMEINTERNAL pVolNext = RTListGetNext(&pThis->VolumeList, pVol, RTDVMVOLUMEINTERNAL, VolumeNode); + if (pVolNext) + { + rc = VINF_SUCCESS; + RTDvmVolumeRetain(pVolNext); + *phVolNext = pVolNext; + } + + return rc; +} + +RTDECL(int) RTDvmMapQueryBlockStatus(RTDVM hVolMgr, uint64_t off, uint64_t cb, bool *pfAllocated) +{ + PRTDVMINTERNAL pThis = hVolMgr; + + /* + * Input validation. + */ + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertPtrReturn(pfAllocated, VERR_INVALID_POINTER); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_WRONG_ORDER); + AssertMsgReturn( off <= pThis->DvmDisk.cbDisk + || cb <= pThis->DvmDisk.cbDisk + || off + cb <= pThis->DvmDisk.cbDisk, + ("off=%#RX64 cb=%#RX64 cbDisk=%#RX64\n", off, cb, pThis->DvmDisk.cbDisk), + VERR_OUT_OF_RANGE); + + /* + * Check whether the range is inuse by the volume manager metadata first. + */ + int rc = pThis->pDvmFmtOps->pfnQueryRangeUse(pThis->hVolMgrFmt, off, cb, pfAllocated); + if (RT_FAILURE(rc) || *pfAllocated) + return rc; + + /* + * Not used by volume manager metadata, so work thru the specified range one + * volume / void (free space) at a time. All must be unallocated for us to + * reach the end, we return immediately if any portion is allocated. + */ + while (cb > 0) + { + /* + * Search through all volumes. + * + * It is not possible to get all start sectors and sizes of all volumes + * here because volumes can be scattered around the disk for certain formats. + * Linux LVM is one example, it extents of logical volumes don't need to be + * contiguous on the medium. + */ + bool fVolFound = false; + PRTDVMVOLUMEINTERNAL pVol; + RTListForEach(&pThis->VolumeList, pVol, RTDVMVOLUMEINTERNAL, VolumeNode) + { + uint64_t cbIntersect; + uint64_t offVol; + bool fIntersect = pThis->pDvmFmtOps->pfnVolumeIsRangeIntersecting(pVol->hVolFmt, off, cb, &offVol, &cbIntersect); + if (fIntersect) + { + fVolFound = true; + if (pVol->pfnQueryBlockStatus) + { + bool fVolAllocated = true; + rc = pVol->pfnQueryBlockStatus(pVol->pvUser, offVol, cbIntersect, &fVolAllocated); + if (RT_FAILURE(rc) || fVolAllocated) + { + *pfAllocated = true; + return rc; + } + } + else if (!(pThis->fFlags & DVM_FLAGS_NO_STATUS_CALLBACK_MARK_AS_UNUSED)) + { + *pfAllocated = true; + return VINF_SUCCESS; + } + /* else, flag is set, continue. */ + + cb -= cbIntersect; + off += cbIntersect; + break; + } + } + + if (!fVolFound) + { + if (pThis->fFlags & DVM_FLAGS_UNUSED_SPACE_MARK_AS_USED) + { + *pfAllocated = true; + return VINF_SUCCESS; + } + + cb -= pThis->DvmDisk.cbSector; + off += pThis->DvmDisk.cbSector; + } + } + + *pfAllocated = false; + return rc; +} + +RTDECL(int) RTDvmMapQueryTableLocations(RTDVM hVolMgr, uint32_t fFlags, + PRTDVMTABLELOCATION paLocations, size_t cLocations, size_t *pcActual) +{ + PRTDVMINTERNAL pThis = hVolMgr; + + /* + * Input validation. + */ + if (cLocations) + { + AssertPtrReturn(paLocations, VERR_INVALID_POINTER); + if (pcActual) + { + AssertPtrReturn(pcActual, VERR_INVALID_POINTER); + *pcActual = 0; + } + } + else + { + AssertPtrReturn(pcActual, VERR_INVALID_POINTER); + *pcActual = 0; + } + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!(fFlags & ~RTDVMMAPQTABLOC_F_VALID_MASK), VERR_INVALID_FLAGS); + + /* + * Pass it down to the format backend. + */ + return pThis->pDvmFmtOps->pfnQueryTableLocations(pThis->hVolMgrFmt, fFlags, paLocations, cLocations, pcActual); +} + +RTDECL(uint32_t) RTDvmVolumeRetain(RTDVMVOLUME hVol) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + AssertMsg(cRefs >= 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + if (cRefs == 1) + RTDvmRetain(pThis->pVolMgr); + return cRefs; +} + +RTDECL(uint32_t) RTDvmVolumeRelease(RTDVMVOLUME hVol) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + if (pThis == NIL_RTDVMVOLUME) + return 0; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + if (cRefs == 0) + { + /* Release the volume manager. */ + pThis->pfnQueryBlockStatus = NULL; + RTDvmRelease(pThis->pVolMgr); + } + return cRefs; +} + +RTDECL(void) RTDvmVolumeSetQueryBlockStatusCallback(RTDVMVOLUME hVol, + PFNDVMVOLUMEQUERYBLOCKSTATUS pfnQueryBlockStatus, + void *pvUser) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTDVMVOLUME_MAGIC); + + pThis->pfnQueryBlockStatus = pfnQueryBlockStatus; + pThis->pvUser = pvUser; +} + +RTDECL(uint64_t) RTDvmVolumeGetSize(RTDVMVOLUME hVol) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, 0); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetSize(pThis->hVolFmt); +} + +RTDECL(int) RTDvmVolumeQueryName(RTDVMVOLUME hVol, char **ppszVolName) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(ppszVolName, VERR_INVALID_POINTER); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryName(pThis->hVolFmt, ppszVolName); +} + +RTDECL(RTDVMVOLTYPE) RTDvmVolumeGetType(RTDVMVOLUME hVol) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, RTDVMVOLTYPE_INVALID); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, RTDVMVOLTYPE_INVALID); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetType(pThis->hVolFmt); +} + +RTDECL(uint64_t) RTDvmVolumeGetFlags(RTDVMVOLUME hVol) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, UINT64_MAX); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT64_MAX); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetFlags(pThis->hVolFmt); +} + +RTDECL(int) RTDvmVolumeQueryRange(RTDVMVOLUME hVol, uint64_t *poffStart, uint64_t *poffLast) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(poffStart, VERR_INVALID_POINTER); + AssertPtrReturn(poffLast, VERR_INVALID_POINTER); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryRange(pThis->hVolFmt, poffStart, poffLast); +} + +RTDECL(int) RTDvmVolumeQueryTableLocation(RTDVMVOLUME hVol, uint64_t *poffTable, uint64_t *pcbTable) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(poffTable, VERR_INVALID_POINTER); + AssertPtrReturn(pcbTable, VERR_INVALID_POINTER); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryTableLocation(pThis->hVolFmt, poffTable, pcbTable); +} + +RTDECL(uint32_t) RTDvmVolumeGetIndex(RTDVMVOLUME hVol, RTDVMVOLIDX enmIndex) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX); + AssertReturn(enmIndex > RTDVMVOLIDX_INVALID && enmIndex < RTDVMVOLIDX_END, UINT32_MAX); + + if (enmIndex == RTDVMVOLIDX_HOST) + { +#ifdef RT_OS_WINDOWS + enmIndex = RTDVMVOLIDX_USER_VISIBLE; +#elif defined(RT_OS_LINUX) \ + || defined(RT_OS_FREEBSD) \ + || defined(RT_OS_NETBSD) \ + || defined(RT_OS_SOLARIS) \ + || defined(RT_OS_DARWIN) \ + || defined(RT_OS_OS2) /*whatever*/ +/* Darwing and freebsd matches the linux algo. Solaris matches linux algo partially, at least, in the part we use. */ + enmIndex = RTDVMVOLIDX_LINUX; +#else +# error "PORTME" +#endif + } + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetIndex(pThis->hVolFmt, enmIndex); +} + +/** + * Helper for RTDvmVolumeQueryProp. + */ +static void rtDvmReturnInteger(void *pvDst, size_t cbDst, PRTUINT64U pSrc, size_t cbSrc) +{ + /* Read the source: */ + uint64_t uSrc; + switch (cbSrc) + { + case sizeof(uint8_t): uSrc = (uint8_t)pSrc->Words.w0; break; + case sizeof(uint16_t): uSrc = pSrc->Words.w0; break; + case sizeof(uint32_t): uSrc = pSrc->s.Lo; break; + default: AssertFailed(); RT_FALL_THROUGH(); + case sizeof(uint64_t): uSrc = pSrc->u; break; + } + + /* Write the destination: */ + switch (cbDst) + { + default: AssertFailed(); RT_FALL_THROUGH(); + case sizeof(uint8_t): *(uint8_t *)pvDst = (uint8_t)uSrc; break; + case sizeof(uint16_t): *(uint16_t *)pvDst = (uint16_t)uSrc; break; + case sizeof(uint32_t): *(uint32_t *)pvDst = (uint32_t)uSrc; break; + case sizeof(uint64_t): *(uint64_t *)pvDst = uSrc; break; + } +} + +RTDECL(int) RTDvmVolumeQueryProp(RTDVMVOLUME hVol, RTDVMVOLPROP enmProperty, void *pvBuf, size_t cbBuf, size_t *pcbBuf) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + size_t cbBufFallback = 0; + if (pcbBuf == NULL) + pcbBuf = &cbBufFallback; + AssertReturnStmt(enmProperty > RTDVMVOLPROP_INVALID && enmProperty < RTDVMVOLPROP_END, *pcbBuf = 0, VERR_INVALID_FUNCTION); + + switch (enmProperty) + { + /* 8, 16, 32 or 64 bit sized integers: */ + case RTDVMVOLPROP_MBR_FIRST_HEAD: + case RTDVMVOLPROP_MBR_FIRST_SECTOR: + case RTDVMVOLPROP_MBR_LAST_HEAD: + case RTDVMVOLPROP_MBR_LAST_SECTOR: + case RTDVMVOLPROP_MBR_TYPE: + { + *pcbBuf = sizeof(uint8_t); + AssertReturn( cbBuf == sizeof(uint8_t) + || cbBuf == sizeof(uint16_t) + || cbBuf == sizeof(uint32_t) + || cbBuf == sizeof(uint64_t), VERR_INVALID_PARAMETER); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + + RTUINT64U Union64 = {0}; + int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Union64, cbBuf, pcbBuf); + rtDvmReturnInteger(pvBuf, cbBuf, &Union64, *pcbBuf); + return rc; + } + + /* 16, 32 or 64 bit sized integers: */ + case RTDVMVOLPROP_MBR_FIRST_CYLINDER: + case RTDVMVOLPROP_MBR_LAST_CYLINDER: + { + *pcbBuf = sizeof(uint16_t); + AssertReturn( cbBuf == sizeof(uint16_t) + || cbBuf == sizeof(uint32_t) + || cbBuf == sizeof(uint64_t), VERR_INVALID_PARAMETER); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + + RTUINT64U Union64 = {0}; + int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Union64, cbBuf, pcbBuf); + rtDvmReturnInteger(pvBuf, cbBuf, &Union64, *pcbBuf); + return rc; + } + + /* RTUUIDs: */ + case RTDVMVOLPROP_GPT_TYPE: + case RTDVMVOLPROP_GPT_UUID: + { + *pcbBuf = sizeof(RTUUID); + AssertReturn(cbBuf == sizeof(RTUUID), VERR_INVALID_PARAMETER); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + + RTUUID Uuid = RTUUID_INITIALIZE_NULL; + int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Uuid, sizeof(RTUUID), pcbBuf); + memcpy(pvBuf, &Uuid, sizeof(Uuid)); + return rc; + } + + case RTDVMVOLPROP_INVALID: + case RTDVMVOLPROP_END: + case RTDVMVOLPROP_32BIT_HACK: + break; + /* No default case! */ + } + AssertFailed(); + return VERR_NOT_SUPPORTED; +} + +RTDECL(uint64_t) RTDvmVolumeGetPropU64(RTDVMVOLUME hVol, RTDVMVOLPROP enmProperty, uint64_t uDefault) +{ + uint64_t uValue = uDefault; + int rc = RTDvmVolumeQueryProp(hVol, enmProperty, &uValue, sizeof(uValue), NULL); + if (RT_SUCCESS(rc)) + return uValue; + AssertMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_FOUND, ("%Rrc enmProperty=%d\n", rc, enmProperty)); + return uDefault; +} + +RTDECL(int) RTDvmVolumeRead(RTDVMVOLUME hVol, uint64_t off, void *pvBuf, size_t cbRead) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeRead(pThis->hVolFmt, off, pvBuf, cbRead); +} + +RTDECL(int) RTDvmVolumeWrite(RTDVMVOLUME hVol, uint64_t off, const void *pvBuf, size_t cbWrite) +{ + PRTDVMVOLUMEINTERNAL pThis = hVol; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER); + + return pThis->pVolMgr->pDvmFmtOps->pfnVolumeWrite(pThis->hVolFmt, off, pvBuf, cbWrite); +} + +RTDECL(const char *) RTDvmVolumeTypeGetDescr(RTDVMVOLTYPE enmVolType) +{ + AssertReturn(enmVolType >= RTDVMVOLTYPE_INVALID && enmVolType < RTDVMVOLTYPE_END, NULL); + + return g_apszDvmVolTypes[enmVolType]; +} + |