summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dvm
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/dvm')
-rw-r--r--src/VBox/Runtime/common/dvm/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/dvm/dvm.cpp940
-rw-r--r--src/VBox/Runtime/common/dvm/dvmbsdlabel.cpp707
-rw-r--r--src/VBox/Runtime/common/dvm/dvmgpt.cpp755
-rw-r--r--src/VBox/Runtime/common/dvm/dvmmbr.cpp1069
-rw-r--r--src/VBox/Runtime/common/dvm/dvmvfs.cpp1467
6 files changed, 4938 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dvm/Makefile.kup b/src/VBox/Runtime/common/dvm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/Makefile.kup
diff --git a/src/VBox/Runtime/common/dvm/dvm.cpp b/src/VBox/Runtime/common/dvm/dvm.cpp
new file mode 100644
index 00000000..591e4562
--- /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-2023 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];
+}
+
diff --git a/src/VBox/Runtime/common/dvm/dvmbsdlabel.cpp b/src/VBox/Runtime/common/dvm/dvmbsdlabel.cpp
new file mode 100644
index 00000000..32b5a740
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmbsdlabel.cpp
@@ -0,0 +1,707 @@
+/* $Id: dvmbsdlabel.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - BSD disklabel format backend.
+ */
+
+/*
+ * Copyright (C) 2011-2023 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
+ */
+
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/dvm.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include "internal/dvm.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/*
+ * Below are the on disk structures of a bsd disklabel as found in
+ * /usr/include/sys/disklabel.h from a FreeBSD system.
+ *
+ * Everything is stored in little endian on the disk.
+ */
+
+/** BSD disklabel magic. */
+#define RTDVM_BSDLBL_MAGIC UINT32_C(0x82564557)
+/** Maximum number of partitions in the label. */
+#define RTDVM_BSDLBL_MAX_PARTITIONS 8
+
+/**
+ * A BSD disk label partition.
+ */
+typedef struct BsdLabelPartition
+{
+ /** Number of sectors in the partition. */
+ uint32_t cSectors;
+ /** Start sector. */
+ uint32_t offSectorStart;
+ /** Filesystem fragment size. */
+ uint32_t cbFsFragment;
+ /** Filesystem type. */
+ uint8_t bFsType;
+ /** Filesystem fragments per block. */
+ uint8_t cFsFragmentsPerBlock;
+ /** Filesystem cylinders per group. */
+ uint16_t cFsCylPerGroup;
+} BsdLabelPartition;
+AssertCompileSize(BsdLabelPartition, 16);
+/** Pointer to a BSD disklabel partition structure. */
+typedef BsdLabelPartition *PBsdLabelPartition;
+
+/**
+ * On disk BSD label structure.
+ */
+typedef struct BsdLabel
+{
+ /** Magic identifying the BSD disk label. */
+ uint32_t u32Magic;
+ /** Drive type */
+ uint16_t u16DriveType;
+ /** Subtype depending on the drive type above. */
+ uint16_t u16SubType;
+ /** Type name. */
+ uint8_t abTypeName[16];
+ /** Pack identifier. */
+ uint8_t abPackName[16];
+ /** Number of bytes per sector. */
+ uint32_t cbSector;
+ /** Number of sectors per track. */
+ uint32_t cSectorsPerTrack;
+ /** Number of tracks per cylinder. */
+ uint32_t cTracksPerCylinder;
+ /** Number of data cylinders pre unit. */
+ uint32_t cDataCylindersPerUnit;
+ /** Number of data sectors per cylinder. */
+ uint32_t cDataSectorsPerCylinder;
+ /** Number of data sectors per unit (unit as in disk drive?). */
+ uint32_t cSectorsPerUnit;
+ /** Number of spare sectors per track. */
+ uint16_t cSpareSectorsPerTrack;
+ /** Number of spare sectors per cylinder. */
+ uint16_t cSpareSectorsPerCylinder;
+ /** Number of alternate cylinders per unit. */
+ uint32_t cSpareCylindersPerUnit;
+ /** Rotational speed of the disk drive in rotations per minute. */
+ uint16_t cRotationsPerMinute;
+ /** Sector interleave. */
+ uint16_t uSectorInterleave;
+ /** Sector 0 skew, per track. */
+ uint16_t uSectorSkewPerTrack;
+ /** Sector 0 skew, per cylinder. */
+ uint16_t uSectorSkewPerCylinder;
+ /** Head switch time in us. */
+ uint32_t usHeadSwitch;
+ /** Time of a track-to-track seek in us. */
+ uint32_t usTrackSeek;
+ /** Flags. */
+ uint32_t fFlags;
+ /** Drive type sepcific information. */
+ uint32_t au32DriveData[5];
+ /** Reserved. */
+ uint32_t au32Reserved[5];
+ /** The magic number again. */
+ uint32_t u32Magic2;
+ /** Checksum (xor of the whole structure). */
+ uint16_t u16ChkSum;
+ /** Number of partitions in the array. */
+ uint16_t cPartitions;
+ /** Boot area size in bytes. */
+ uint32_t cbBootArea;
+ /** Maximum size of the filesystem super block. */
+ uint32_t cbFsSuperBlock;
+ /** The partition array. */
+ BsdLabelPartition aPartitions[RTDVM_BSDLBL_MAX_PARTITIONS];
+} BsdLabel;
+AssertCompileSize(BsdLabel, 148 + RTDVM_BSDLBL_MAX_PARTITIONS * 16);
+/** Pointer to a BSD disklabel structure. */
+typedef BsdLabel *PBsdLabel;
+
+/**
+ * BSD disk label volume manager data.
+ */
+typedef struct RTDVMFMTINTERNAL
+{
+ /** Pointer to the underlying disk. */
+ PCRTDVMDISK pDisk;
+ /** Number of used partitions. */
+ uint32_t cPartitions;
+ /** Saved BSD disklabel structure. */
+ BsdLabel DiskLabel;
+} RTDVMFMTINTERNAL;
+/** Pointer to the MBR volume manager. */
+typedef RTDVMFMTINTERNAL *PRTDVMFMTINTERNAL;
+
+/**
+ * MBR volume data.
+ */
+typedef struct RTDVMVOLUMEFMTINTERNAL
+{
+ /** Pointer to the volume manager. */
+ PRTDVMFMTINTERNAL pVolMgr;
+ /** Partition table entry index. */
+ uint32_t idxEntry;
+ /** Start offset of the volume. */
+ uint64_t offStart;
+ /** Size of the volume. */
+ uint64_t cbVolume;
+ /** Pointer to the raw partition table entry. */
+ PBsdLabelPartition pBsdPartitionEntry;
+} RTDVMVOLUMEFMTINTERNAL;
+/** Pointer to an MBR volume. */
+typedef RTDVMVOLUMEFMTINTERNAL *PRTDVMVOLUMEFMTINTERNAL;
+
+/** Converts a LBA number to the byte offset. */
+#define RTDVM_BSDLBL_LBA2BYTE(lba, disk) ((lba) * (disk)->cbSector)
+/** Converts a Byte offset to the LBA number. */
+#define RTDVM_BSDLBL_BYTE2LBA(lba, disk) ((lba) / (disk)->cbSector)
+
+/**
+ * Calculates the checksum of the entire bsd disklabel structure.
+ *
+ * @returns The checksum.
+ * @param pBsdLabel BSD disklabel to get the checksum for.
+ */
+static uint16_t rtDvmFmtBsdLblDiskLabelChkSum(PBsdLabel pBsdLabel)
+{
+ uint16_t uChkSum = 0;
+ uint16_t *pCurr = (uint16_t *)pBsdLabel;
+ uint16_t *pEnd = (uint16_t *)&pBsdLabel->aPartitions[pBsdLabel->cPartitions];
+
+ while (pCurr < pEnd)
+ uChkSum ^= *pCurr++;
+
+ return uChkSum;
+}
+
+/**
+ * Converts a partition entry to the host endianness.
+ *
+ * @param pPartition The partition to decode.
+ */
+static void rtDvmFmtBsdLblDiskLabelDecodePartition(PBsdLabelPartition pPartition)
+{
+ pPartition->cSectors = RT_LE2H_U32(pPartition->cSectors);
+ pPartition->offSectorStart = RT_LE2H_U32(pPartition->offSectorStart);
+ pPartition->cbFsFragment = RT_LE2H_U32(pPartition->cbFsFragment);
+ pPartition->cFsCylPerGroup = RT_LE2H_U16(pPartition->cFsCylPerGroup);
+}
+
+/**
+ * Converts the on disk BSD label to the host endianness.
+ *
+ * @returns Whether the given label structure is a valid BSD disklabel.
+ * @param pBsdLabel Pointer to the BSD disklabel to decode.
+ */
+static bool rtDvmFmtBsdLblDiskLabelDecode(PBsdLabel pBsdLabel)
+{
+ pBsdLabel->u32Magic = RT_LE2H_U32(pBsdLabel->u32Magic);
+ pBsdLabel->u16DriveType = RT_LE2H_U16(pBsdLabel->u16DriveType);
+ pBsdLabel->u16SubType = RT_LE2H_U16(pBsdLabel->u16SubType);
+ pBsdLabel->cbSector = RT_LE2H_U32(pBsdLabel->cbSector);
+ pBsdLabel->cSectorsPerTrack = RT_LE2H_U32(pBsdLabel->cSectorsPerTrack);
+ pBsdLabel->cTracksPerCylinder = RT_LE2H_U32(pBsdLabel->cTracksPerCylinder);
+ pBsdLabel->cDataCylindersPerUnit = RT_LE2H_U32(pBsdLabel->cDataCylindersPerUnit);
+ pBsdLabel->cDataSectorsPerCylinder = RT_LE2H_U32(pBsdLabel->cDataSectorsPerCylinder);
+ pBsdLabel->cSectorsPerUnit = RT_LE2H_U32(pBsdLabel->cSectorsPerUnit);
+ pBsdLabel->cSpareSectorsPerTrack = RT_LE2H_U16(pBsdLabel->cSpareSectorsPerTrack);
+ pBsdLabel->cSpareSectorsPerCylinder = RT_LE2H_U16(pBsdLabel->cSpareSectorsPerCylinder);
+ pBsdLabel->cSpareCylindersPerUnit = RT_LE2H_U32(pBsdLabel->cSpareCylindersPerUnit);
+ pBsdLabel->cRotationsPerMinute = RT_LE2H_U16(pBsdLabel->cRotationsPerMinute);
+ pBsdLabel->uSectorInterleave = RT_LE2H_U16(pBsdLabel->uSectorInterleave);
+ pBsdLabel->uSectorSkewPerTrack = RT_LE2H_U16(pBsdLabel->uSectorSkewPerTrack);
+ pBsdLabel->uSectorSkewPerCylinder = RT_LE2H_U16(pBsdLabel->uSectorSkewPerCylinder);
+ pBsdLabel->usHeadSwitch = RT_LE2H_U16(pBsdLabel->usHeadSwitch);
+ pBsdLabel->usTrackSeek = RT_LE2H_U16(pBsdLabel->usTrackSeek);
+ pBsdLabel->fFlags = RT_LE2H_U32(pBsdLabel->fFlags);
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pBsdLabel->au32DriveData); i++)
+ pBsdLabel->au32DriveData[i] = RT_LE2H_U32(pBsdLabel->au32DriveData[i]);
+ for (unsigned i = 0; i < RT_ELEMENTS(pBsdLabel->au32Reserved); i++)
+ pBsdLabel->au32Reserved[i] = RT_LE2H_U32(pBsdLabel->au32Reserved[i]);
+
+ pBsdLabel->u32Magic2 = RT_LE2H_U32(pBsdLabel->u32Magic2);
+ pBsdLabel->u16ChkSum = RT_LE2H_U16(pBsdLabel->u16ChkSum);
+ pBsdLabel->cPartitions = RT_LE2H_U16(pBsdLabel->cPartitions);
+ pBsdLabel->cbBootArea = RT_LE2H_U32(pBsdLabel->cbBootArea);
+ pBsdLabel->cbFsSuperBlock = RT_LE2H_U32(pBsdLabel->cbFsSuperBlock);
+
+ /* Check the magics now. */
+ if ( pBsdLabel->u32Magic != RTDVM_BSDLBL_MAGIC
+ || pBsdLabel->u32Magic2 != RTDVM_BSDLBL_MAGIC
+ || pBsdLabel->cPartitions != RTDVM_BSDLBL_MAX_PARTITIONS)
+ return false;
+
+ /* Convert the partitions array. */
+ for (unsigned i = 0; i < RT_ELEMENTS(pBsdLabel->aPartitions); i++)
+ rtDvmFmtBsdLblDiskLabelDecodePartition(&pBsdLabel->aPartitions[i]);
+
+ /* Check the checksum now. */
+ uint16_t u16ChkSumSaved = pBsdLabel->u16ChkSum;
+
+ pBsdLabel->u16ChkSum = 0;
+ if (u16ChkSumSaved != rtDvmFmtBsdLblDiskLabelChkSum(pBsdLabel))
+ return false;
+
+ pBsdLabel->u16ChkSum = u16ChkSumSaved;
+ return true;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblProbe(PCRTDVMDISK pDisk, uint32_t *puScore)
+{
+ BsdLabel DiskLabel;
+ int rc = VINF_SUCCESS;
+
+ *puScore = RTDVM_MATCH_SCORE_UNSUPPORTED;
+
+ if (pDisk->cbDisk >= sizeof(BsdLabel))
+ {
+ /* Read from the disk and check for the disk label structure. */
+ rc = rtDvmDiskReadUnaligned(pDisk, RTDVM_BSDLBL_LBA2BYTE(1, pDisk), &DiskLabel, sizeof(BsdLabel));
+ if ( RT_SUCCESS(rc)
+ && rtDvmFmtBsdLblDiskLabelDecode(&DiskLabel))
+ *puScore = RTDVM_MATCH_SCORE_PERFECT;
+ }
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblOpen(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ int rc = VINF_SUCCESS;
+ PRTDVMFMTINTERNAL pThis = NULL;
+
+ pThis = (PRTDVMFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMFMTINTERNAL));
+ if (pThis)
+ {
+ pThis->pDisk = pDisk;
+ pThis->cPartitions = 0;
+
+ /* Read from the disk and check for the disk label structure. */
+ rc = rtDvmDiskReadUnaligned(pDisk, RTDVM_BSDLBL_LBA2BYTE(1, pDisk), &pThis->DiskLabel, sizeof(BsdLabel));
+ if ( RT_SUCCESS(rc)
+ && rtDvmFmtBsdLblDiskLabelDecode(&pThis->DiskLabel))
+ {
+ /* Count number of used entries. */
+ for (unsigned i = 0; i < pThis->DiskLabel.cPartitions; i++)
+ if (pThis->DiskLabel.aPartitions[i].cSectors)
+ pThis->cPartitions++;
+
+ *phVolMgrFmt = pThis;
+ }
+ else
+ {
+ RTMemFree(pThis);
+ rc = VERR_INVALID_MAGIC;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ NOREF(pDisk); NOREF(phVolMgrFmt);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static DECLCALLBACK(void) rtDvmFmtBsdLblClose(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ pThis->pDisk = NULL;
+ pThis->cPartitions = 0;
+ RT_ZERO(pThis->DiskLabel);
+ RTMemFree(pThis);
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblQueryRangeUse(RTDVMFMT hVolMgrFmt,
+ uint64_t off, uint64_t cbRange,
+ bool *pfUsed)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ NOREF(cbRange);
+
+ if (off <= RTDVM_BSDLBL_LBA2BYTE(1, pThis->pDisk))
+ *pfUsed = true;
+ else
+ *pfUsed = false;
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtBsdLblGetValidVolumes(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ return pThis->cPartitions;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtBsdLblGetMaxVolumes(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ return pThis->DiskLabel.cPartitions;
+}
+
+/**
+ * Creates a new volume.
+ *
+ * @returns IPRT status code.
+ * @param pThis The MBR volume manager data.
+ * @param pbBsdLblEntry The raw MBR entry data.
+ * @param idx The index in the partition table.
+ * @param phVolFmt Where to store the volume data on success.
+ */
+static int rtDvmFmtBsdLblVolumeCreate(PRTDVMFMTINTERNAL pThis, PBsdLabelPartition pBsdPartitionEntry,
+ uint32_t idx, PRTDVMVOLUMEFMT phVolFmt)
+{
+ int rc = VINF_SUCCESS;
+ PRTDVMVOLUMEFMTINTERNAL pVol = (PRTDVMVOLUMEFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEFMTINTERNAL));
+
+ if (pVol)
+ {
+ pVol->pVolMgr = pThis;
+ pVol->idxEntry = idx;
+ pVol->pBsdPartitionEntry = pBsdPartitionEntry;
+ pVol->offStart = (uint64_t)pBsdPartitionEntry->offSectorStart * pThis->DiskLabel.cbSector;
+ pVol->cbVolume = (uint64_t)pBsdPartitionEntry->cSectors * pThis->DiskLabel.cbSector;
+
+ *phVolFmt = pVol;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblQueryFirstVolume(RTDVMFMT hVolMgrFmt, PRTDVMVOLUMEFMT phVolFmt)
+{
+ int rc = VINF_SUCCESS;
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ if (pThis->cPartitions != 0)
+ {
+ /* Search for the first non empty entry. */
+ for (unsigned i = 0; i < pThis->DiskLabel.cPartitions; i++)
+ {
+ if (pThis->DiskLabel.aPartitions[i].cSectors)
+ {
+ rc = rtDvmFmtBsdLblVolumeCreate(pThis, &pThis->DiskLabel.aPartitions[i], i, phVolFmt);
+ break;
+ }
+ }
+ }
+ else
+ rc = VERR_DVM_MAP_EMPTY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblQueryNextVolume(RTDVMFMT hVolMgrFmt, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUMEFMT phVolFmtNext)
+{
+ int rc = VERR_DVM_MAP_NO_VOLUME;
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ PBsdLabelPartition pBsdPartitionEntry = pVol->pBsdPartitionEntry + 1;
+
+ for (unsigned i = pVol->idxEntry + 1; i < pThis->DiskLabel.cPartitions; i++)
+ {
+ if (pBsdPartitionEntry->cSectors)
+ {
+ rc = rtDvmFmtBsdLblVolumeCreate(pThis, pBsdPartitionEntry, i, phVolFmtNext);
+ break;
+ }
+ pBsdPartitionEntry++;
+ }
+
+ return rc;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnQueryTableLocations */
+static DECLCALLBACK(int) rtDvmFmtBsdLblQueryTableLocations(RTDVMFMT hVolMgrFmt, uint32_t fFlags, PRTDVMTABLELOCATION paLocations,
+ size_t cLocations, size_t *pcActual)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ /*
+ * The MBR if requested.
+ */
+ int rc = VINF_SUCCESS;
+ size_t iLoc = 0;
+ if (fFlags & RTDVMMAPQTABLOC_F_INCLUDE_LEGACY)
+ {
+ if (cLocations > 0)
+ {
+ paLocations[iLoc].off = 0;
+ paLocations[iLoc].cb = RTDVM_BSDLBL_LBA2BYTE(1, pThis->pDisk);
+ paLocations[iLoc].cbPadding = 0;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+ }
+
+ /*
+ * The BSD lable.
+ */
+ if (cLocations > iLoc)
+ {
+ paLocations[iLoc].off = RTDVM_BSDLBL_LBA2BYTE(1, pThis->pDisk);
+ paLocations[iLoc].cb = (sizeof(BsdLabel) + pThis->pDisk->cbSector - 1) / pThis->pDisk->cbSector * pThis->pDisk->cbSector;
+
+ uint32_t offFirstSector = pThis->pDisk->cbDisk / pThis->pDisk->cbSector;
+ for (unsigned i = 0; i < pThis->DiskLabel.cPartitions; i++)
+ if ( pThis->DiskLabel.aPartitions[i].cSectors
+ && pThis->DiskLabel.aPartitions[i].offSectorStart < offFirstSector)
+ offFirstSector = pThis->DiskLabel.aPartitions[i].offSectorStart;
+
+ uint64_t offEnd = paLocations[iLoc].off + paLocations[iLoc].cb;
+ paLocations[iLoc].cbPadding = (uint64_t)offFirstSector * pThis->DiskLabel.cbSector;
+ if (paLocations[iLoc].cbPadding > offEnd)
+ paLocations[iLoc].cbPadding -= offEnd;
+ else
+ AssertFailedStmt(paLocations[iLoc].cbPadding = 0);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+
+ /*
+ * Return values.
+ */
+ if (pcActual)
+ *pcActual = iLoc;
+ else if (cLocations != iLoc && RT_SUCCESS(rc))
+ {
+ RT_BZERO(&paLocations[iLoc], (cLocations - iLoc) * sizeof(paLocations[0]));
+ rc = VERR_BUFFER_UNDERFLOW;
+ }
+ return rc;
+}
+
+static DECLCALLBACK(void) rtDvmFmtBsdLblVolumeClose(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ pVol->pVolMgr = NULL;
+ pVol->offStart = 0;
+ pVol->cbVolume = 0;
+ pVol->pBsdPartitionEntry = NULL;
+
+ RTMemFree(pVol);
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtBsdLblVolumeGetSize(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ return pVol->cbVolume;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeQueryName(RTDVMVOLUMEFMT hVolFmt, char **ppszVolName)
+{
+ NOREF(hVolFmt); NOREF(ppszVolName);
+ return VERR_NOT_SUPPORTED;
+}
+
+static DECLCALLBACK(RTDVMVOLTYPE) rtDvmFmtBsdLblVolumeGetType(RTDVMVOLUMEFMT hVolFmt)
+{
+ NOREF(hVolFmt);
+ return RTDVMVOLTYPE_UNKNOWN;
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtBsdLblVolumeGetFlags(RTDVMVOLUMEFMT hVolFmt)
+{
+ NOREF(hVolFmt);
+ return DVMVOLUME_F_CONTIGUOUS;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeQueryRange(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffStart, uint64_t *poffLast)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ *poffStart = pVol->offStart;
+ *poffLast = pVol->offStart + pVol->cbVolume - 1;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(bool) rtDvmFmtBsdLblVolumeIsRangeIntersecting(RTDVMVOLUMEFMT hVolFmt,
+ uint64_t offStart, size_t cbRange,
+ uint64_t *poffVol,
+ uint64_t *pcbIntersect)
+{
+ bool fIntersect = false;
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ if (RTDVM_RANGE_IS_INTERSECTING(pVol->offStart, pVol->cbVolume, offStart))
+ {
+ fIntersect = true;
+ *poffVol = offStart - pVol->offStart;
+ *pcbIntersect = RT_MIN(cbRange, pVol->offStart + pVol->cbVolume - offStart);
+ }
+
+ return fIntersect;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryTableLocation */
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeQueryTableLocation(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffTable, uint64_t *pcbTable)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ *poffTable = RTDVM_BSDLBL_LBA2BYTE(1, pVol->pVolMgr->pDisk);
+ *pcbTable = RT_ALIGN_Z(sizeof(BsdLabel), pVol->pVolMgr->pDisk->cbSector);
+ return VINF_SUCCESS;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeGetIndex */
+static DECLCALLBACK(uint32_t) rtDvmFmtBsdLblVolumeGetIndex(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLIDX enmIndex)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ switch (enmIndex)
+ {
+ case RTDVMVOLIDX_USER_VISIBLE:
+ case RTDVMVOLIDX_ALL:
+ case RTDVMVOLIDX_LINUX:
+ return pVol->idxEntry + 1;
+ case RTDVMVOLIDX_IN_TABLE:
+ return pVol->idxEntry;
+
+ case RTDVMVOLIDX_INVALID:
+ case RTDVMVOLIDX_HOST:
+ case RTDVMVOLIDX_END:
+ case RTDVMVOLIDX_32BIT_HACK:
+ break;
+ /* no default! */
+ }
+ AssertFailed();
+ return UINT32_MAX;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryProp */
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeQueryProp(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLPROP enmProperty,
+ void *pvBuf, size_t cbBuf, size_t *pcbBuf)
+{
+ switch (enmProperty)
+ {
+ case RTDVMVOLPROP_MBR_FIRST_CYLINDER:
+ case RTDVMVOLPROP_MBR_FIRST_HEAD:
+ case RTDVMVOLPROP_MBR_FIRST_SECTOR:
+ case RTDVMVOLPROP_MBR_LAST_CYLINDER:
+ case RTDVMVOLPROP_MBR_LAST_HEAD:
+ case RTDVMVOLPROP_MBR_LAST_SECTOR:
+ case RTDVMVOLPROP_MBR_TYPE:
+ case RTDVMVOLPROP_GPT_TYPE:
+ case RTDVMVOLPROP_GPT_UUID:
+ return VERR_NOT_SUPPORTED;
+
+ case RTDVMVOLPROP_INVALID:
+ case RTDVMVOLPROP_END:
+ case RTDVMVOLPROP_32BIT_HACK:
+ break;
+ /* no default! */
+ }
+ RT_NOREF(hVolFmt, pvBuf, cbBuf, pcbBuf);
+ return VERR_NOT_SUPPORTED;
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeRead(RTDVMVOLUMEFMT hVolFmt, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbRead <= pVol->cbVolume, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskRead(pVol->pVolMgr->pDisk, pVol->offStart + off, pvBuf, cbRead);
+}
+
+static DECLCALLBACK(int) rtDvmFmtBsdLblVolumeWrite(RTDVMVOLUMEFMT hVolFmt, uint64_t off, const void *pvBuf, size_t cbWrite)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbWrite <= pVol->cbVolume, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskWrite(pVol->pVolMgr->pDisk, pVol->offStart + off, pvBuf, cbWrite);
+}
+
+DECL_HIDDEN_CONST(const RTDVMFMTOPS) g_rtDvmFmtBsdLbl =
+{
+ /* pcszFmt */
+ "BsdLabel",
+ /* enmFormat, */
+ RTDVMFORMATTYPE_BSD_LABEL,
+ /* pfnProbe */
+ rtDvmFmtBsdLblProbe,
+ /* pfnOpen */
+ rtDvmFmtBsdLblOpen,
+ /* pfnInitialize */
+ rtDvmFmtBsdLblInitialize,
+ /* pfnClose */
+ rtDvmFmtBsdLblClose,
+ /* pfnQueryRangeUse */
+ rtDvmFmtBsdLblQueryRangeUse,
+ /* pfnQueryDiskUuid */
+ NULL,
+ /* pfnGetValidVolumes */
+ rtDvmFmtBsdLblGetValidVolumes,
+ /* pfnGetMaxVolumes */
+ rtDvmFmtBsdLblGetMaxVolumes,
+ /* pfnQueryFirstVolume */
+ rtDvmFmtBsdLblQueryFirstVolume,
+ /* pfnQueryNextVolume */
+ rtDvmFmtBsdLblQueryNextVolume,
+ /* pfnQueryTableLocations */
+ rtDvmFmtBsdLblQueryTableLocations,
+ /* pfnVolumeClose */
+ rtDvmFmtBsdLblVolumeClose,
+ /* pfnVolumeGetSize */
+ rtDvmFmtBsdLblVolumeGetSize,
+ /* pfnVolumeQueryName */
+ rtDvmFmtBsdLblVolumeQueryName,
+ /* pfnVolumeGetType */
+ rtDvmFmtBsdLblVolumeGetType,
+ /* pfnVolumeGetFlags */
+ rtDvmFmtBsdLblVolumeGetFlags,
+ /* pfnVolumeQueryRange */
+ rtDvmFmtBsdLblVolumeQueryRange,
+ /* pfnVolumeIsRangeIntersecting */
+ rtDvmFmtBsdLblVolumeIsRangeIntersecting,
+ /* pfnVolumeQueryTableLocation */
+ rtDvmFmtBsdLblVolumeQueryTableLocation,
+ /* pfnVolumeGetIndex */
+ rtDvmFmtBsdLblVolumeGetIndex,
+ /* pfnVolumeQueryProp */
+ rtDvmFmtBsdLblVolumeQueryProp,
+ /* pfnVolumeRead */
+ rtDvmFmtBsdLblVolumeRead,
+ /* pfnVolumeWrite */
+ rtDvmFmtBsdLblVolumeWrite
+};
+
diff --git a/src/VBox/Runtime/common/dvm/dvmgpt.cpp b/src/VBox/Runtime/common/dvm/dvmgpt.cpp
new file mode 100644
index 00000000..d4328dd9
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmgpt.cpp
@@ -0,0 +1,755 @@
+/* $Id: dvmgpt.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - GPT format backend.
+ */
+
+/*
+ * Copyright (C) 2011-2023 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/dvm.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/uuid.h>
+#include "internal/dvm.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** The GPT signature. */
+#define RTDVM_GPT_SIGNATURE "EFI PART"
+
+/**
+ * GPT on disk header.
+ */
+typedef struct GPTHDR
+{
+ /** 0x00: Signature ("EFI PART"). */
+ char abSignature[8];
+ /** 0x08: Revision. */
+ uint32_t u32Revision;
+ /** 0x0c: Header size. */
+ uint32_t cbHeader;
+ /** 0x10: CRC of header. */
+ uint32_t u32Crc;
+} GPTHDR;
+/** Pointer to a GPT header. */
+typedef struct GPTHDR *PGPTHDR;
+AssertCompileSize(GPTHDR, 20);
+
+/**
+ * Complete GPT table header for revision 1.0.
+ */
+#pragma pack(1)
+typedef struct GPTHDRREV1
+{
+ /** 0x00: Header. */
+ GPTHDR Hdr;
+ /** 0x14: Reserved. */
+ uint32_t u32Reserved;
+ /** 0x18: Current LBA. */
+ uint64_t u64LbaCurrent;
+ /** 0x20: Backup LBA. */
+ uint64_t u64LbaBackup;
+ /** 0x28:First usable LBA for partitions. */
+ uint64_t u64LbaFirstPartition;
+ /** 0x30: Last usable LBA for partitions. */
+ uint64_t u64LbaLastPartition;
+ /** 0x38: Disk UUID. */
+ RTUUID DiskUuid;
+ /** 0x48: LBA of first partition entry. */
+ uint64_t u64LbaPartitionEntries;
+ /** 0x50: Number of partition entries. */
+ uint32_t cPartitionEntries;
+ /** 0x54: Partition entry size. */
+ uint32_t cbPartitionEntry;
+ /** 0x58: CRC of partition entries. */
+ uint32_t u32CrcPartitionEntries;
+} GPTHDRREV1;
+/** Pointer to a revision 1.0 GPT header. */
+typedef GPTHDRREV1 *PGPTHDRREV1;
+#pragma pack()
+AssertCompileSize(GPTHDRREV1, 92);
+
+/**
+ * GPT partition table entry.
+ */
+typedef struct GPTENTRY
+{
+ /** 0x00: Partition type UUID. */
+ RTUUID UuidType;
+ /** 0x10: Partition UUID. */
+ RTUUID UuidPartition;
+ /** 0x20: First LBA. */
+ uint64_t u64LbaFirst;
+ /** 0x28: Last LBA. */
+ uint64_t u64LbaLast;
+ /** 0x30: Attribute flags. */
+ uint64_t u64Flags;
+ /** 0x38: Partition name (UTF-16LE code units). */
+ RTUTF16 aPartitionName[36];
+} GPTENTRY;
+/** Pointer to a GPT entry. */
+typedef struct GPTENTRY *PGPTENTRY;
+AssertCompileSize(GPTENTRY, 128);
+
+/** Partition flags - System partition. */
+#define RTDVM_GPT_ENTRY_SYSTEM RT_BIT_64(0)
+/** Partition flags - Partition is readonly. */
+#define RTDVM_GPT_ENTRY_READONLY RT_BIT_64(60)
+/** Partition flags - Partition is hidden. */
+#define RTDVM_GPT_ENTRY_HIDDEN RT_BIT_64(62)
+/** Partition flags - Don't automount this partition. */
+#define RTDVM_GPT_ENTRY_NO_AUTOMOUNT RT_BIT_64(63)
+
+/**
+ * GPT volume manager data.
+ */
+typedef struct RTDVMFMTINTERNAL
+{
+ /** Pointer to the underlying disk. */
+ PCRTDVMDISK pDisk;
+ /** GPT header. */
+ GPTHDRREV1 HdrRev1;
+ /** GPT array. */
+ PGPTENTRY paGptEntries;
+ /** Number of occupied partition entries. */
+ uint32_t cPartitions;
+} RTDVMFMTINTERNAL;
+/** Pointer to the MBR volume manager. */
+typedef RTDVMFMTINTERNAL *PRTDVMFMTINTERNAL;
+
+/**
+ * GPT volume data.
+ */
+typedef struct RTDVMVOLUMEFMTINTERNAL
+{
+ /** Pointer to the volume manager. */
+ PRTDVMFMTINTERNAL pVolMgr;
+ /** Partition table entry index. */
+ uint32_t idxEntry;
+ /** Start offset of the volume. */
+ uint64_t offStart;
+ /** Size of the volume. */
+ uint64_t cbVolume;
+ /** Pointer to the GPT entry in the array. */
+ PGPTENTRY pGptEntry;
+} RTDVMVOLUMEFMTINTERNAL;
+/** Pointer to an MBR volume. */
+typedef RTDVMVOLUMEFMTINTERNAL *PRTDVMVOLUMEFMTINTERNAL;
+
+/**
+ * GPT partition type to DVM volume type mapping entry.
+ */
+
+typedef struct RTDVMGPTPARTTYPE2VOLTYPE
+{
+ /** Type UUID. */
+ const char *pcszUuid;
+ /** DVM volume type. */
+ RTDVMVOLTYPE enmVolType;
+} RTDVMGPTPARTTYPE2VOLTYPE;
+/** Pointer to a MBR FS Type to volume type mapping entry. */
+typedef RTDVMGPTPARTTYPE2VOLTYPE *PRTDVMGPTPARTTYPE2VOLTYPE;
+
+/** Converts a LBA number to the byte offset. */
+#define RTDVM_GPT_LBA2BYTE(lba, disk) ((lba) * (disk)->cbSector)
+/** Converts a Byte offset to the LBA number. */
+#define RTDVM_GPT_BYTE2LBA(lba, disk) ((lba) / (disk)->cbSector)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Mapping of partition types to DVM volume types.
+ *
+ * From http://en.wikipedia.org/wiki/GUID_Partition_Table
+ */
+static const RTDVMGPTPARTTYPE2VOLTYPE g_aPartType2DvmVolTypes[] =
+{
+ { "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", RTDVMVOLTYPE_EFI_SYSTEM },
+
+ { "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", RTDVMVOLTYPE_WIN_BASIC },
+ { "E3C9E316-0B5C-4DB8-817D-F92DF00215AE", RTDVMVOLTYPE_WIN_MSR },
+ { "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", RTDVMVOLTYPE_WIN_LDM_META },
+ { "AF9B60A0-1431-4F62-BC68-3311714A69AD", RTDVMVOLTYPE_WIN_LDM_DATA },
+ { "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", RTDVMVOLTYPE_WIN_RECOVERY },
+ { "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", RTDVMVOLTYPE_WIN_STORAGE_SPACES },
+
+ { "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", RTDVMVOLTYPE_LINUX_SWAP },
+ { "0FC63DAF-8483-4772-8E79-3D69D8477DE4", RTDVMVOLTYPE_LINUX_NATIVE },
+ { "44479540-F297-41B2-9AF7-D131D5F0458A", RTDVMVOLTYPE_LINUX_NATIVE }, /* x86 root */
+ { "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", RTDVMVOLTYPE_LINUX_NATIVE }, /* AMD64 root */
+ { "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", RTDVMVOLTYPE_LINUX_NATIVE }, /* ARM32 root */
+ { "B921B045-1DF0-41C3-AF44-4C6F280D3FAE", RTDVMVOLTYPE_LINUX_NATIVE }, /* ARM64 root */
+ { "E6D6D379-F507-44C2-A23C-238F2A3DF928", RTDVMVOLTYPE_LINUX_LVM },
+ { "A19D880F-05FC-4D3B-A006-743F0F84911E", RTDVMVOLTYPE_LINUX_SOFTRAID },
+
+ { "83BD6B9D-7F41-11DC-BE0B-001560B84F0F", RTDVMVOLTYPE_FREEBSD }, /* Boot */
+ { "516E7CB4-6ECF-11D6-8FF8-00022D09712B", RTDVMVOLTYPE_FREEBSD }, /* Data */
+ { "516E7CB5-6ECF-11D6-8FF8-00022D09712B", RTDVMVOLTYPE_FREEBSD }, /* Swap */
+ { "516E7CB6-6ECF-11D6-8FF8-00022D09712B", RTDVMVOLTYPE_FREEBSD }, /* UFS */
+ { "516E7CB8-6ECF-11D6-8FF8-00022D09712B", RTDVMVOLTYPE_FREEBSD }, /* Vinum */
+ { "516E7CBA-6ECF-11D6-8FF8-00022D09712B", RTDVMVOLTYPE_FREEBSD }, /* ZFS */
+
+ { "49F48D32-B10E-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* Swap */
+ { "49F48D5A-B10E-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* FFS */
+ { "49F48D82-B10E-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* LFS */
+ { "49F48DAA-B10E-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* Raid */
+ { "2DB519C4-B10F-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* Concatenated */
+ { "2DB519EC-B10F-11DC-B99B-0019D1879648", RTDVMVOLTYPE_NETBSD }, /* Encrypted */
+
+ { "48465300-0000-11AA-AA11-00306543ECAC", RTDVMVOLTYPE_DARWIN_HFS },
+ { "7C3457EF-0000-11AA-AA11-00306543ECAC", RTDVMVOLTYPE_DARWIN_APFS },
+
+ { "6A82CB45-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* Boot */
+ { "6A85CF4D-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* Root */
+ { "6A87C46F-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* Swap */
+ { "6A8B642B-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* Backup */
+ { "6A898CC3-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* /usr */
+ { "6A8EF2E9-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* /var */
+ { "6A90BA39-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* /home */
+ { "6A9283A5-1DD2-11B2-99A6-080020736631", RTDVMVOLTYPE_SOLARIS }, /* Alternate sector */
+
+ { "37AFFC90-EF7D-4E96-91C3-2D7AE055B174", RTDVMVOLTYPE_IBM_GPFS },
+
+ { "90B6FF38-B98F-4358-A21F-48F35B4A8AD3", RTDVMVOLTYPE_ARCA_OS2 }, /* OS/2 type 1 defined by Arca Noae */
+};
+
+static DECLCALLBACK(int) rtDvmFmtGptProbe(PCRTDVMDISK pDisk, uint32_t *puScore)
+{
+ int rc = VINF_SUCCESS;
+ GPTHDR Hdr;
+
+ *puScore = RTDVM_MATCH_SCORE_UNSUPPORTED;
+
+ if (rtDvmDiskGetSectors(pDisk) >= 2)
+ {
+ /* Read from the disk and check for the signature. */
+ rc = rtDvmDiskReadUnaligned(pDisk, RTDVM_GPT_LBA2BYTE(1, pDisk), &Hdr, sizeof(GPTHDR));
+ if ( RT_SUCCESS(rc)
+ && !strncmp(&Hdr.abSignature[0], RTDVM_GPT_SIGNATURE, RT_ELEMENTS(Hdr.abSignature))
+ && RT_LE2H_U32(Hdr.u32Revision) == 0x00010000
+ && RT_LE2H_U32(Hdr.cbHeader) == sizeof(GPTHDRREV1))
+ *puScore = RTDVM_MATCH_SCORE_PERFECT;
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptOpen(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ int rc = VINF_SUCCESS;
+ PRTDVMFMTINTERNAL pThis = NULL;
+
+ pThis = (PRTDVMFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMFMTINTERNAL));
+ if (pThis)
+ {
+ pThis->pDisk = pDisk;
+ pThis->cPartitions = 0;
+
+ /* Read the complete GPT header and convert to host endianess. */
+ rc = rtDvmDiskReadUnaligned(pDisk, RTDVM_GPT_LBA2BYTE(1, pDisk), &pThis->HdrRev1, sizeof(pThis->HdrRev1));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->HdrRev1.Hdr.u32Revision = RT_LE2H_U32(pThis->HdrRev1.Hdr.u32Revision);
+ pThis->HdrRev1.Hdr.cbHeader = RT_LE2H_U32(pThis->HdrRev1.Hdr.cbHeader);
+ pThis->HdrRev1.Hdr.u32Crc = RT_LE2H_U32(pThis->HdrRev1.Hdr.u32Crc);
+ pThis->HdrRev1.u64LbaCurrent = RT_LE2H_U64(pThis->HdrRev1.u64LbaCurrent);
+ pThis->HdrRev1.u64LbaBackup = RT_LE2H_U64(pThis->HdrRev1.u64LbaBackup);
+ pThis->HdrRev1.u64LbaFirstPartition = RT_LE2H_U64(pThis->HdrRev1.u64LbaFirstPartition);
+ pThis->HdrRev1.u64LbaLastPartition = RT_LE2H_U64(pThis->HdrRev1.u64LbaLastPartition);
+ /** @todo Disk UUID */
+ pThis->HdrRev1.u64LbaPartitionEntries = RT_LE2H_U64(pThis->HdrRev1.u64LbaPartitionEntries);
+ pThis->HdrRev1.cPartitionEntries = RT_LE2H_U32(pThis->HdrRev1.cPartitionEntries);
+ pThis->HdrRev1.cbPartitionEntry = RT_LE2H_U32(pThis->HdrRev1.cbPartitionEntry);
+ pThis->HdrRev1.u32CrcPartitionEntries = RT_LE2H_U32(pThis->HdrRev1.u32CrcPartitionEntries);
+
+ if (pThis->HdrRev1.cbPartitionEntry == sizeof(GPTENTRY))
+ {
+ size_t cbAlignedGptEntries = RT_ALIGN_Z(pThis->HdrRev1.cPartitionEntries * pThis->HdrRev1.cbPartitionEntry, pDisk->cbSector);
+ pThis->paGptEntries = (PGPTENTRY)RTMemAllocZ(cbAlignedGptEntries);
+ if (pThis->paGptEntries)
+ {
+ rc = rtDvmDiskRead(pDisk, RTDVM_GPT_LBA2BYTE(pThis->HdrRev1.u64LbaPartitionEntries, pDisk),
+ pThis->paGptEntries, cbAlignedGptEntries);
+ if (RT_SUCCESS(rc))
+ {
+ /* Count the occupied entries. */
+ for (unsigned i = 0; i < pThis->HdrRev1.cPartitionEntries; i++)
+ if (!RTUuidIsNull(&pThis->paGptEntries[i].UuidType))
+ {
+ /* Convert to host endianess. */
+ /** @todo Uuids */
+ pThis->paGptEntries[i].u64LbaFirst = RT_LE2H_U64(pThis->paGptEntries[i].u64LbaFirst);
+ pThis->paGptEntries[i].u64LbaLast = RT_LE2H_U64(pThis->paGptEntries[i].u64LbaLast);
+ pThis->paGptEntries[i].u64Flags = RT_LE2H_U64(pThis->paGptEntries[i].u64Flags);
+ for (unsigned cwc = 0; cwc < RT_ELEMENTS(pThis->paGptEntries[i].aPartitionName); cwc++)
+ pThis->paGptEntries[i].aPartitionName[cwc] = RT_LE2H_U16(pThis->paGptEntries[i].aPartitionName[cwc]);
+
+ pThis->cPartitions++;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *phVolMgrFmt = pThis;
+ return rc;
+ }
+ }
+ RTMemFree(pThis->paGptEntries);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ NOREF(pDisk); NOREF(phVolMgrFmt);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static DECLCALLBACK(void) rtDvmFmtGptClose(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ pThis->pDisk = NULL;
+ RT_ZERO(pThis->HdrRev1);
+
+ RTMemFree(pThis->paGptEntries);
+ pThis->paGptEntries = NULL;
+
+ RTMemFree(pThis);
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptQueryRangeUse(RTDVMFMT hVolMgrFmt,
+ uint64_t off, uint64_t cbRange,
+ bool *pfUsed)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ NOREF(cbRange);
+
+ if (off < 33*pThis->pDisk->cbSector)
+ *pfUsed = true;
+ else
+ *pfUsed = false;
+
+ return VINF_SUCCESS;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnQueryDiskUuid */
+static DECLCALLBACK(int) rtDvmFmtGptQueryDiskUuid(RTDVMFMT hVolMgrFmt, PRTUUID pUuid)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ *pUuid = pThis->HdrRev1.DiskUuid;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtGptGetValidVolumes(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ return pThis->cPartitions;
+}
+
+static DECLCALLBACK(uint32_t) rtDvmFmtGptGetMaxVolumes(RTDVMFMT hVolMgrFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ return pThis->HdrRev1.cPartitionEntries;
+}
+
+/**
+ * Creates a new volume.
+ *
+ * @returns IPRT status code.
+ * @param pThis The MBR volume manager data.
+ * @param pGptEntry The GPT entry.
+ * @param idx The index in the partition array.
+ * @param phVolFmt Where to store the volume data on success.
+ */
+static int rtDvmFmtMbrVolumeCreate(PRTDVMFMTINTERNAL pThis, PGPTENTRY pGptEntry,
+ uint32_t idx, PRTDVMVOLUMEFMT phVolFmt)
+{
+ int rc = VINF_SUCCESS;
+ PRTDVMVOLUMEFMTINTERNAL pVol = (PRTDVMVOLUMEFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEFMTINTERNAL));
+
+ if (pVol)
+ {
+ pVol->pVolMgr = pThis;
+ pVol->idxEntry = idx;
+ pVol->pGptEntry = pGptEntry;
+ pVol->offStart = RTDVM_GPT_LBA2BYTE(pGptEntry->u64LbaFirst, pThis->pDisk);
+ pVol->cbVolume = RTDVM_GPT_LBA2BYTE(pGptEntry->u64LbaLast - pGptEntry->u64LbaFirst + 1, pThis->pDisk);
+
+ *phVolFmt = pVol;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptQueryFirstVolume(RTDVMFMT hVolMgrFmt, PRTDVMVOLUMEFMT phVolFmt)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ if (pThis->cPartitions != 0)
+ {
+ PGPTENTRY pGptEntry = &pThis->paGptEntries[0];
+
+ /* Search for the first non empty entry. */
+ for (unsigned i = 0; i < pThis->HdrRev1.cPartitionEntries; i++)
+ {
+ if (!RTUuidIsNull(&pGptEntry->UuidType))
+ return rtDvmFmtMbrVolumeCreate(pThis, pGptEntry, i, phVolFmt);
+ pGptEntry++;
+ }
+ AssertFailed();
+ }
+ return VERR_DVM_MAP_EMPTY;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptQueryNextVolume(RTDVMFMT hVolMgrFmt, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUMEFMT phVolFmtNext)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ PGPTENTRY pGptEntry = pVol->pGptEntry + 1;
+
+ for (unsigned i = pVol->idxEntry + 1; i < pThis->HdrRev1.cPartitionEntries; i++)
+ {
+ if (!RTUuidIsNull(&pGptEntry->UuidType))
+ return rtDvmFmtMbrVolumeCreate(pThis, pGptEntry, i, phVolFmtNext);
+ pGptEntry++;
+ }
+
+ return VERR_DVM_MAP_NO_VOLUME;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnQueryTableLocations */
+static DECLCALLBACK(int) rtDvmFmtGptQueryTableLocations(RTDVMFMT hVolMgrFmt, uint32_t fFlags, PRTDVMTABLELOCATION paLocations,
+ size_t cLocations, size_t *pcActual)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+
+ /*
+ * The MBR if requested.
+ */
+ int rc = VINF_SUCCESS;
+ size_t iLoc = 0;
+ if (fFlags & RTDVMMAPQTABLOC_F_INCLUDE_LEGACY)
+ {
+ if (cLocations > 0)
+ {
+ paLocations[iLoc].off = 0;
+ paLocations[iLoc].cb = RTDVM_GPT_LBA2BYTE(1, pThis->pDisk);
+ paLocations[iLoc].cbPadding = 0;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+ }
+
+ /*
+ * The GPT.
+ */
+ if (cLocations > iLoc)
+ {
+ uint64_t const offEnd = (pThis->HdrRev1.cPartitionEntries * pThis->HdrRev1.cbPartitionEntry + pThis->pDisk->cbSector - 1)
+ / pThis->pDisk->cbSector
+ * pThis->pDisk->cbSector;
+ paLocations[iLoc].off = RTDVM_GPT_LBA2BYTE(1, pThis->pDisk);
+ paLocations[iLoc].cb = offEnd - paLocations[iLoc].off;
+
+ uint64_t uLbaFirstPart = pThis->pDisk->cbDisk / pThis->pDisk->cbSector;
+ for (unsigned i = 0; i < pThis->HdrRev1.cPartitionEntries; i++)
+ if ( pThis->paGptEntries[i].u64LbaFirst < uLbaFirstPart
+ && !RTUuidIsNull(&pThis->paGptEntries[i].UuidType))
+ uLbaFirstPart = pThis->paGptEntries[i].u64LbaFirst;
+
+ paLocations[iLoc].cbPadding = RTDVM_GPT_LBA2BYTE(uLbaFirstPart, pThis->pDisk);
+ if (paLocations[iLoc].cbPadding > offEnd)
+ paLocations[iLoc].cbPadding -= offEnd;
+ else
+ AssertFailedStmt(paLocations[iLoc].cbPadding = 0);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+
+ /*
+ * Return values.
+ */
+ if (pcActual)
+ *pcActual = iLoc;
+ else if (cLocations != iLoc && RT_SUCCESS(rc))
+ {
+ RT_BZERO(&paLocations[iLoc], (cLocations - iLoc) * sizeof(paLocations[0]));
+ rc = VERR_BUFFER_UNDERFLOW;
+ }
+ return rc;
+}
+
+static DECLCALLBACK(void) rtDvmFmtGptVolumeClose(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ pVol->pVolMgr = NULL;
+ pVol->offStart = 0;
+ pVol->cbVolume = 0;
+ pVol->pGptEntry = NULL;
+
+ RTMemFree(pVol);
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtGptVolumeGetSize(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ return pVol->cbVolume;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptVolumeQueryName(RTDVMVOLUMEFMT hVolFmt, char **ppszVolName)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ *ppszVolName = NULL;
+ return RTUtf16ToUtf8Ex(&pVol->pGptEntry->aPartitionName[0], RT_ELEMENTS(pVol->pGptEntry->aPartitionName),
+ ppszVolName, 0, NULL);
+}
+
+static DECLCALLBACK(RTDVMVOLTYPE) rtDvmFmtGptVolumeGetType(RTDVMVOLUMEFMT hVolFmt)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aPartType2DvmVolTypes); i++)
+ if (!RTUuidCompareStr(&pVol->pGptEntry->UuidType, g_aPartType2DvmVolTypes[i].pcszUuid))
+ return g_aPartType2DvmVolTypes[i].enmVolType;
+
+ return RTDVMVOLTYPE_UNKNOWN;
+}
+
+static DECLCALLBACK(uint64_t) rtDvmFmtGptVolumeGetFlags(RTDVMVOLUMEFMT hVolFmt)
+{
+ NOREF(hVolFmt);
+ return DVMVOLUME_F_CONTIGUOUS;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptVolumeQueryRange(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffStart, uint64_t *poffLast)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ *poffStart = pVol->offStart;
+ *poffLast = pVol->offStart + pVol->cbVolume - 1;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(bool) rtDvmFmtGptVolumeIsRangeIntersecting(RTDVMVOLUMEFMT hVolFmt,
+ uint64_t offStart, size_t cbRange,
+ uint64_t *poffVol,
+ uint64_t *pcbIntersect)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+
+ if (RTDVM_RANGE_IS_INTERSECTING(pVol->offStart, pVol->cbVolume, offStart))
+ {
+ *poffVol = offStart - pVol->offStart;
+ *pcbIntersect = RT_MIN(cbRange, pVol->offStart + pVol->cbVolume - offStart);
+ return true;
+ }
+ return false;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryTableLocation */
+static DECLCALLBACK(int) rtDvmFmtGptVolumeQueryTableLocation(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffTable, uint64_t *pcbTable)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ PRTDVMFMTINTERNAL pVolMgr = pVol->pVolMgr;
+ *poffTable = RTDVM_GPT_LBA2BYTE(1, pVolMgr->pDisk);
+ *pcbTable = RTDVM_GPT_LBA2BYTE(pVolMgr->HdrRev1.u64LbaPartitionEntries, pVolMgr->pDisk)
+ + RT_ALIGN_Z(pVolMgr->HdrRev1.cPartitionEntries * pVolMgr->HdrRev1.cbPartitionEntry, pVolMgr->pDisk->cbSector)
+ - *poffTable;
+ return VINF_SUCCESS;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeGetIndex */
+static DECLCALLBACK(uint32_t) rtDvmFmtGptVolumeGetIndex(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLIDX enmIndex)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ switch (enmIndex)
+ {
+ case RTDVMVOLIDX_USER_VISIBLE:
+ case RTDVMVOLIDX_ALL:
+ case RTDVMVOLIDX_LINUX:
+ return pVol->idxEntry + 1;
+
+ case RTDVMVOLIDX_IN_TABLE:
+ return pVol->idxEntry;
+
+ case RTDVMVOLIDX_INVALID:
+ case RTDVMVOLIDX_HOST:
+ case RTDVMVOLIDX_END:
+ case RTDVMVOLIDX_32BIT_HACK:
+ break;
+ /* no default! */
+ }
+ AssertFailed();
+ return UINT32_MAX;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryProp */
+static DECLCALLBACK(int) rtDvmFmtGptVolumeQueryProp(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLPROP enmProperty,
+ void *pvBuf, size_t cbBuf, size_t *pcbBuf)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ switch (enmProperty)
+ {
+ case RTDVMVOLPROP_MBR_FIRST_CYLINDER:
+ case RTDVMVOLPROP_MBR_FIRST_HEAD:
+ case RTDVMVOLPROP_MBR_FIRST_SECTOR:
+ case RTDVMVOLPROP_MBR_LAST_CYLINDER:
+ case RTDVMVOLPROP_MBR_LAST_HEAD:
+ case RTDVMVOLPROP_MBR_LAST_SECTOR:
+ case RTDVMVOLPROP_MBR_TYPE:
+ return VERR_NOT_SUPPORTED;
+
+ case RTDVMVOLPROP_GPT_TYPE:
+ *pcbBuf = sizeof(RTUUID);
+ Assert(cbBuf >= *pcbBuf);
+ *(PRTUUID)pvBuf = pVol->pGptEntry->UuidType;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_GPT_UUID:
+ *pcbBuf = sizeof(RTUUID);
+ Assert(cbBuf >= *pcbBuf);
+ *(PRTUUID)pvBuf = pVol->pGptEntry->UuidPartition;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_INVALID:
+ case RTDVMVOLPROP_END:
+ case RTDVMVOLPROP_32BIT_HACK:
+ break;
+ /* not default! */
+ }
+ AssertFailed();
+ RT_NOREF(cbBuf);
+ return VERR_NOT_SUPPORTED;
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptVolumeRead(RTDVMVOLUMEFMT hVolFmt, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbRead <= pVol->cbVolume, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskRead(pVol->pVolMgr->pDisk, pVol->offStart + off, pvBuf, cbRead);
+}
+
+static DECLCALLBACK(int) rtDvmFmtGptVolumeWrite(RTDVMVOLUMEFMT hVolFmt, uint64_t off, const void *pvBuf, size_t cbWrite)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ AssertReturn(off + cbWrite <= pVol->cbVolume, VERR_INVALID_PARAMETER);
+
+ return rtDvmDiskWrite(pVol->pVolMgr->pDisk, pVol->offStart + off, pvBuf, cbWrite);
+}
+
+DECL_HIDDEN_CONST(const RTDVMFMTOPS) g_rtDvmFmtGpt =
+{
+ /* pszFmt */
+ "GPT",
+ /* enmFormat, */
+ RTDVMFORMATTYPE_GPT,
+ /* pfnProbe */
+ rtDvmFmtGptProbe,
+ /* pfnOpen */
+ rtDvmFmtGptOpen,
+ /* pfnInitialize */
+ rtDvmFmtGptInitialize,
+ /* pfnClose */
+ rtDvmFmtGptClose,
+ /* pfnQueryRangeUse */
+ rtDvmFmtGptQueryRangeUse,
+ /* pfnQueryDiskUuid */
+ rtDvmFmtGptQueryDiskUuid,
+ /* pfnGetValidVolumes */
+ rtDvmFmtGptGetValidVolumes,
+ /* pfnGetMaxVolumes */
+ rtDvmFmtGptGetMaxVolumes,
+ /* pfnQueryFirstVolume */
+ rtDvmFmtGptQueryFirstVolume,
+ /* pfnQueryNextVolume */
+ rtDvmFmtGptQueryNextVolume,
+ /* pfnQueryTableLocations */
+ rtDvmFmtGptQueryTableLocations,
+ /* pfnVolumeClose */
+ rtDvmFmtGptVolumeClose,
+ /* pfnVolumeGetSize */
+ rtDvmFmtGptVolumeGetSize,
+ /* pfnVolumeQueryName */
+ rtDvmFmtGptVolumeQueryName,
+ /* pfnVolumeGetType */
+ rtDvmFmtGptVolumeGetType,
+ /* pfnVolumeGetFlags */
+ rtDvmFmtGptVolumeGetFlags,
+ /* pfnVolumeQueryRange */
+ rtDvmFmtGptVolumeQueryRange,
+ /* pfnVolumeIsRangeIntersecting */
+ rtDvmFmtGptVolumeIsRangeIntersecting,
+ /* pfnVolumeQueryTableLocation */
+ rtDvmFmtGptVolumeQueryTableLocation,
+ /* pfnVolumeGetIndex */
+ rtDvmFmtGptVolumeGetIndex,
+ /* pfnVolumeQueryProp */
+ rtDvmFmtGptVolumeQueryProp,
+ /* pfnVolumeRead */
+ rtDvmFmtGptVolumeRead,
+ /* pfnVolumeWrite */
+ rtDvmFmtGptVolumeWrite
+};
+
diff --git a/src/VBox/Runtime/common/dvm/dvmmbr.cpp b/src/VBox/Runtime/common/dvm/dvmmbr.cpp
new file mode 100644
index 00000000..ef9fe659
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmmbr.cpp
@@ -0,0 +1,1069 @@
+/* $Id: dvmmbr.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - MBR format backend.
+ */
+
+/*
+ * Copyright (C) 2011-2023 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 *
+*********************************************************************************************************************************/
+#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 <iprt/uuid.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;
+
+
+/** The on-disk Cylinder/Head/Sector (CHS) info. */
+typedef struct MBRCHSADDR
+{
+ uint8_t uHead;
+ uint8_t uSector : 6;
+ uint8_t uCylinderH : 2;
+ uint8_t uCylinderL;
+} MBRCHSADDR;
+AssertCompileSize(MBRCHSADDR, 3);
+
+
+/** A decoded cylinder/head/sector address. */
+typedef struct RTDVMMBRCHSADDR
+{
+ uint16_t uCylinder;
+ uint8_t uHead;
+ uint8_t uSector;
+} RTDVMMBRCHSADDR;
+
+
+/**
+ * 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;
+ /** RTDVMVOLIDX_IN_TABLE - Zero-based index within the table in pSector.
+ * (Also the index into RTDVMMBRSECTOR::aEntries.) */
+ uint8_t idxTable;
+ /** RTDVMVOLIDX_ALL - One-based index. All primary entries are included,
+ * whether they are used or not. In the extended table chain, only USED
+ * entries are counted (but we include RTDVMMBR_IS_EXTENDED entries). */
+ uint8_t idxAll;
+ /** RTDVMVOLIDX_USER_VISIBLE - One-base index. Skips all unused entries
+ * and RTDVMMBR_IS_EXTENDED. */
+ uint8_t idxVisible;
+ /** RTDVMVOLIDX_LINUX - One-based index following the /dev/sdaX scheme. */
+ uint8_t idxLinux;
+ uint8_t bUnused;
+ /** The first CHS address of this partition */
+ RTDVMMBRCHSADDR FirstChs;
+ /** The last CHS address of this partition */
+ RTDVMMBRCHSADDR LastChs;
+} 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;
+#if ARCH_BITS == 64
+ uint32_t uAlignmentPadding;
+#endif
+ /** The raw data. */
+ uint8_t abData[RT_FLEXIBLE_ARRAY_NESTED];
+} 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 sector size to use when doing address calculation based on partition
+ * table sector addresses and counts. */
+ uint32_t cbSector;
+ /** 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 > RT_MAX(512, pDisk->cbSector))
+ {
+ /* Read from the disk and check for the 0x55aa signature at the end. */
+ size_t cbAlignedSize = RT_MAX(512, pDisk->cbSector);
+ uint8_t *pbMbr = (uint8_t *)RTMemTmpAllocZ(cbAlignedSize);
+ if (pbMbr)
+ {
+ rc = rtDvmDiskRead(pDisk, 0, pbMbr, cbAlignedSize);
+ if ( RT_SUCCESS(rc)
+ && pbMbr[510] == 0x55
+ && pbMbr[511] == 0xaa)
+ *puScore = RTDVM_MATCH_SCORE_SUPPORTED; /* Not perfect because GPTs have a protective MBR. */
+ /** @todo this could easily confuser a DOS, OS/2 or NT boot sector with a MBR... */
+ RTMemTmpFree(pbMbr);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+
+ 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);
+}
+
+
+/**
+ * Decodes the on-disk cylinder/head/sector info and stores it the
+ * destination structure.
+ */
+DECLINLINE(void) rtDvmFmtMbrDecodeChs(RTDVMMBRCHSADDR *pDst, uint8_t *pbRaw)
+{
+ MBRCHSADDR *pRawChs = (MBRCHSADDR *)pbRaw;
+ pDst->uCylinder = RT_MAKE_U16(pRawChs->uCylinderL, pRawChs->uCylinderH);
+ pDst->uSector = pRawChs->uSector;
+ pDst->uHead = pRawChs->uHead;
+}
+
+
+static int rtDvmFmtMbrReadExtended(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pPrimaryEntry,
+ uint8_t *pidxAll, uint8_t *pidxVisible, uint8_t *pidxLinux)
+{
+ 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.
+ */
+ size_t const cbMbr = RT_MAX(512, pThis->pDisk->cbSector);
+ PRTDVMMBRSECTOR pNext = (PRTDVMMBRSECTOR)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMMBRSECTOR, abData[cbMbr]));
+ 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;
+
+ uint8_t *pabData = &pNext->abData[0];
+ int rc = rtDvmDiskReadUnaligned(pThis->pDisk, pNext->offOnDisk, pabData, cbMbr);
+ if ( RT_FAILURE(rc)
+ || pabData[510] != 0x55
+ || pabData[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, pabData[510], pabData[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 = &pabData[446];
+ for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
+ {
+ pEntry->pSector = pNext;
+ pEntry->idxTable = (uint8_t)i;
+ RTListInit(&pEntry->ListEntry);
+
+ uint8_t const bType = pbMbrEntry[4];
+ if (bType != 0)
+ {
+ pEntry->bType = bType;
+ pEntry->fFlags = pbMbrEntry[0];
+ pEntry->idxAll = *pidxAll;
+ *pidxAll += 1;
+
+ rtDvmFmtMbrDecodeChs(&pEntry->FirstChs, &pbMbrEntry[1]);
+ rtDvmFmtMbrDecodeChs(&pEntry->LastChs, &pbMbrEntry[5]);
+
+ pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
+ pbMbrEntry[0x08 + 1],
+ pbMbrEntry[0x08 + 2],
+ pbMbrEntry[0x08 + 3]);
+ pEntry->offPart *= pThis->cbSector;
+ pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
+ pbMbrEntry[0x0c + 1],
+ pbMbrEntry[0x0c + 2],
+ pbMbrEntry[0x0c + 3]);
+ pEntry->cbPart *= pThis->cbSector;
+ if (!RTDVMMBR_IS_EXTENDED(bType))
+ {
+ pEntry->offPart += offCurBegin;
+ pEntry->idxVisible = *pidxVisible;
+ *pidxVisible += 1;
+ pEntry->idxLinux = *pidxLinux;
+ *pidxLinux += 1;
+
+ 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;
+ size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
+ PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
+ 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;
+
+ /* We'll use the sector size reported by the disk.
+
+ Though, giiven that the MBR was hardwired to 512 byte sectors, we probably
+ should do some probing when the sector size differs from 512, but that can
+ wait till there is a real need for it and we've got some semi reliable
+ heuristics for doing that. */
+ pThis->cbSector = (uint32_t)pDisk->cbSector;
+ AssertLogRelMsgStmt( pThis->cbSector >= 512
+ && pThis->cbSector <= _64K,
+ ("cbSector=%#x\n", pThis->cbSector),
+ pThis->cbSector = 512);
+
+ /*
+ * Read the primary MBR.
+ */
+ uint8_t *pabData = &pThis->Primary.abData[0];
+ rc = rtDvmDiskRead(pDisk, 0, pabData, cbMbr);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pabData[510] == 0x55 && pabData[511] == 0xaa);
+
+ /*
+ * Setup basic data for the 4 entries.
+ */
+ PRTDVMMBRENTRY pEntry = &pThis->Primary.aEntries[0];
+ uint8_t *pbMbrEntry = &pabData[446];
+ uint8_t idxVisible = 1;
+ for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
+ {
+ pEntry->pSector = &pThis->Primary;
+ pEntry->idxTable = (uint8_t)i;
+ RTListInit(&pEntry->ListEntry);
+
+ uint8_t const bType = pbMbrEntry[4];
+ if (bType != 0)
+ {
+ pEntry->bType = bType;
+ pEntry->fFlags = pbMbrEntry[0];
+ pEntry->idxAll = (uint8_t)(i + 1);
+
+ rtDvmFmtMbrDecodeChs(&pEntry->FirstChs, &pbMbrEntry[1]);
+ rtDvmFmtMbrDecodeChs(&pEntry->LastChs, &pbMbrEntry[5]);
+
+ pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
+ pbMbrEntry[0x08 + 1],
+ pbMbrEntry[0x08 + 2],
+ pbMbrEntry[0x08 + 3]);
+ pEntry->offPart *= pThis->cbSector;
+ pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
+ pbMbrEntry[0x0c + 1],
+ pbMbrEntry[0x0c + 2],
+ pbMbrEntry[0x0c + 3]);
+ pEntry->cbPart *= pThis->cbSector;
+ if (!RTDVMMBR_IS_EXTENDED(bType))
+ {
+ pEntry->idxVisible = idxVisible++;
+ pEntry->idxLinux = (uint8_t)(i + 1);
+ 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)
+ {
+ uint8_t idxAll = 5;
+ uint8_t idxLinux = 5;
+ 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], &idxAll, &idxVisible, &idxLinux);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *phVolMgrFmt = pThis;
+ return rc;
+ }
+ }
+ rtDvmFmtMbrDestroy(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
+{
+ int rc;
+ size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
+ PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
+ 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. */
+ uint8_t *pabData = &pThis->Primary.abData[0];
+ RT_BZERO(pabData, 512);
+ pabData[510] = 0x55;
+ pabData[511] = 0xaa;
+ rc = rtDvmDiskWrite(pDisk, 0, pabData, cbMbr);
+ 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;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnQueryDiskUuid */
+static DECLCALLBACK(int) rtDvmFmtMbrQueryDiskUuid(RTDVMFMT hVolMgrFmt, PRTUUID pUuid)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ uint32_t idDisk = RT_MAKE_U32_FROM_U8(pThis->Primary.abData[440],
+ pThis->Primary.abData[441],
+ pThis->Primary.abData[442],
+ pThis->Primary.abData[443]);
+ if (idDisk != 0)
+ {
+ RTUuidClear(pUuid);
+ pUuid->Gen.u32TimeLow = idDisk;
+ return VINF_NOT_SUPPORTED;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+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;
+}
+
+/**
+ * Helper for rtDvmFmtMbrQueryTableLocations that calculates the padding and/or
+ * free space at @a off.
+ *
+ * Because nothing need to be sorted by start offset, we have to traverse all
+ * partition tables to determine this.
+ */
+static uint64_t rtDvmFmtMbrCalcTablePadding(PRTDVMFMTINTERNAL pThis, uint64_t off)
+{
+ uint64_t offNext = pThis->pDisk->cbDisk;
+ for (unsigned i = 0; i < 4; i++)
+ {
+ /* Check this primary entry */
+ uint64_t offCur = pThis->Primary.aEntries[i].offPart;
+ if (offCur >= off && offCur < offNext && pThis->Primary.aEntries[i].bType != 0)
+ offNext = offCur;
+
+ /* If it's an extended partition, check the chained ones too. */
+ for (PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
+ pCur != NULL;
+ pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
+ {
+ for (unsigned j = 0; j < 4; j++)
+ {
+ offCur = pCur->aEntries[j].offPart;
+ if (offCur >= off && offCur < offNext && pCur->aEntries[j].bType != 0)
+ offNext = offCur;
+ }
+ }
+ }
+ Assert(offNext >= off);
+ return offNext - off;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnQueryTableLocations */
+static DECLCALLBACK(int) rtDvmFmtMbrQueryTableLocations(RTDVMFMT hVolMgrFmt, uint32_t fFlags, PRTDVMTABLELOCATION paLocations,
+ size_t cLocations, size_t *pcActual)
+{
+ PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
+ RT_NOREF(fFlags);
+
+ /*
+ * The MBR.
+ */
+ int rc = VINF_SUCCESS;
+ size_t iLoc = 0;
+ if (cLocations > 0)
+ {
+ paLocations[iLoc].off = pThis->Primary.offOnDisk;
+ paLocations[iLoc].cb = pThis->cbSector;
+ paLocations[iLoc].cbPadding = rtDvmFmtMbrCalcTablePadding(pThis, 0 + pThis->cbSector);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+
+ /*
+ * Now do the extended partitions.
+ *
+ * Remember, we only support multiple in the primary MBR, only the first
+ * one is honored in the chained ones.
+ */
+ for (unsigned i = 0; i < 4; i++)
+ {
+ for (PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
+ pCur != NULL;
+ pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
+ {
+ if (cLocations > iLoc)
+ {
+ paLocations[iLoc].off = pCur->offOnDisk;
+ paLocations[iLoc].cb = pThis->cbSector;
+ paLocations[iLoc].cbPadding = rtDvmFmtMbrCalcTablePadding(pThis, pCur->offOnDisk + pThis->cbSector);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ iLoc++;
+ }
+ }
+
+ /*
+ * Return values.
+ */
+ if (pcActual)
+ *pcActual = iLoc;
+ else if (cLocations != iLoc && RT_SUCCESS(rc))
+ {
+ RT_BZERO(&paLocations[iLoc], (cLocations - iLoc) * sizeof(paLocations[0]));
+ rc = VERR_BUFFER_UNDERFLOW;
+ }
+ return rc;
+}
+
+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 = DVMVOLUME_F_CONTIGUOUS;
+ if (pVol->pEntry->fFlags & 0x80)
+ fFlags |= DVMVOLUME_FLAGS_BOOTABLE | DVMVOLUME_FLAGS_ACTIVE;
+
+ return fFlags;
+}
+
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryRange(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffStart, uint64_t *poffLast)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ *poffStart = pVol->pEntry->offPart;
+ *poffLast = pVol->pEntry->offPart + pVol->pEntry->cbPart - 1;
+ return VINF_SUCCESS;
+}
+
+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;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryTableLocation */
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryTableLocation(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffTable, uint64_t *pcbTable)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ *poffTable = pVol->pEntry->pSector->offOnDisk;
+ *pcbTable = RT_MAX(512, pVol->pVolMgr->pDisk->cbSector);
+ return VINF_SUCCESS;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeGetIndex */
+static DECLCALLBACK(uint32_t) rtDvmFmtMbrVolumeGetIndex(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLIDX enmIndex)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ switch (enmIndex)
+ {
+ case RTDVMVOLIDX_USER_VISIBLE:
+ return pVol->pEntry->idxVisible;
+ case RTDVMVOLIDX_ALL:
+ return pVol->pEntry->idxAll;
+ case RTDVMVOLIDX_IN_TABLE:
+ return pVol->pEntry->idxTable;
+ case RTDVMVOLIDX_LINUX:
+ return pVol->pEntry->idxLinux;
+
+ case RTDVMVOLIDX_INVALID:
+ case RTDVMVOLIDX_HOST:
+ case RTDVMVOLIDX_END:
+ case RTDVMVOLIDX_32BIT_HACK:
+ break;
+ /* no default! */
+ }
+ return UINT32_MAX;
+}
+
+/** @copydoc RTDVMFMTOPS::pfnVolumeQueryProp */
+static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryProp(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLPROP enmProperty,
+ void *pvBuf, size_t cbBuf, size_t *pcbBuf)
+{
+ PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
+ switch (enmProperty)
+ {
+ case RTDVMVOLPROP_MBR_FIRST_CYLINDER:
+ *pcbBuf = sizeof(uint16_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint16_t *)pvBuf = pVol->pEntry->FirstChs.uCylinder;
+ return VINF_SUCCESS;
+ case RTDVMVOLPROP_MBR_LAST_CYLINDER:
+ *pcbBuf = sizeof(uint16_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint16_t *)pvBuf = pVol->pEntry->LastChs.uCylinder;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_MBR_FIRST_HEAD:
+ *pcbBuf = sizeof(uint8_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint8_t *)pvBuf = pVol->pEntry->FirstChs.uHead;
+ return VINF_SUCCESS;
+ case RTDVMVOLPROP_MBR_LAST_HEAD:
+ *pcbBuf = sizeof(uint8_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint8_t *)pvBuf = pVol->pEntry->LastChs.uHead;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_MBR_FIRST_SECTOR:
+ *pcbBuf = sizeof(uint8_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint8_t *)pvBuf = pVol->pEntry->FirstChs.uSector;
+ return VINF_SUCCESS;
+ case RTDVMVOLPROP_MBR_LAST_SECTOR:
+ *pcbBuf = sizeof(uint8_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint8_t *)pvBuf = pVol->pEntry->LastChs.uSector;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_MBR_TYPE:
+ *pcbBuf = sizeof(uint8_t);
+ Assert(cbBuf >= *pcbBuf);
+ *(uint8_t *)pvBuf = pVol->pEntry->bType;
+ return VINF_SUCCESS;
+
+ case RTDVMVOLPROP_GPT_TYPE:
+ case RTDVMVOLPROP_GPT_UUID:
+ return VERR_NOT_SUPPORTED;
+
+ case RTDVMVOLPROP_INVALID:
+ case RTDVMVOLPROP_END:
+ case RTDVMVOLPROP_32BIT_HACK:
+ break;
+ /* not default! */
+ }
+ RT_NOREF(cbBuf);
+ AssertFailed();
+ return VERR_NOT_SUPPORTED;
+}
+
+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);
+}
+
+DECL_HIDDEN_CONST(const RTDVMFMTOPS) g_rtDvmFmtMbr =
+{
+ /* pszFmt */
+ "MBR",
+ /* enmFormat */
+ RTDVMFORMATTYPE_MBR,
+ /* pfnProbe */
+ rtDvmFmtMbrProbe,
+ /* pfnOpen */
+ rtDvmFmtMbrOpen,
+ /* pfnInitialize */
+ rtDvmFmtMbrInitialize,
+ /* pfnClose */
+ rtDvmFmtMbrClose,
+ /* pfnQueryRangeUse */
+ rtDvmFmtMbrQueryRangeUse,
+ /* pfnQueryDiskUuid */
+ rtDvmFmtMbrQueryDiskUuid,
+ /* pfnGetValidVolumes */
+ rtDvmFmtMbrGetValidVolumes,
+ /* pfnGetMaxVolumes */
+ rtDvmFmtMbrGetMaxVolumes,
+ /* pfnQueryFirstVolume */
+ rtDvmFmtMbrQueryFirstVolume,
+ /* pfnQueryNextVolume */
+ rtDvmFmtMbrQueryNextVolume,
+ /* pfnQueryTableLocations */
+ rtDvmFmtMbrQueryTableLocations,
+ /* pfnVolumeClose */
+ rtDvmFmtMbrVolumeClose,
+ /* pfnVolumeGetSize */
+ rtDvmFmtMbrVolumeGetSize,
+ /* pfnVolumeQueryName */
+ rtDvmFmtMbrVolumeQueryName,
+ /* pfnVolumeGetType */
+ rtDvmFmtMbrVolumeGetType,
+ /* pfnVolumeGetFlags */
+ rtDvmFmtMbrVolumeGetFlags,
+ /* pfnVolumeQueryRange */
+ rtDvmFmtMbrVolumeQueryRange,
+ /* pfnVOlumeIsRangeIntersecting */
+ rtDvmFmtMbrVolumeIsRangeIntersecting,
+ /* pfnVolumeQueryTableLocation */
+ rtDvmFmtMbrVolumeQueryTableLocation,
+ /* pfnVolumeGetIndex */
+ rtDvmFmtMbrVolumeGetIndex,
+ /* pfnVolumeQueryProp */
+ rtDvmFmtMbrVolumeQueryProp,
+ /* pfnVolumeRead */
+ rtDvmFmtMbrVolumeRead,
+ /* pfnVolumeWrite */
+ rtDvmFmtMbrVolumeWrite
+};
+
diff --git a/src/VBox/Runtime/common/dvm/dvmvfs.cpp b/src/VBox/Runtime/common/dvm/dvmvfs.cpp
new file mode 100644
index 00000000..f22f02f9
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmvfs.cpp
@@ -0,0 +1,1467 @@
+/* $Id: dvmvfs.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - VFS glue.
+ */
+
+/*
+ * Copyright (C) 2012-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS /** @todo fix log group */
+#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/file.h>
+#include <iprt/sg.h>
+#include <iprt/vfslowlevel.h>
+#include <iprt/poll.h>
+#include <iprt/log.h>
+#include "internal/dvm.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a volume manager VFS. */
+typedef struct RTDVMVFSVOL *PRTDVMVFSVOL;
+
+/**
+ * The internal data of a DVM volume I/O stream.
+ */
+typedef struct RTVFSDVMFILE
+{
+ /** The volume the VFS file belongs to. */
+ RTDVMVOLUME hVol;
+ /** Pointer to the VFS volume. Can be NULL. */
+ PRTDVMVFSVOL pVfsVol;
+ /** Current position. */
+ uint64_t offCurPos;
+ /** Set if readable. */
+ bool fCanRead;
+ /** Set if writable. */
+ bool fCanWrite;
+} RTVFSDVMFILE;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef RTVFSDVMFILE *PRTVFSDVMFILE;
+
+/**
+ * The internal data of a DVM volume symlink.
+ */
+typedef struct RTVFSDVMSYMLINK
+{
+ /** The DVM volume the symlink represent. */
+ RTDVMVOLUME hVol;
+ /** The DVM volume manager @a hVol belongs to. */
+ RTDVM hVolMgr;
+ /** The symlink name. */
+ char *pszSymlink;
+ /** The symlink target (volXX). */
+ char szTarget[16];
+} RTVFSDVMSYMLINK;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef RTVFSDVMSYMLINK *PRTVFSDVMSYMLINK;
+
+/**
+ * The volume manager VFS (root) dir data.
+ */
+typedef struct RTDVMVFSDIR
+{
+ /** Pointer to the VFS volume. */
+ PRTDVMVFSVOL pVfsVol;
+ /** The current directory offset. */
+ uint32_t offDir;
+ /** Set if we need to try return hCurVolume again because of buffer overflow. */
+ bool fReturnCurrent;
+ /** Pointer to name alias string (returned by RTDvmVolumeQueryName, free it). */
+ char *pszNameAlias;
+ /** The current DVM volume. */
+ RTDVMVOLUME hCurVolume;
+} RTDVMVFSDIR;
+/** Pointer to a volume manager VFS (root) dir. */
+typedef RTDVMVFSDIR *PRTDVMVFSDIR;
+
+/**
+ * A volume manager VFS for use in chains (thing pseudo/devfs).
+ */
+typedef struct RTDVMVFSVOL
+{
+ /** The volume manager. */
+ RTDVM hVolMgr;
+ /** Whether to close it on success. */
+ bool fCloseDvm;
+ /** Whether the access is read-only. */
+ bool fReadOnly;
+ /** Number of volumes. */
+ uint32_t cVolumes;
+ /** Self reference. */
+ RTVFS hVfsSelf;
+} RTDVMVFSVOL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir);
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Close(void *pvThis)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+
+ RTDvmVolumeRelease(pThis->hVol);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsFile_QueryInfoWorker and rtDvmVfsSym_QueryInfoWorker.
+ */
+static int rtDvmVfsFileSym_QueryAddAttrWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = (RTUID)RTDvmVolumeGetType(hVolume);
+ pObjInfo->Attr.u.Unix.gid = hVolMgr != NIL_RTDVM ? (RTGID)RTDvmMapGetFormatType(hVolMgr) : NIL_RTGID;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
+ pObjInfo->Attr.u.Unix.INodeId = 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ {
+ RTDVMVOLTYPE enmType = RTDvmVolumeGetType(hVolume);
+ pObjInfo->Attr.u.UnixOwner.uid = (RTUID)enmType;
+ RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
+ RTDvmVolumeTypeGetDescr(enmType));
+ break;
+ }
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ if (hVolMgr != NIL_RTDVM)
+ {
+ pObjInfo->Attr.u.UnixGroup.gid = (RTGID)RTDvmMapGetFormatType(hVolMgr);
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
+ RTDvmMapGetFormatName(hVolMgr));
+ }
+ else
+ {
+ pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ }
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsFile_QueryInfo, rtDvmVfsDir_QueryEntryInfo, and
+ * rtDvmVfsDir_ReadDir.
+ */
+static int rtDvmVfsFile_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, bool fReadOnly,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+
+ pObjInfo->cbObject = RTDvmVolumeGetSize(hVolume);
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+ RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
+ pObjInfo->Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL;
+ if (fReadOnly)
+ pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0444;
+ else
+ pObjInfo->Attr.fMode |= 0666;
+
+ return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ return rtDvmVfsFile_QueryInfoWorker(pThis->hVol,
+ pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM,
+ pThis->pVfsVol ? pThis->pVfsVol->fReadOnly : !pThis->fCanWrite,
+ pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ int rc = VINF_SUCCESS;
+
+ Assert(pSgBuf->cSegs == 1);
+ NOREF(fBlocking);
+
+ /*
+ * Find the current position and check if it's within the volume.
+ */
+ uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
+ if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = 0;
+ pThis->offCurPos = offUnsigned;
+ return VINF_EOF;
+ }
+ return VERR_EOF;
+ }
+
+ size_t cbLeftToRead;
+ if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (!pcbRead)
+ return VERR_EOF;
+ *pcbRead = cbLeftToRead = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
+ }
+ else
+ {
+ cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
+ if (pcbRead)
+ *pcbRead = cbLeftToRead;
+ }
+
+ /*
+ * Ok, we've got a valid stretch within the file. Do the reading.
+ */
+ if (cbLeftToRead > 0)
+ {
+ rc = RTDvmVolumeRead(pThis->hVol, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
+ if (RT_SUCCESS(rc))
+ offUnsigned += cbLeftToRead;
+ }
+
+ pThis->offCurPos = offUnsigned;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ int rc = VINF_SUCCESS;
+
+ Assert(pSgBuf->cSegs == 1);
+ NOREF(fBlocking);
+
+ /*
+ * Find the current position and check if it's within the volume.
+ * Writing beyond the end of a volume is not supported.
+ */
+ uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
+ if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = 0;
+ pThis->offCurPos = offUnsigned;
+ }
+ return VERR_NOT_SUPPORTED;
+ }
+
+ size_t cbLeftToWrite;
+ if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (!pcbWritten)
+ return VERR_EOF;
+ *pcbWritten = cbLeftToWrite = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
+ }
+ else
+ {
+ cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
+ if (pcbWritten)
+ *pcbWritten = cbLeftToWrite;
+ }
+
+ /*
+ * Ok, we've got a valid stretch within the file. Do the reading.
+ */
+ if (cbLeftToWrite > 0)
+ {
+ rc = RTDvmVolumeWrite(pThis->hVol, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
+ if (RT_SUCCESS(rc))
+ offUnsigned += cbLeftToWrite;
+ }
+
+ pThis->offCurPos = offUnsigned;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Flush(void *pvThis)
+{
+ NOREF(pvThis);
+ return VINF_SUCCESS; /** @todo Implement missing DVM API. */
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis);
+ NOREF(fMode);
+ NOREF(fMask);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis);
+ NOREF(pAccessTime);
+ NOREF(pModificationTime);
+ NOREF(pChangeTime);
+ NOREF(pBirthTime);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis);
+ NOREF(uid);
+ NOREF(gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+
+ /*
+ * Seek relative to which position.
+ */
+ uint64_t offWrt;
+ switch (uMethod)
+ {
+ case RTFILE_SEEK_BEGIN:
+ offWrt = 0;
+ break;
+
+ case RTFILE_SEEK_CURRENT:
+ offWrt = pThis->offCurPos;
+ break;
+
+ case RTFILE_SEEK_END:
+ offWrt = RTDvmVolumeGetSize(pThis->hVol);
+ break;
+
+ default:
+ return VERR_INTERNAL_ERROR_5;
+ }
+
+ /*
+ * Calc new position, take care to stay within bounds.
+ *
+ * @todo: Setting position beyond the end of the volume does not make sense.
+ */
+ uint64_t offNew;
+ if (offSeek == 0)
+ offNew = offWrt;
+ else if (offSeek > 0)
+ {
+ offNew = offWrt + offSeek;
+ if ( offNew < offWrt
+ || offNew > RTFOFF_MAX)
+ offNew = RTFOFF_MAX;
+ }
+ else if ((uint64_t)-offSeek < offWrt)
+ offNew = offWrt + offSeek;
+ else
+ offNew = 0;
+
+ /*
+ * Update the state and set return value.
+ */
+ pThis->offCurPos = offNew;
+
+ *poffActual = offNew;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ *pcbFile = RTDvmVolumeGetSize(pThis->hVol);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Standard file operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtDvmVfsStdFileOps =
+{
+ { /* Stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "DvmFile",
+ rtDvmVfsFile_Close,
+ rtDvmVfsFile_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ rtDvmVfsFile_Read,
+ rtDvmVfsFile_Write,
+ rtDvmVfsFile_Flush,
+ NULL /*pfnPollOne*/,
+ rtDvmVfsFile_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+ },
+ RTVFSFILEOPS_VERSION,
+ /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
+ rtDvmVfsFile_SetMode,
+ rtDvmVfsFile_SetTimes,
+ rtDvmVfsFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsFile_Seek,
+ rtDvmVfsFile_QuerySize,
+ NULL /*SetSize*/,
+ NULL /*QueryMaxSize*/,
+ RTVFSFILEOPS_VERSION
+};
+
+
+/**
+ * Internal worker for RTDvmVolumeCreateVfsFile and rtDvmVfsDir_OpenFile.
+ *
+ * @returns IPRT status code.
+ * @param pVfsVol The VFS volume, optional.
+ * @param hVol The volume handle. (Reference not consumed.)
+ * @param fOpen RTFILE_O_XXX (valid).
+ * @param phVfsFileOut Where to return the handle to the file.
+ */
+static int rtDvmVfsCreateFileForVolume(PRTDVMVFSVOL pVfsVol, RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
+{
+ uint32_t cRefs = RTDvmVolumeRetain(hVol);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSFILE hVfsFile;
+ PRTVFSDVMFILE pThis;
+ int rc = RTVfsNewFile(&g_rtDvmVfsStdFileOps, sizeof(*pThis), fOpen, NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->offCurPos = 0;
+ pThis->hVol = hVol;
+ pThis->fCanRead = RT_BOOL(fOpen & RTFILE_O_READ);
+ pThis->fCanWrite = RT_BOOL(fOpen & RTFILE_O_WRITE);
+ pThis->pVfsVol = pVfsVol;
+
+ *phVfsFileOut = hVfsFile;
+ return VINF_SUCCESS;
+ }
+
+ RTDvmVolumeRelease(hVol);
+ return rc;
+}
+
+
+RTDECL(int) RTDvmVolumeCreateVfsFile(RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
+{
+ AssertPtrReturn(hVol, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phVfsFileOut, VERR_INVALID_POINTER);
+ AssertReturn(fOpen & RTFILE_O_ACCESS_MASK, VERR_INVALID_FLAGS);
+ AssertReturn(!(fOpen & ~RTFILE_O_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtDvmVfsCreateFileForVolume(NULL, hVol, fOpen, phVfsFileOut);
+}
+
+
+/*********************************************************************************************************************************
+* DVM Symbolic Link Objects *
+*********************************************************************************************************************************/
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_Close(void *pvThis)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ if (pThis->pszSymlink)
+ {
+ RTStrFree(pThis->pszSymlink);
+ pThis->pszSymlink = NULL;
+ }
+ if (pThis->hVol != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hVol);
+ pThis->hVol = NIL_RTDVMVOLUME;
+ }
+ if (pThis->hVolMgr != NIL_RTDVM)
+ {
+ RTDvmRelease(pThis->hVolMgr);
+ pThis->hVolMgr = NIL_RTDVM;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsSym_QueryInfo and rtDvmVfsDir_Read.
+ *
+ * @returns IPRT status code.
+ * @param hVolume The volume handle.
+ * @param hVolMgr The volume manager handle. Optional.
+ * @param pszTarget The link target.
+ * @param pObjInfo The object info structure to populate.
+ * @param enmAddAttr The additional attributes to supply.
+ */
+static int rtDvmVfsSym_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, const char *pszTarget,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RT_ZERO(*pObjInfo);
+ pObjInfo->cbObject = pObjInfo->cbAllocated = pszTarget ? strlen(pszTarget) : 0;
+ pObjInfo->Attr.fMode = 0777 | RTFS_TYPE_SYMLINK | RTFS_DOS_NT_REPARSE_POINT;
+
+ return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ return rtDvmVfsSym_QueryInfoWorker(pThis->hVol, pThis->hVolMgr, pThis->szTarget, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ return RTStrCopy(pszTarget, cbTarget, pThis->szTarget);
+}
+
+
+/**
+ * DVM symbolic link operations.
+ */
+static const RTVFSSYMLINKOPS g_rtDvmVfsSymOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_SYMLINK,
+ "DvmSymlink",
+ rtDvmVfsSym_Close,
+ rtDvmVfsSym_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSSYMLINKOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
+ NULL /*rtDvmVfsSym_SetMode*/,
+ NULL /*rtDvmVfsSym_SetTimes*/,
+ NULL /*rtDvmVfsSym_SetOwner*/,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsSym_Read,
+ RTVFSSYMLINKOPS_VERSION
+};
+
+
+/**
+ * Internal worker for rtDvmVfsDir_OpenFile.
+ *
+ * @returns IPRT status code.
+ * @param hVol The volume handle (not consumed).
+ * @param hVolMgr The volume manager handle (not consumed).
+ * @param iVol The volume number.
+ * @param pszSymlink The volume name. Consumed on success.
+ * @param phVfsSymlinkOut Where to return the handle to the file.
+ */
+static int rtDvmVfsCreateSymlinkForVolume(RTDVMVOLUME hVol, RTDVM hVolMgr, uint32_t iVol, char *pszSymlink,
+ PRTVFSSYMLINK phVfsSymlinkOut)
+{
+ uint32_t cRefs = RTDvmVolumeRetain(hVol);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ cRefs = RTDvmRetain(hVolMgr);
+ AssertReturnStmt(cRefs != UINT32_MAX, RTDvmVolumeRelease(hVol), VERR_INVALID_HANDLE);
+
+ /*
+ * Create the symlink.
+ */
+ RTVFSSYMLINK hVfsSym;
+ PRTVFSDVMSYMLINK pThis;
+ int rc = RTVfsNewSymlink(&g_rtDvmVfsSymOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsSym, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVol = hVol;
+ pThis->hVolMgr = hVolMgr;
+ pThis->pszSymlink = pszSymlink;
+ RTStrPrintf(pThis->szTarget, sizeof(pThis->szTarget), "vol%u", iVol);
+
+ *phVfsSymlinkOut = hVfsSym;
+ return VINF_SUCCESS;
+ }
+ RTDvmRelease(hVolMgr);
+ RTDvmVolumeRelease(hVol);
+ return rc;
+}
+
+
+
+/*********************************************************************************************************************************
+* DVM Directory Objects *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_Close(void *pvThis)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ if (pThis->hCurVolume != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ }
+
+ if (pThis->pszNameAlias)
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+
+ pThis->pVfsVol = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+ pObjInfo->cbObject = pThis->pVfsVol->cVolumes;
+ pObjInfo->cbAllocated = pThis->pVfsVol->cVolumes;
+ RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
+ pObjInfo->Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
+ if (pThis->pVfsVol->fReadOnly)
+ pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0555;
+ else
+ pObjInfo->Attr.fMode |= 0777;
+
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
+ pObjInfo->Attr.u.Unix.gid = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
+ pObjInfo->Attr.u.Unix.cHardlinks = pThis->pVfsVol->cVolumes;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
+ pObjInfo->Attr.u.Unix.INodeId = 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.u.UnixGroup.gid = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
+ RTDvmMapGetFormatName(pThis->pVfsVol->hVolMgr));
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis); NOREF(fMode); NOREF(fMask);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ RT_NOREF(pvThis, uid, gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+static int rtDvmVfsDir_FindEntry(PRTDVMVFSDIR pThis, const char *pszEntry,
+ PRTDVMVOLUME phVolume, uint32_t *piVol, char **ppszSymlink)
+{
+ *phVolume = NIL_RTDVMVOLUME;
+ *ppszSymlink = NULL;
+ *piVol = UINT32_MAX;
+
+ /*
+ * Enumerate the volumes and try match the volume name.
+ */
+ int rc;
+ PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
+ if (pVfsVol->cVolumes > 0)
+ {
+ /* The first volume. */
+ uint32_t iVol = 0;
+ RTDVMVOLUME hVol;
+ rc = RTDvmMapQueryFirstVolume(pThis->pVfsVol->hVolMgr, &hVol);
+ while (RT_SUCCESS(rc))
+ {
+ /* Match the name. */
+ bool fMatch;
+ char *pszVolName;
+ rc = RTDvmVolumeQueryName(hVol, &pszVolName);
+ if (RT_SUCCESS(rc))
+ {
+ fMatch = RTStrCmp(pszEntry, pszVolName) == 0 && *pszVolName != '\0';
+ if (fMatch)
+ {
+ *phVolume = hVol;
+ *ppszSymlink = pszVolName;
+ *piVol = iVol;
+ return VINF_SUCCESS;
+ }
+ RTStrFree(pszVolName);
+ }
+ else if (rc == VERR_NOT_SUPPORTED)
+ fMatch = false;
+ else
+ {
+ RTDvmVolumeRelease(hVol);
+ break;
+ }
+
+ /* Match the sequential volume number. */
+ if (!fMatch)
+ {
+ char szTmp[16];
+ RTStrPrintf(szTmp, sizeof(szTmp), "vol%u", iVol);
+ fMatch = RTStrCmp(pszEntry, szTmp) == 0;
+ }
+
+ if (fMatch)
+ {
+ *phVolume = hVol;
+ *piVol = iVol;
+ return VINF_SUCCESS;
+ }
+
+ /* More volumes? */
+ iVol++;
+ if (iVol >= pVfsVol->cVolumes)
+ {
+ RTDvmVolumeRelease(hVol);
+ rc = VERR_FILE_NOT_FOUND;
+ break;
+ }
+
+ /* Get the next volume. */
+ RTDVMVOLUME hVolNext;
+ rc = RTDvmMapQueryNextVolume(pThis->pVfsVol->hVolMgr, hVol, &hVolNext);
+ RTDvmVolumeRelease(hVol);
+ hVol = hVolNext;
+ }
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpen}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen, uint32_t fFlags, PRTVFSOBJ phVfsObj)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ /*
+ * Special case: '.' and '..'
+ */
+ if ( pszEntry[0] == '.'
+ && ( pszEntry[1] == '\0'
+ || ( pszEntry[1] == '.'
+ && pszEntry[2] == '\0')))
+ {
+ if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
+ {
+ RTVFSDIR hVfsDir;
+ int rc = rtDvmVfsVol_OpenRoot(pThis->pVfsVol, &hVfsDir);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromDir(hVfsDir);
+ RTVfsDirRelease(hVfsDir);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ return rc;
+ }
+ return VERR_IS_A_DIRECTORY;
+ }
+ return VERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Open volume file.
+ */
+ RTDVMVOLUME hVolume = NIL_RTDVMVOLUME;
+ uint32_t iVol = 0;
+ char *pszSymlink = NULL;
+ int rc = rtDvmVfsDir_FindEntry(pThis, pszEntry, &hVolume, &iVol, &pszSymlink);
+ if (RT_SUCCESS(rc))
+ {
+ if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ if (fFlags & (RTVFSOBJ_F_OPEN_FILE | RTVFSOBJ_F_OPEN_DEV_BLOCK))
+ {
+ if (!pszSymlink)
+ {
+ if ( !(fOpen & RTFILE_O_WRITE)
+ || !pThis->pVfsVol->fReadOnly)
+ {
+ /* Create file object. */
+ RTVFSFILE hVfsFile;
+ rc = rtDvmVfsCreateFileForVolume(pThis->pVfsVol, hVolume, fOpen, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ }
+ else
+ rc = VERR_WRITE_PROTECT;
+ }
+ else
+ rc = VERR_IS_A_SYMLINK;
+ }
+ else if (fFlags & RTVFSOBJ_F_OPEN_SYMLINK)
+ {
+ /* Create symlink object */
+ RTVFSSYMLINK hVfsSym = NIL_RTVFSSYMLINK; /* (older gcc maybe used uninitialized) */
+ rc = rtDvmVfsCreateSymlinkForVolume(hVolume, pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM, iVol,
+ pszSymlink, &hVfsSym);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromSymlink(hVfsSym);
+ RTVfsSymlinkRelease(hVfsSym);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ pszSymlink = NULL;
+ }
+ }
+ else
+ rc = VERR_IS_A_FILE;
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ RTDvmVolumeRelease(hVolume);
+ if (pszSymlink)
+ RTStrFree(pszSymlink);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_OpenFile(void *pvThis, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
+{
+ RTVFSOBJ hVfsObj;
+ int rc = rtDvmVfsDir_Open(pvThis, pszFilename, fOpen, RTVFSOBJ_F_OPEN_FILE, &hVfsObj);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsFile = RTVfsObjToFile(hVfsObj);
+ RTVfsObjRelease(hVfsObj);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
+{
+ RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
+{
+ RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
+{
+ RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
+{
+ RT_NOREF(pvThis, pszEntry, fType);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
+{
+ RT_NOREF(pvThis, pszEntry, fType, pszNewName);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_RewindDir(void *pvThis)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ if (pThis->hCurVolume != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ }
+ pThis->fReturnCurrent = false;
+ pThis->offDir = 0;
+ if (pThis->pszNameAlias)
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAddAttr)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+ PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
+ int rc;
+
+ /*
+ * Format the volume name since we'll be needing it all but the final call.
+ */
+ char szVolNo[16];
+ size_t const cchVolNo = RTStrPrintf(szVolNo, sizeof(szVolNo), "vol%u", pThis->offDir);
+
+ if (!pThis->fReturnCurrent)
+ {
+ /*
+ * Do we have a pending name alias to return?
+ */
+ if (pThis->pszNameAlias)
+ {
+ size_t cchNameAlias = strlen(pThis->pszNameAlias);
+ size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchNameAlias + 1]);
+ if (cbNeeded <= *pcbDirEntry)
+ {
+ *pcbDirEntry = cbNeeded;
+
+ /* Do the names. */
+ pDirEntry->cbName = (uint16_t)cchNameAlias;
+ memcpy(pDirEntry->szName, pThis->pszNameAlias, cchNameAlias + 1);
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->wszShortName[0] = '\0';
+
+
+ /* Do the rest. */
+ rc = rtDvmVfsSym_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, szVolNo, &pDirEntry->Info, enmAddAttr);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ pThis->offDir += 1;
+ }
+ return rc;
+ }
+
+ *pcbDirEntry = cbNeeded;
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * Get the next volume to return info about.
+ */
+ if (pThis->offDir < pVfsVol->cVolumes)
+ {
+ RTDVMVOLUME hNextVolume;
+ if (pThis->offDir == 0)
+ rc = RTDvmMapQueryFirstVolume(pVfsVol->hVolMgr, &hNextVolume);
+ else
+ rc = RTDvmMapQueryNextVolume(pVfsVol->hVolMgr, pThis->hCurVolume, &hNextVolume);
+ if (RT_FAILURE(rc))
+ return rc;
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = hNextVolume;
+
+ /* Check if we need to return a name alias later. */
+ rc = RTDvmVolumeQueryName(pThis->hCurVolume, &pThis->pszNameAlias);
+ if (RT_FAILURE(rc))
+ pThis->pszNameAlias = NULL;
+ else if (*pThis->pszNameAlias == '\0')
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+ }
+ else
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ return VERR_NO_MORE_FILES;
+ }
+ }
+
+ /*
+ * Figure out the name length.
+ */
+ size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchVolNo + 1]);
+ if (cbNeeded <= *pcbDirEntry)
+ {
+ *pcbDirEntry = cbNeeded;
+
+ /* Do the names. */
+ pDirEntry->cbName = (uint16_t)cchVolNo;
+ memcpy(pDirEntry->szName, szVolNo, cchVolNo + 1);
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->wszShortName[0] = '\0';
+
+ /* Do the rest. */
+ rc = rtDvmVfsFile_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, pVfsVol->fReadOnly, &pDirEntry->Info, enmAddAttr);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->fReturnCurrent = false;
+ if (!pThis->pszNameAlias)
+ pThis->offDir += 1;
+ return rc;
+ }
+ }
+ else
+ {
+ *pcbDirEntry = cbNeeded;
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ pThis->fReturnCurrent = true;
+ return rc;
+}
+
+
+/**
+ * DVM (root) directory operations.
+ */
+static const RTVFSDIROPS g_rtDvmVfsDirOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_DIR,
+ "DvmDir",
+ rtDvmVfsDir_Close,
+ rtDvmVfsDir_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSDIROPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
+ rtDvmVfsDir_SetMode,
+ rtDvmVfsDir_SetTimes,
+ rtDvmVfsDir_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsDir_Open,
+ NULL /* pfnFollowAbsoluteSymlink */,
+ rtDvmVfsDir_OpenFile,
+ NULL /* pfnOpenDir */,
+ rtDvmVfsDir_CreateDir,
+ rtDvmVfsDir_OpenSymlink,
+ rtDvmVfsDir_CreateSymlink,
+ NULL /* pfnQueryEntryInfo */,
+ rtDvmVfsDir_UnlinkEntry,
+ rtDvmVfsDir_RenameEntry,
+ rtDvmVfsDir_RewindDir,
+ rtDvmVfsDir_ReadDir,
+ RTVFSDIROPS_VERSION,
+};
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_Close(void *pvThis)
+{
+ PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
+ LogFlow(("rtDvmVfsVol_Close(%p)\n", pThis));
+
+ if ( pThis->fCloseDvm
+ && pThis->hVolMgr != NIL_RTDVM )
+ RTDvmRelease(pThis->hVolMgr);
+ pThis->hVolMgr = NIL_RTDVM;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RT_NOREF(pvThis, pObjInfo, enmAddAttr);
+ return VERR_WRONG_TYPE;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
+{
+ PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
+
+ PRTDVMVFSDIR pNewDir;
+ int rc = RTVfsNewDir(&g_rtDvmVfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
+ NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
+ if (RT_SUCCESS(rc))
+ {
+ pNewDir->offDir = 0;
+ pNewDir->pVfsVol = pThis;
+ pNewDir->fReturnCurrent = false;
+ pNewDir->pszNameAlias = NULL;
+ pNewDir->hCurVolume = NIL_RTDVMVOLUME;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
+{
+ RT_NOREF(pvThis, off, cb, pfUsed);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECL_HIDDEN_CONST(const RTVFSOPS) g_rtDvmVfsVolOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_VFS,
+ "DvmVol",
+ rtDvmVfsVol_Close,
+ rtDvmVfsVol_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSOPS_VERSION,
+ 0 /* fFeatures */,
+ rtDvmVfsVol_OpenRoot,
+ rtDvmVfsVol_QueryRangeState,
+ RTVFSOPS_VERSION
+};
+
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtDvmVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+ PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg, pSpec);
+
+ /*
+ * Basic checks.
+ */
+ if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
+ return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
+ if (pElement->enmType != RTVFSOBJTYPE_VFS)
+ return VERR_VFS_CHAIN_ONLY_VFS;
+
+ if (pElement->cArgs > 1)
+ return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
+
+ /*
+ * Parse the flag if present, save in pElement->uProvider.
+ */
+ /** @todo allow specifying sector size */
+ bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
+ if (pElement->cArgs > 0)
+ {
+ const char *psz = pElement->paArgs[0].psz;
+ if (*psz)
+ {
+ if ( !strcmp(psz, "ro")
+ || !strcmp(psz, "r"))
+ fReadOnly = true;
+ else if (!strcmp(psz, "rw"))
+ fReadOnly = false;
+ else
+ {
+ *poffError = pElement->paArgs[0].offSpec;
+ return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
+ }
+ }
+ }
+
+ pElement->uProvider = fReadOnly;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtDvmVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
+ PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
+ PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
+ AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
+
+ /*
+ * Instantiate the volume manager and open the map stuff.
+ */
+ RTVFSFILE hPrevVfsFile = RTVfsObjToFile(hPrevVfsObj);
+ AssertReturn(hPrevVfsFile != NIL_RTVFSFILE, VERR_VFS_CHAIN_CAST_FAILED);
+
+ RTDVM hVolMgr;
+ int rc = RTDvmCreate(&hVolMgr, hPrevVfsFile, 512, 0 /*fFlags*/);
+ RTVfsFileRelease(hPrevVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDvmMapOpen(hVolMgr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a VFS instance for the volume manager.
+ */
+ RTVFS hVfs = NIL_RTVFS;
+ PRTDVMVFSVOL pThis = NULL;
+ rc = RTVfsNew(&g_rtDvmVfsVolOps, sizeof(RTDVMVFSVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVolMgr = hVolMgr;
+ pThis->fCloseDvm = true;
+ pThis->fReadOnly = pElement->uProvider == (uint64_t)true;
+ pThis->cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
+ pThis->hVfsSelf = hVfs;
+
+ *phVfsObj = RTVfsObjFromVfs(hVfs);
+ RTVfsRelease(hVfs);
+ return *phVfsObj != NIL_RTVFSOBJ ? VINF_SUCCESS : VERR_VFS_CHAIN_CAST_FAILED;
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmMapOpen failed: %Rrc", rc);
+ RTDvmRelease(hVolMgr);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmCreate failed: %Rrc", rc);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtDvmVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
+ PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
+ PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
+{
+ RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
+ return false;
+}
+
+
+/** VFS chain element 'file'. */
+static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
+{
+ /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
+ /* fReserved = */ 0,
+ /* pszName = */ "dvm",
+ /* ListEntry = */ { NULL, NULL },
+ /* pszHelp = */ "Opens a container image using the VD API.\n"
+ "Optionally takes one parameter 'ro' (read only) or 'rw' (read write).\n",
+ /* pfnValidate = */ rtDvmVfsChain_Validate,
+ /* pfnInstantiate = */ rtDvmVfsChain_Instantiate,
+ /* pfnCanReuseElement = */ rtDvmVfsChain_CanReuseElement,
+ /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
+