/* $Id: dvm.cpp $ */
/** @file
 * IPRT Disk Volume Management API (DVM) - generic code.
 */

/*
 * Copyright (C) 2011-2016 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#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                                                                                                             *
*********************************************************************************************************************************/
extern RTDVMFMTOPS g_rtDvmFmtMbr;
extern RTDVMFMTOPS g_rtDvmFmtGpt;
extern RTDVMFMTOPS g_rtDvmFmtBsdLbl;

/**
 * Supported volume formats.
 */
static PCRTDVMFMTOPS g_aDvmFmts[] =
{
    &g_rtDvmFmtMbr,
    &g_rtDvmFmtGpt,
    &g_rtDvmFmtBsdLbl
};

/**
 * Descriptions of the volume types.
 *
 * This is indexed by RTDVMVOLTYPE.
 */
static const char * g_apcszDvmVolTypes[] =
{
    "Invalid",
    "Unknown",
    "NTFS",
    "FAT16",
    "FAT32",
    "Linux swap",
    "Linux native",
    "Linux LVM",
    "Linux SoftRaid",
    "FreeBSD",
    "NetBSD",
    "OpenBSD",
    "Mac OS X HFS or HFS+",
    "Solaris"
};

/**
 * 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)
{
    int rc = VINF_SUCCESS;
    PRTDVMVOLUMEINTERNAL pVol = NULL;

    pVol = (PRTDVMVOLUMEINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEINTERNAL));
    if (pVol)
    {
        pVol->u32Magic = RTDVMVOLUME_MAGIC;
        pVol->cRefs    = 0;
        pVol->pVolMgr  = pThis;
        pVol->hVolFmt  = hVolFmt;

        *phVol = pVol;
    }
    else
        rc = VERR_NO_MEMORY;

    return rc;
}

/**
 * Destroys a volume handle.
 *
 * @param   pThis               The volume to destroy.
 */
static void rtDvmVolumeDestroy(PRTDVMVOLUMEINTERNAL pThis)
{
    PRTDVMINTERNAL pVolMgr = pThis->pVolMgr;

    AssertPtr(pVolMgr);

    /* Close the volume. */
    pVolMgr->pDvmFmtOps->pfnVolumeClose(pThis->hVolFmt);

    pThis->u32Magic = RTDVMVOLUME_MAGIC_DEAD;
    pThis->pVolMgr  = NULL;
    pThis->hVolFmt  = NIL_RTDVMVOLUMEFMT;
    RTMemFree(pThis);

    /* Release the reference of the volume manager. */
    RTDvmRelease(pVolMgr);
}

RTDECL(int) RTDvmCreate(PRTDVM phVolMgr, PFNDVMREAD pfnRead,
                        PFNDVMWRITE pfnWrite, uint64_t cbDisk,
                        uint64_t cbSector, uint32_t fFlags, void *pvUser)
{
    int rc = VINF_SUCCESS;
    PRTDVMINTERNAL pThis;

    AssertMsgReturn(!(fFlags & ~DVM_FLAGS_MASK),
                    ("Invalid flags given %#x\n", fFlags),
                    VERR_INVALID_PARAMETER);

    pThis = (PRTDVMINTERNAL)RTMemAllocZ(sizeof(RTDVMINTERNAL));
    if (pThis)
    {
        pThis->u32Magic         = RTDVM_MAGIC;
        pThis->DvmDisk.cbDisk   = cbDisk;
        pThis->DvmDisk.cbSector = cbSector;
        pThis->DvmDisk.pvUser   = pvUser;
        pThis->DvmDisk.pfnRead  = pfnRead;
        pThis->DvmDisk.pfnWrite = pfnWrite;
        pThis->pDvmFmtOps       = NULL;
        pThis->hVolMgrFmt       = NIL_RTDVMFMT;
        pThis->fFlags           = fFlags;
        pThis->cRefs            = 1;
        RTListInit(&pThis->VolumeList);
        *phVolMgr = pThis;
    }
    else
        rc = VERR_NO_MEMORY;

    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)
{
    if (pThis->hVolMgrFmt != NIL_RTDVMFMT)
    {
        AssertPtr(pThis->pDvmFmtOps);

        /* Let the backend do it's own cleanup first. */
        pThis->pDvmFmtOps->pfnClose(pThis->hVolMgrFmt);
        pThis->hVolMgrFmt = NIL_RTDVMFMT;
    }

    pThis->DvmDisk.cbDisk   = 0;
    pThis->DvmDisk.pvUser   = NULL;
    pThis->DvmDisk.pfnRead  = NULL;
    pThis->DvmDisk.pfnWrite = NULL;
    pThis->u32Magic         = RTDVM_MAGIC_DEAD;
    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)
{
    int            rc = VINF_SUCCESS;
    uint32_t       uScoreMax = RTDVM_MATCH_SCORE_UNSUPPORTED;
    PCRTDVMFMTOPS  pDvmFmtOpsMatch = NULL;
    PRTDVMINTERNAL pThis = hVolMgr;
    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
    AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
    AssertReturn(pThis->hVolMgrFmt == NIL_RTDVMFMT, VERR_INVALID_HANDLE);

    Assert(!pThis->pDvmFmtOps);

    for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++)
    {
        uint32_t uScore;
        PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i];

        rc = pDvmFmtOps->pfnProbe(&pThis->DvmDisk, &uScore);
        if (   RT_SUCCESS(rc)
            && uScore > uScoreMax)
        {
            pDvmFmtOpsMatch = pDvmFmtOps;
            uScoreMax       = uScore;
        }
        else if (RT_FAILURE(rc))
            break;
    }

    if (RT_SUCCESS(rc))
    {
        if (uScoreMax > RTDVM_MATCH_SCORE_UNSUPPORTED)
        {
            AssertPtr(pDvmFmtOpsMatch);

            /* Open the format. */
            rc = pDvmFmtOpsMatch->pfnOpen(&pThis->DvmDisk, &pThis->hVolMgrFmt);
            if (RT_SUCCESS(rc))
            {
                uint32_t cVols;

                pThis->pDvmFmtOps = pDvmFmtOpsMatch;

                cVols = pThis->pDvmFmtOps->pfnGetValidVolumes(pThis->hVolMgrFmt);

                /* Construct volume list. */
                if (cVols)
                {
                    PRTDVMVOLUMEINTERNAL pVol = NULL;
                    RTDVMVOLUMEFMT hVolFmt = NIL_RTDVMVOLUMEFMT;

                    rc = pThis->pDvmFmtOps->pfnQueryFirstVolume(pThis->hVolMgrFmt, &hVolFmt);
                    if (RT_SUCCESS(rc))
                    {
                        rc = rtDvmVolumeCreate(pThis, hVolFmt, &pVol);
                        if (RT_FAILURE(rc))
                            pThis->pDvmFmtOps->pfnVolumeClose(hVolFmt);
                    }

                    if (RT_SUCCESS(rc))
                    {
                        cVols--;
                        RTListAppend(&pThis->VolumeList, &pVol->VolumeNode);

                        while (   cVols > 0
                               && RT_SUCCESS(rc))
                        {
                            rc = pThis->pDvmFmtOps->pfnQueryNextVolume(pThis->hVolMgrFmt, pVol->hVolFmt, &hVolFmt);
                            if (RT_SUCCESS(rc))
                            {
                                rc = rtDvmVolumeCreate(pThis, hVolFmt, &pVol);
                                if (RT_FAILURE(rc))
                                    pThis->pDvmFmtOps->pfnVolumeClose(hVolFmt);
                                else
                                    RTListAppend(&pThis->VolumeList, &pVol->VolumeNode);
                                cVols--;
                            }
                        }
                    }

                    if (RT_FAILURE(rc))
                    {
                        PRTDVMVOLUMEINTERNAL pItNext, pIt;

                        /* Remove all entries. */
                        RTListForEachSafe(&pThis->VolumeList, pIt, pItNext, RTDVMVOLUMEINTERNAL, VolumeNode)
                        {
                            RTListNodeRemove(&pIt->VolumeNode);
                            rtDvmVolumeDestroy(pIt);
                        }
                    }
                }
            }
        }
        else
            rc = VERR_NOT_SUPPORTED;
    }

    return rc;
}

RTDECL(int) RTDvmMapInitialize(RTDVM hVolMgr, const char *pszFmt)
{
    int rc = VERR_NOT_SUPPORTED;
    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_INVALID_HANDLE);

    for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++)
    {
        PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i];

        if (!RTStrCmp(pDvmFmtOps->pcszFmt, pszFmt))
        {
            rc = pDvmFmtOps->pfnInitialize(&pThis->DvmDisk, &pThis->hVolMgrFmt);
            if (RT_SUCCESS(rc))
                pThis->pDvmFmtOps = pDvmFmtOps;

            break;
        }
    }

    return rc;
}

RTDECL(const char *) RTDvmMapGetFormat(RTDVM hVolMgr)
{
    PRTDVMINTERNAL pThis = hVolMgr;
    AssertPtrReturn(pThis, NULL);
    AssertReturn(pThis->u32Magic == RTDVM_MAGIC, NULL);
    AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, NULL);

    return pThis->pDvmFmtOps->pcszFmt;
}

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)
{
    int rc = VERR_DVM_MAP_EMPTY;
    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);

    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)
{
    int rc = VERR_DVM_MAP_NO_VOLUME;
    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);

    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)
{
    int rc = VINF_SUCCESS;
    PRTDVMINTERNAL       pThis = hVolMgr;
    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
    AssertPtrReturn(pfAllocated, VERR_INVALID_POINTER);
    AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
    AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE);
    AssertReturn(off + cb <= pThis->DvmDisk.cbDisk * pThis->DvmDisk.cbSector,
                 VERR_INVALID_PARAMETER);

    /* Check whether the range is inuse by the volume manager metadata first. */
    rc = pThis->pDvmFmtOps->pfnQueryRangeUse(pThis->hVolMgrFmt, off, cb, pfAllocated);
    if (RT_FAILURE(rc))
        return rc;

    if (!*pfAllocated)
    {
        bool fAllocated = false;

        while (   cb > 0
               && !fAllocated)
        {
            PRTDVMVOLUMEINTERNAL pVol;
            bool fVolFound = false;
            uint64_t cbIntersect;
            uint64_t offVol;

            /*
             * 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, extents of logical volumes don't need to be
             * contigous on the medium.
             */
            RTListForEach(&pThis->VolumeList, pVol, RTDVMVOLUMEINTERNAL, VolumeNode)
            {
                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))
                            break;
                        else if (fVolAllocated)
                        {
                            fAllocated = true;
                            break;
                        }
                    }
                    else if (!(pThis->fFlags & DVM_FLAGS_NO_STATUS_CALLBACK_MARK_AS_UNUSED))
                        fAllocated = true;
                    /* else, flag is set, continue. */

                    cb  -= cbIntersect;
                    off += cbIntersect;
                    break;
                }
            }

            if (!fVolFound)
            {
                if (pThis->fFlags & DVM_FLAGS_UNUSED_SPACE_MARK_AS_USED)
                    fAllocated = true;

                cb  -= pThis->DvmDisk.cbSector;
                off += pThis->DvmDisk.cbSector;
            }
        }

        *pfAllocated = fAllocated;
    }

    return rc;
}

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) 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_apcszDvmVolTypes[enmVolType];
}
