VirtualBox

Changeset 85902 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Aug 27, 2020 1:03:49 PM (4 years ago)
Author:
vboxsync
Message:

Storage/VMDK: Moved and rewrote raw disk creation preparation code from VBoxInternalManage.cpp. See bugref:9224 for details.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Storage/VMDK.cpp

    r85885 r85902  
    2121*********************************************************************************************************************************/
    2222#define LOG_GROUP LOG_GROUP_VD_VMDK
     23#include <VBox/log.h>           /* before VBox/vd-ifs.h */
    2324#include <VBox/vd-plugin.h>
    2425#include <VBox/err.h>
    2526
    26 #include <VBox/log.h>
    2727#include <iprt/assert.h>
    2828#include <iprt/alloc.h>
     29#include <iprt/base64.h>
     30#include <iprt/ctype.h>
     31#include <iprt/crc.h>
     32#include <iprt/dvm.h>
    2933#include <iprt/uuid.h>
    3034#include <iprt/path.h>
     35#include <iprt/rand.h>
    3136#include <iprt/string.h>
    32 #include <iprt/rand.h>
     37#include <iprt/sort.h>
    3338#include <iprt/zip.h>
    3439#include <iprt/asm.h>
     40#ifdef RT_OS_WINDOWS
     41# include <iprt/utf16.h>
     42# include <iprt/uni.h>
     43# include <iprt/uni.h>
     44# include <iprt/nt/nt-and-windows.h>
     45# include <winioctl.h>
     46#endif
    3547
    3648#include "VDBackends.h"
     
    532544};
    533545
     546/** NULL-terminated array of configuration option. */
     547static const VDCONFIGINFO s_aVmdkConfigInfo[] =
     548{
     549    /* Options for VMDK raw disks */
     550    { "RawDrive",                       NULL,                             VDCFGVALUETYPE_STRING,       0 },
     551    { "Partitions",                     NULL,                             VDCFGVALUETYPE_STRING,       0 },
     552    { "BootSector",                     NULL,                             VDCFGVALUETYPE_BYTES,        0 },
     553    { "Relative",                       NULL,                             VDCFGVALUETYPE_INTEGER,      0 },
     554
     555    /* End of options list */
     556    { NULL,                             NULL,                             VDCFGVALUETYPE_INTEGER,      0 }
     557};
     558
    534559
    535560/*********************************************************************************************************************************
     
    34123437
    34133438/**
     3439 * Frees a raw descriptor.
     3440 * @internal
     3441 */
     3442static int vmdkRawDescFree(PVDISKRAW pRawDesc)
     3443{
     3444    if (!pRawDesc)
     3445        return VINF_SUCCESS;
     3446
     3447    RTStrFree(pRawDesc->pszRawDisk);
     3448    pRawDesc->pszRawDisk = NULL;
     3449
     3450    /* Partitions: */
     3451    for (unsigned i = 0; i < pRawDesc->cPartDescs; i++)
     3452    {
     3453        RTStrFree(pRawDesc->pPartDescs[i].pszRawDevice);
     3454        pRawDesc->pPartDescs[i].pszRawDevice = NULL;
     3455
     3456        RTMemFree(pRawDesc->pPartDescs[i].pvPartitionData);
     3457        pRawDesc->pPartDescs[i].pvPartitionData = NULL;
     3458    }
     3459
     3460    RTMemFree(pRawDesc->pPartDescs);
     3461    pRawDesc->pPartDescs = NULL;
     3462
     3463    RTMemFree(pRawDesc);
     3464    return VINF_SUCCESS;
     3465}
     3466
     3467/**
     3468 * Helper that grows the raw partition descriptor table by @a cToAdd entries,
     3469 * returning the pointer to the first new entry.
     3470 * @internal
     3471 */
     3472static int vmdkRawDescAppendPartDesc(PVMDKIMAGE pImage, PVDISKRAW pRawDesc, uint32_t cToAdd, PVDISKRAWPARTDESC *ppRet)
     3473{
     3474    uint32_t const    cOld = pRawDesc->cPartDescs;
     3475    uint32_t const    cNew   = cOld + cToAdd;
     3476    PVDISKRAWPARTDESC paNew = (PVDISKRAWPARTDESC)RTMemReallocZ(pRawDesc->pPartDescs,
     3477                                                               cOld * sizeof(pRawDesc->pPartDescs[0]),
     3478                                                               cNew * sizeof(pRawDesc->pPartDescs[0]));
     3479    if (paNew)
     3480    {
     3481        pRawDesc->cPartDescs = cNew;
     3482        pRawDesc->pPartDescs = paNew;
     3483
     3484        *ppRet = &paNew[cOld];
     3485        return VINF_SUCCESS;
     3486    }
     3487    *ppRet = NULL;
     3488    return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
     3489                     N_("VMDK: Image path: '%s'. Out of memory growing the partition descriptors (%u -> %u)."),
     3490                     pImage->pszFilename, cOld, cNew);
     3491}
     3492
     3493/**
     3494 * @callback_method_impl{FNRTSORTCMP}
     3495 */
     3496static DECLCALLBACK(int) vmdkRawDescPartComp(void const *pvElement1, void const *pvElement2, void *pvUser)
     3497{
     3498    RT_NOREF(pvUser);
     3499    int64_t const iDelta = ((PVDISKRAWPARTDESC)pvElement1)->offStartInVDisk - ((PVDISKRAWPARTDESC)pvElement2)->offStartInVDisk;
     3500    return iDelta < 0 ? -1 : iDelta > 0 ? 1 : 0;
     3501}
     3502
     3503/**
     3504 * Post processes the partition descriptors.
     3505 *
     3506 * Sorts them and check that they don't overlap.
     3507 */
     3508static int vmdkRawDescPostProcessPartitions(PVMDKIMAGE pImage, PVDISKRAW pRawDesc, uint64_t cbSize)
     3509{
     3510    /*
     3511     * Sort data areas in ascending order of start.
     3512     */
     3513    RTSortShell(pRawDesc->pPartDescs, pRawDesc->cPartDescs, sizeof(pRawDesc->pPartDescs[0]), vmdkRawDescPartComp, NULL);
     3514
     3515    /*
     3516     * Check that we don't have overlapping descriptors.  If we do, that's an
     3517     * indication that the drive is corrupt or that the RTDvm code is buggy.
     3518     */
     3519    VDISKRAWPARTDESC const *paPartDescs = pRawDesc->pPartDescs;
     3520    for (uint32_t i = 0; i < pRawDesc->cPartDescs; i++)
     3521    {
     3522        uint64_t offLast = paPartDescs[i].offStartInVDisk + paPartDescs[i].cbData;
     3523        if (offLast <= paPartDescs[i].offStartInVDisk)
     3524            return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
     3525                             N_("VMDK: Image path: '%s'. Bogus partition descriptor #%u (%#RX64 LB %#RX64%s): Wrap around or zero"),
     3526                             pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
     3527                             paPartDescs[i].pvPartitionData ? " (data)" : "");
     3528        offLast -= 1;
     3529
     3530        if (i + 1 < pRawDesc->cPartDescs && offLast >= paPartDescs[i + 1].offStartInVDisk)
     3531            return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
     3532                             N_("VMDK: Image path: '%s'. Partition descriptor #%u (%#RX64 LB %#RX64%s) overlaps with the next (%#RX64 LB %#RX64%s)"),
     3533                             pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
     3534                             paPartDescs[i].pvPartitionData ? " (data)" : "", paPartDescs[i + 1].offStartInVDisk,
     3535                             paPartDescs[i + 1].cbData, paPartDescs[i + 1].pvPartitionData ? " (data)" : "");
     3536        if (offLast >= cbSize)
     3537            return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
     3538                             N_("VMDK: Image path: '%s'. Partition descriptor #%u (%#RX64 LB %#RX64%s) goes beyond the end of the drive (%#RX64)"),
     3539                             pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
     3540                             paPartDescs[i].pvPartitionData ? " (data)" : "", cbSize);
     3541    }
     3542
     3543    return VINF_SUCCESS;
     3544}
     3545
     3546/**
     3547 * Attempts to verify the raw partition path.
     3548 *
     3549 * We don't want to trust RTDvm and the partition device node morphing blindly.
     3550 */
     3551static int vmdkRawDescVerifyPartitionPath(PVMDKIMAGE pImage, PVDISKRAWPARTDESC pPartDesc, uint32_t idxPartition,
     3552                                          const char *pszRawDrive, RTFILE hRawDrive, uint32_t cbSector, RTDVMVOLUME hVol)
     3553{
     3554    RT_NOREF(pImage, pPartDesc, idxPartition, pszRawDrive, hRawDrive, cbSector, hVol);
     3555
     3556    /*
     3557     * Try open the raw partition device.
     3558     */
     3559    RTFILE hRawPart = NIL_RTFILE;
     3560    int rc = RTFileOpen(&hRawPart, pPartDesc->pszRawDevice, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
     3561    if (RT_FAILURE(rc))
     3562        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3563                         N_("VMDK: Image path: '%s'. Failed to open partition #%u on '%s' via '%s' (%Rrc)"),
     3564                         pImage->pszFilename, idxPartition, pszRawDrive, pPartDesc->pszRawDevice, rc);
     3565
     3566    /*
     3567     * Compare the partition UUID if we can get it.
     3568     */
     3569#ifdef RT_OS_WINDOWS
     3570    DWORD cbReturned;
     3571
     3572    /* 1. Get the device numbers for both handles, they should have the same disk. */
     3573    STORAGE_DEVICE_NUMBER DevNum1;
     3574    RT_ZERO(DevNum1);
     3575    if (!DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_STORAGE_GET_DEVICE_NUMBER,
     3576                         NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, &DevNum1, sizeof(DevNum1), &cbReturned, NULL /*pOverlapped*/))
     3577        rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
     3578                       N_("VMDK: Image path: '%s'. IOCTL_STORAGE_GET_DEVICE_NUMBER failed on '%s': %u"),
     3579                       pImage->pszFilename, pszRawDrive, GetLastError());
     3580
     3581    STORAGE_DEVICE_NUMBER DevNum2;
     3582    RT_ZERO(DevNum2);
     3583    if (!DeviceIoControl((HANDLE)RTFileToNative(hRawPart), IOCTL_STORAGE_GET_DEVICE_NUMBER,
     3584                         NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, &DevNum2, sizeof(DevNum2), &cbReturned, NULL /*pOverlapped*/))
     3585        rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
     3586                       N_("VMDK: Image path: '%s'. IOCTL_STORAGE_GET_DEVICE_NUMBER failed on '%s': %u"),
     3587                       pImage->pszFilename, pPartDesc->pszRawDevice, GetLastError());
     3588    if (   RT_SUCCESS(rc)
     3589        && (   DevNum1.DeviceNumber != DevNum2.DeviceNumber
     3590            || DevNum1.DeviceType   != DevNum2.DeviceType))
     3591        rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
     3592                       N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s' (%#x != %#x || %#x != %#x)"),
     3593                       pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
     3594                       DevNum1.DeviceNumber, DevNum2.DeviceNumber, DevNum1.DeviceType, DevNum2.DeviceType);
     3595    if (RT_SUCCESS(rc))
     3596    {
     3597        /* Get the partitions from the raw drive and match up with the volume info
     3598           from RTDvm.  The partition number is found in DevNum2. */
     3599        DWORD cbNeeded = 0;
     3600        if (   DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
     3601                               NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, NULL, 0, &cbNeeded, NULL /*pOverlapped*/)
     3602            || cbNeeded < RT_UOFFSETOF_DYN(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]))
     3603            cbNeeded = RT_UOFFSETOF_DYN(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[64]);
     3604        cbNeeded += sizeof(PARTITION_INFORMATION_EX) * 2; /* just in case */
     3605        DRIVE_LAYOUT_INFORMATION_EX *pLayout = (DRIVE_LAYOUT_INFORMATION_EX *)RTMemTmpAllocZ(cbNeeded);
     3606        if (pLayout)
     3607        {
     3608            cbReturned = 0;
     3609            if (DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
     3610                                NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, pLayout, cbNeeded, &cbReturned, NULL /*pOverlapped*/))
     3611            {
     3612                /* Find the entry with the given partition number (it's not an index, array contains empty MBR entries ++). */
     3613                unsigned iEntry = 0;
     3614                while (   iEntry < pLayout->PartitionCount
     3615                       && pLayout->PartitionEntry[iEntry].PartitionNumber != DevNum2.PartitionNumber)
     3616                    iEntry++;
     3617                if (iEntry < pLayout->PartitionCount)
     3618                {
     3619                    /* Compare the basics */
     3620                    PARTITION_INFORMATION_EX const * const pLayoutEntry = &pLayout->PartitionEntry[iEntry];
     3621                    if (pLayoutEntry->StartingOffset.QuadPart != (int64_t)pPartDesc->offStartInVDisk)
     3622                        rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
     3623                                       N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': StartingOffset %RU64, expected %RU64"),
     3624                                       pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
     3625                                       pLayoutEntry->StartingOffset.QuadPart, pPartDesc->offStartInVDisk);
     3626                    else if (pLayoutEntry->PartitionLength.QuadPart != (int64_t)pPartDesc->cbData)
     3627                        rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
     3628                                       N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': PartitionLength %RU64, expected %RU64"),
     3629                                       pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
     3630                                       pLayoutEntry->PartitionLength.QuadPart, pPartDesc->cbData);
     3631                    /** @todo We could compare the MBR type, GPT type and ID. */
     3632                    RT_NOREF(hVol);
     3633                }
     3634                else
     3635                    rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
     3636                                   N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': PartitionCount (%#x vs %#x)"),
     3637                                   pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
     3638                                   DevNum2.PartitionNumber, pLayout->PartitionCount);
     3639# ifndef LOG_ENABLED
     3640                if (RT_FAILURE(rc))
     3641# endif
     3642                {
     3643                    LogRel(("VMDK: Windows reports %u partitions for '%s':\n", pLayout->PartitionCount, pszRawDrive));
     3644                    PARTITION_INFORMATION_EX const *pEntry = &pLayout->PartitionEntry[0];
     3645                    for (DWORD i = 0; i < pLayout->PartitionCount; i++, pEntry++)
     3646                    {
     3647                        LogRel(("VMDK: #%u/%u: %016RU64 LB %016RU64 style=%d rewrite=%d",
     3648                                i, pEntry->PartitionNumber, pEntry->StartingOffset.QuadPart, pEntry->PartitionLength.QuadPart,
     3649                                pEntry->PartitionStyle, pEntry->RewritePartition));
     3650                        if (pEntry->PartitionStyle == PARTITION_STYLE_MBR)
     3651                            LogRel((" type=%#x boot=%d rec=%d hidden=%u\n", pEntry->Mbr.PartitionType, pEntry->Mbr.BootIndicator,
     3652                                    pEntry->Mbr.RecognizedPartition, pEntry->Mbr.HiddenSectors));
     3653                        else if (pEntry->PartitionStyle == PARTITION_STYLE_GPT)
     3654                            LogRel((" type=%RTuuid id=%RTuuid aatrib=%RX64 name=%.36ls\n", &pEntry->Gpt.PartitionType,
     3655                                    &pEntry->Gpt.PartitionId, pEntry->Gpt.Attributes, &pEntry->Gpt.Name[0]));
     3656                        else
     3657                            LogRel(("\n"));
     3658                    }
     3659                    LogRel(("VMDK: Looked for partition #%u (%u, '%s') at %RU64 LB %RU64\n", DevNum2.PartitionNumber,
     3660                            idxPartition, pPartDesc->pszRawDevice, pPartDesc->offStartInVDisk, pPartDesc->cbData));
     3661               }
     3662            }
     3663            else
     3664                rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
     3665                               N_("VMDK: Image path: '%s'. IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed on '%s': %u (cb %u, cbRet %u)"),
     3666                               pImage->pszFilename, pPartDesc->pszRawDevice, GetLastError(), cbNeeded, cbReturned);
     3667            RTMemTmpFree(pLayout);
     3668        }
     3669        else
     3670            rc = VERR_NO_TMP_MEMORY;
     3671    }
     3672#else
     3673    RT_NOREF(hVol); /* PORTME */
     3674#endif
     3675    if (RT_SUCCESS(rc))
     3676    {
     3677        /*
     3678         * Compare the first 32 sectors of the partition.
     3679         *
     3680         * This might not be conclusive, but for partitions formatted with the more
     3681         * common file systems it should be as they have a superblock copy at or near
     3682         * the start of the partition (fat, fat32, ntfs, and ext4 does at least).
     3683         */
     3684        size_t const cbToCompare = (size_t)RT_MIN(pPartDesc->cbData / cbSector, 32) * cbSector;
     3685        uint8_t *pbSector1 = (uint8_t *)RTMemTmpAlloc(cbToCompare * 2);
     3686        if (pbSector1 != NULL)
     3687        {
     3688            uint8_t *pbSector2 = pbSector1 + cbToCompare;
     3689
     3690            /* Do the comparing, we repeat if it fails and the data might be volatile. */
     3691            uint64_t uPrevCrc1 = 0;
     3692            uint64_t uPrevCrc2 = 0;
     3693            uint32_t cStable   = 0;
     3694            for (unsigned iTry = 0; iTry < 256; iTry++)
     3695            {
     3696                rc = RTFileReadAt(hRawDrive, pPartDesc->offStartInVDisk, pbSector1, cbToCompare, NULL);
     3697                if (RT_SUCCESS(rc))
     3698                {
     3699                    rc = RTFileReadAt(hRawPart, pPartDesc->offStartInDevice, pbSector2, cbToCompare, NULL);
     3700                    if (RT_SUCCESS(rc))
     3701                    {
     3702                        if (memcmp(pbSector1, pbSector2, cbToCompare) != 0)
     3703                        {
     3704                            rc = VERR_MISMATCH;
     3705
     3706                            /* Do data stability checks before repeating: */
     3707                            uint64_t const uCrc1 = RTCrc64(pbSector1, cbToCompare);
     3708                            uint64_t const uCrc2 = RTCrc64(pbSector2, cbToCompare);
     3709                            if (   uPrevCrc1 != uCrc1
     3710                                || uPrevCrc2 != uCrc2)
     3711                                cStable = 0;
     3712                            else if (++cStable > 4)
     3713                                break;
     3714                            uPrevCrc1 = uCrc1;
     3715                            uPrevCrc2 = uCrc2;
     3716                            continue;
     3717                        }
     3718                        rc = VINF_SUCCESS;
     3719                    }
     3720                    else
     3721                        rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3722                                       N_("VMDK: Image path: '%s'. Error reading %zu bytes from '%s' at offset %RU64 (%Rrc)"),
     3723                                       pImage->pszFilename, cbToCompare, pPartDesc->pszRawDevice, pPartDesc->offStartInDevice, rc);
     3724                }
     3725                else
     3726                    rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3727                                   N_("VMDK: Image path: '%s'. Error reading %zu bytes from '%s' at offset %RU64 (%Rrc)"),
     3728                                   pImage->pszFilename, cbToCompare, pszRawDrive, pPartDesc->offStartInVDisk, rc);
     3729                break;
     3730            }
     3731            if (rc == VERR_MISMATCH)
     3732            {
     3733                /* Find the first mismatching bytes: */
     3734                size_t offMissmatch = 0;
     3735                while (offMissmatch < cbToCompare && pbSector1[offMissmatch] == pbSector2[offMissmatch])
     3736                    offMissmatch++;
     3737                int cbSample = (int)RT_MIN(cbToCompare - offMissmatch, 16);
     3738
     3739                if (cStable > 0)
     3740                    rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3741                                   N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s' (cStable=%s @%#zx: %.*Rhxs vs %.*Rhxs)"),
     3742                                   pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cStable,
     3743                                   cbSample, &pbSector1[offMissmatch], cbSample, &pbSector2[offMissmatch]);
     3744                else
     3745                {
     3746                    LogRel(("VMDK: Image path: '%s'. Partition #%u path ('%s') verification undecided on '%s' because of unstable data! (@%#zx: %.*Rhxs vs %.*Rhxs)\n",
     3747                            pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
     3748                            cbSample, &pbSector1[offMissmatch], cbSample, &pbSector2[offMissmatch]));
     3749                    rc = -rc;
     3750                }
     3751            }
     3752
     3753            RTMemTmpFree(pbSector1);
     3754        }
     3755        else
     3756            rc = vdIfError(pImage->pIfError, VERR_NO_TMP_MEMORY, RT_SRC_POS,
     3757                           N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes for a temporary read buffer\n"),
     3758                           pImage->pszFilename, cbToCompare * 2);
     3759    }
     3760    RTFileClose(hRawPart);
     3761    return rc;
     3762}
     3763
     3764#ifdef RT_OS_WINDOWS
     3765/**
     3766 * Transform the physical drive device name into the one for the given partition.
     3767 */
     3768static int vmdkRawDescWinMakePartitionName(PVMDKIMAGE pImage, const char *pszRawDrive, RTFILE hRawDrive, uint32_t idxPartition,
     3769                                           char **ppszRawPartition)
     3770{
     3771    /*
     3772     * First variant is \\.\PhysicalDriveX -> \\.\HarddiskXPartition{idxPartition}
     3773     *
     3774     * Find the start of the digits and validate name prefix before using the digit
     3775     * portition to construct the partition device node name.
     3776     */
     3777    size_t offDigits = strlen(pszRawDrive);
     3778    if (offDigits > 0 && RT_C_IS_DIGIT(pszRawDrive[offDigits - 1]))
     3779    {
     3780        do
     3781            offDigits--;
     3782        while (offDigits > 0 && RT_C_IS_DIGIT(pszRawDrive[offDigits - 1]));
     3783        static const char s_szBaseName[] = "PhysicalDrive";
     3784        if (   offDigits > sizeof(s_szBaseName)
     3785            && RTPATH_IS_SLASH(pszRawDrive[offDigits - sizeof(s_szBaseName)])
     3786            && RTStrNICmp(&pszRawDrive[offDigits - sizeof(s_szBaseName) + 1], RT_STR_TUPLE(s_szBaseName)) == 0)
     3787        {
     3788            RTStrAPrintf(ppszRawPartition, "\\\\.\\Harddisk%sPartition%u", &pszRawDrive[offDigits], idxPartition);
     3789            return VINF_SUCCESS;
     3790        }
     3791    }
     3792
     3793    /*
     3794     * Query the name.  We should then get "\Device\HarddiskX\DRy" back and we can parse
     3795     * the X out of that and use it to construct the \\.\HarddiskXPartition{idxPartition}.
     3796     */
     3797    UNICODE_STRING NtName;
     3798    int rc = RTNtPathFromHandle(&NtName, (HANDLE)RTFileToNative(hRawDrive), 0 /*cwcExtra*/);
     3799    if (RT_SUCCESS(rc))
     3800    {
     3801        Log(("RTNtPathFromHandle: pszRawDrive=%s; NtName=%ls\n", pszRawDrive, NtName.Buffer));
     3802        static const char s_szBaseName[] = "\\Device\\Harddisk";
     3803        if (   RTUtf16NCmpAscii(NtName.Buffer, s_szBaseName, sizeof(s_szBaseName) - 1) == 0
     3804            && RTUniCpIsDecDigit(NtName.Buffer[sizeof(s_szBaseName) - 1])) /* A bit HACKY as words in UTF-16 != codepoint. */
     3805        {
     3806            offDigits = sizeof(s_szBaseName) - 1;
     3807            size_t offEndDigits = offDigits + 1;
     3808            while (RTUniCpIsDecDigit(NtName.Buffer[offEndDigits]))
     3809                offEndDigits++;
     3810            if (   NtName.Buffer[offEndDigits] == '\\'
     3811                && NtName.Buffer[offEndDigits + 1] == 'D'
     3812                && NtName.Buffer[offEndDigits + 2] == 'R'
     3813                && RTUniCpIsDecDigit(NtName.Buffer[offEndDigits + 3]))
     3814            {
     3815                RTStrAPrintf(ppszRawPartition, "\\\\.\\Harddisk%.*lsPartition%u",
     3816                             offEndDigits - offDigits, &NtName.Buffer[offDigits], idxPartition);
     3817                RTNtPathFree(&NtName, NULL);
     3818                return VINF_SUCCESS;
     3819            }
     3820        }
     3821        rc = vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     3822                       N_("VMDK: Image path: '%s'. Do not know how to translate '%s' ('%ls') into a name for partition #%u"),
     3823                       pImage->pszFilename, pszRawDrive, NtName.Buffer, idxPartition);
     3824        RTNtPathFree(&NtName, NULL);
     3825    }
     3826    else
     3827        rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3828                       N_("VMDK: Image path: '%s'. Failed to get the NT path for '%s' and therefore unable to determin path to partition #%u (%Rrc)"),
     3829                       pImage->pszFilename, pszRawDrive, idxPartition, rc);
     3830    return rc;
     3831}
     3832#endif /* RT_OS_WINDOWS */
     3833
     3834/**
     3835 * Worker for vmdkMakeRawDescriptor that adds partition descriptors when the
     3836 * 'Partitions' configuration value is present.
     3837 *
     3838 * @returns VBox status code, error message has been set on failure.
     3839 *
     3840 * @note    Caller is assumed to clean up @a pRawDesc and release
     3841 *          @a *phVolToRelease.
     3842 * @internal
     3843 */
     3844static int vmdkRawDescDoPartitions(PVMDKIMAGE pImage, RTDVM hVolMgr, PVDISKRAW pRawDesc,
     3845                                   RTFILE hRawDrive, const char *pszRawDrive, uint32_t cbSector,
     3846                                   uint32_t fPartitions, uint32_t fPartitionsReadOnly, bool fRelative,
     3847                                   PRTDVMVOLUME phVolToRelease)
     3848{
     3849    *phVolToRelease = NIL_RTDVMVOLUME;
     3850
     3851    /* Check sanity/understanding. */
     3852    Assert(fPartitions);
     3853    Assert((fPartitions & fPartitionsReadOnly) == fPartitionsReadOnly); /* RO should be a sub-set */
     3854
     3855    /*
     3856     * Allocate on descriptor for each volume up front.
     3857     */
     3858    uint32_t const cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
     3859
     3860    PVDISKRAWPARTDESC paPartDescs = NULL;
     3861    int rc = vmdkRawDescAppendPartDesc(pImage, pRawDesc, cVolumes, &paPartDescs);
     3862    AssertRCReturn(rc, rc);
     3863
     3864    /*
     3865     * Enumerate the partitions (volumes) on the disk and create descriptors for each of them.
     3866     */
     3867    uint32_t    fPartitionsLeft = fPartitions;
     3868    RTDVMVOLUME hVol            = NIL_RTDVMVOLUME; /* the current volume, needed for getting the next. */
     3869    for (uint32_t i = 0; i < cVolumes; i++)
     3870    {
     3871        /*
     3872         * Get the next/first volume and release the current.
     3873         */
     3874        RTDVMVOLUME hVolNext = NIL_RTDVMVOLUME;
     3875        if (i == 0)
     3876            rc = RTDvmMapQueryFirstVolume(hVolMgr, &hVolNext);
     3877        else
     3878            rc = RTDvmMapQueryNextVolume(hVolMgr, hVol, &hVolNext);
     3879        if (RT_FAILURE(rc))
     3880            return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3881                             N_("VMDK: Image path: '%s'. Volume enumeration failed at volume #%u on '%s' (%Rrc)"),
     3882                             pImage->pszFilename, i, pszRawDrive, rc);
     3883        uint32_t cRefs = RTDvmVolumeRelease(hVol);
     3884        Assert(cRefs != UINT32_MAX); RT_NOREF(cRefs);
     3885        *phVolToRelease = hVol = hVolNext;
     3886
     3887        /*
     3888         * Depending on the fPartitions selector and associated read-only mask,
     3889         * the guest either gets read-write or read-only access (bits set)
     3890         * or no access (selector bit clear, access directed to the VMDK).
     3891         */
     3892        paPartDescs[i].cbData = RTDvmVolumeGetSize(hVol);
     3893
     3894        uint64_t offVolumeEndIgnored = 0;
     3895        rc = RTDvmVolumeQueryRange(hVol, &paPartDescs[i].offStartInVDisk, &offVolumeEndIgnored);
     3896        if (RT_FAILURE(rc))
     3897            return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     3898                             N_("VMDK: Image path: '%s'. Failed to get location of volume #%u on '%s' (%Rrc)"),
     3899                             pImage->pszFilename, i, pszRawDrive, rc);
     3900        Assert(paPartDescs[i].cbData == offVolumeEndIgnored + 1 - paPartDescs[i].offStartInVDisk);
     3901
     3902        /* Note! The index must match IHostDrivePartition::number. */
     3903        uint32_t idxPartition = RTDvmVolumeGetIndex(hVol, RTDVMVOLIDX_HOST);
     3904        if (   idxPartition < 32
     3905            && (fPartitions & RT_BIT_32(idxPartition)))
     3906        {
     3907            fPartitionsLeft &= ~RT_BIT_32(idxPartition);
     3908            if (fPartitionsReadOnly & RT_BIT_32(idxPartition))
     3909                paPartDescs[i].uFlags |= VDISKRAW_READONLY;
     3910
     3911            if (!fRelative)
     3912            {
     3913                /*
     3914                 * Accessing the drive thru the main device node (pRawDesc->pszRawDisk).
     3915                 */
     3916                paPartDescs[i].offStartInDevice = paPartDescs[i].offStartInVDisk;
     3917                paPartDescs[i].pszRawDevice     = RTStrDup(pszRawDrive);
     3918                AssertPtrReturn(paPartDescs[i].pszRawDevice, VERR_NO_STR_MEMORY);
     3919            }
     3920            else
     3921            {
     3922                /*
     3923                 * Relative means access the partition data via the device node for that
     3924                 * partition, allowing the sysadmin/OS to allow a user access to individual
     3925                 * partitions without necessarily being able to compromise the host OS.
     3926                 * Obviously, the creation of the VMDK requires read access to the main
     3927                 * device node for the drive, but that's a one-time thing and can be done
     3928                 * by the sysadmin.   Here data starts at offset zero in the device node.
     3929                 */
     3930                paPartDescs[i].offStartInDevice = 0;
     3931
     3932#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
     3933                /* /dev/rdisk1 -> /dev/rdisk1s2 (s=slice) */
     3934                RTStrAPrintf(&paPartDescs[i].pszRawDevice, "%ss%u", pszRawDrive, idxPartition);
     3935#elif defined(RT_OS_LINUX)
     3936                /* Two naming schemes here: /dev/nvme0n1 -> /dev/nvme0n1p1;  /dev/sda -> /dev/sda1 */
     3937                RTStrAPrintf(&paPartDescs[i].pszRawDevice,
     3938                             RT_C_IS_DIGIT(pszRawDrive[strlen(pszRawDrive) - 1]) ? "%sp%u" : "%s%u", pszRawDrive, idxPartition);
     3939#elif defined(RT_OS_WINDOWS)
     3940                rc = vmdkRawDescWinMakePartitionName(pImage, pszRawDrive, hRawDrive, idxPartition, &paPartDescs[i].pszRawDevice);
     3941                AssertRCReturn(rc, rc);
     3942#else
     3943                AssertFailedReturn(VERR_INTERNAL_ERROR_4); /* The option parsing code should have prevented this - PORTME */
     3944#endif
     3945                AssertPtrReturn(paPartDescs[i].pszRawDevice, VERR_NO_STR_MEMORY);
     3946
     3947                rc = vmdkRawDescVerifyPartitionPath(pImage, &paPartDescs[i], idxPartition, pszRawDrive, hRawDrive, cbSector, hVol);
     3948                AssertRCReturn(rc, rc);
     3949            }
     3950        }
     3951        else
     3952        {
     3953            /* Not accessible to the guest. */
     3954            paPartDescs[i].offStartInDevice = 0;
     3955            paPartDescs[i].pszRawDevice     = NULL;
     3956        }
     3957    } /* for each volume */
     3958
     3959    RTDvmVolumeRelease(hVol);
     3960    *phVolToRelease = NIL_RTDVMVOLUME;
     3961
     3962    /*
     3963     * Check that we found all the partitions the user selected.
     3964     */
     3965    if (fPartitionsLeft)
     3966    {
     3967        char   szLeft[3 * sizeof(fPartitions) * 8];
     3968        size_t cchLeft = 0;
     3969        for (unsigned i = 0; i < sizeof(fPartitions) * 8; i++)
     3970            if (fPartitionsLeft & RT_BIT_32(i))
     3971                cchLeft += RTStrPrintf(&szLeft[cchLeft], sizeof(szLeft) - cchLeft, cchLeft ? "%u" : ",%u", i);
     3972        return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     3973                             N_("VMDK: Image path: '%s'. Not all the specified partitions for drive '%s' was found: %s"),
     3974                             pImage->pszFilename, pszRawDrive, szLeft);
     3975    }
     3976
     3977    return VINF_SUCCESS;
     3978}
     3979
     3980/**
     3981 * Worker for vmdkMakeRawDescriptor that adds partition descriptors with copies
     3982 * of the partition tables and associated padding areas when the 'Partitions'
     3983 * configuration value is present.
     3984 *
     3985 * The guest is not allowed access to the partition tables, however it needs
     3986 * them to be able to access the drive.  So, create descriptors for each of the
     3987 * tables and attach the current disk content.  vmdkCreateRawImage() will later
     3988 * write the content to the VMDK.  Any changes the guest later makes to the
     3989 * partition tables will then go to the VMDK copy, rather than the host drive.
     3990 *
     3991 * @returns VBox status code, error message has been set on failure.
     3992 *
     3993 * @note    Caller is assumed to clean up @a pRawDesc
     3994 * @internal
     3995 */
     3996static int vmdkRawDescDoCopyPartitionTables(PVMDKIMAGE pImage, RTDVM hVolMgr, PVDISKRAW pRawDesc,
     3997                                            const char *pszRawDrive, RTFILE hRawDrive, void *pvBootSector, size_t cbBootSector)
     3998{
     3999    /*
     4000     * Query the locations.
     4001     */
     4002    /* Determin how many locations there are: */
     4003    size_t cLocations = 0;
     4004    int rc = RTDvmMapQueryTableLocations(hVolMgr, RTDVMMAPQTABLOC_F_INCLUDE_LEGACY, NULL, 0, &cLocations);
     4005    if (rc != VERR_BUFFER_OVERFLOW)
     4006        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4007                         N_("VMDK: Image path: '%s'. RTDvmMapQueryTableLocations failed on '%s' (%Rrc)"),
     4008                         pImage->pszFilename, pszRawDrive, rc);
     4009    AssertReturn(cLocations > 0 && cLocations < _16M, VERR_INTERNAL_ERROR_5);
     4010
     4011    /* We can allocate the partition descriptors here to save an intentation level. */
     4012    PVDISKRAWPARTDESC paPartDescs = NULL;
     4013    rc = vmdkRawDescAppendPartDesc(pImage, pRawDesc, (uint32_t)cLocations, &paPartDescs);
     4014    AssertRCReturn(rc, rc);
     4015
     4016    /* Allocate the result table and repeat the location table query: */
     4017    PRTDVMTABLELOCATION paLocations = (PRTDVMTABLELOCATION)RTMemAllocZ(sizeof(paLocations[0]) * cLocations);
     4018    if (!paLocations)
     4019        return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes"),
     4020                         pImage->pszFilename, sizeof(paLocations[0]) * cLocations);
     4021    rc = RTDvmMapQueryTableLocations(hVolMgr, RTDVMMAPQTABLOC_F_INCLUDE_LEGACY, paLocations, cLocations, NULL);
     4022    if (RT_SUCCESS(rc))
     4023    {
     4024        /*
     4025         * Translate them into descriptors.
     4026         *
     4027         * We restrict the amount of partition alignment padding to 4MiB as more
     4028         * will just be a waste of space.  The use case for including the padding
     4029         * are older boot loaders and boot manager (including one by a team member)
     4030         * that put data and code in the 62 sectors between the MBR and the first
     4031         * partition (total of 63).  Later CHS was abandond and partition started
     4032         * being aligned on power of two sector boundraries (typically 64KiB or
     4033         * 1MiB depending on the media size).
     4034         */
     4035        for (size_t i = 0; i < cLocations && RT_SUCCESS(rc); i++)
     4036        {
     4037            Assert(paLocations[i].cb > 0);
     4038            if (paLocations[i].cb <= _64M)
     4039            {
     4040                /* Create the partition descriptor entry: */
     4041                //paPartDescs[i].pszRawDevice      = NULL;
     4042                //paPartDescs[i].offStartInDevice  = 0;
     4043                //paPartDescs[i].uFlags            = 0;
     4044                paPartDescs[i].offStartInVDisk   = paLocations[i].off;
     4045                paPartDescs[i].cbData            = paLocations[i].cb;
     4046                if (paPartDescs[i].cbData < _4M)
     4047                    paPartDescs[i].cbData = RT_MIN(paPartDescs[i].cbData + paLocations[i].cbPadding, _4M);
     4048                paPartDescs[i].pvPartitionData = RTMemAllocZ((size_t)paPartDescs[i].cbData);
     4049                if (paPartDescs[i].pvPartitionData)
     4050                {
     4051                    /* Read the content from the drive: */
     4052                    rc = RTFileReadAt(hRawDrive, paPartDescs[i].offStartInVDisk, paPartDescs[i].pvPartitionData,
     4053                                      (size_t)paPartDescs[i].cbData, NULL);
     4054                    if (RT_SUCCESS(rc))
     4055                    {
     4056                        /* Do we have custom boot sector code? */
     4057                        if (pvBootSector && cbBootSector && paPartDescs[i].offStartInVDisk == 0)
     4058                        {
     4059                            /* Note! Old code used to quietly drop the bootsector if it was considered too big.
     4060                                     Instead we fail as we weren't able to do what the user requested us to do.
     4061                                     Better if the user knows than starts questioning why the guest isn't
     4062                                     booting as expected. */
     4063                            if (cbBootSector <= paPartDescs[i].cbData)
     4064                                memcpy(paPartDescs[i].pvPartitionData, pvBootSector, cbBootSector);
     4065                            else
     4066                                rc = vdIfError(pImage->pIfError, VERR_TOO_MUCH_DATA, RT_SRC_POS,
     4067                                               N_("VMDK: Image path: '%s'. The custom boot sector is too big: %zu bytes, %RU64 bytes available"),
     4068                                               pImage->pszFilename, cbBootSector, paPartDescs[i].cbData);
     4069                        }
     4070                    }
     4071                    else
     4072                        rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4073                                       N_("VMDK: Image path: '%s'. Failed to read partition at off %RU64 length %zu from '%s' (%Rrc)"),
     4074                                       pImage->pszFilename, paPartDescs[i].offStartInVDisk,
     4075                                       (size_t)paPartDescs[i].cbData, pszRawDrive, rc);
     4076                }
     4077                else
     4078                    rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4079                                   N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes for copying the partition table at off %RU64"),
     4080                                      pImage->pszFilename, (size_t)paPartDescs[i].cbData, paPartDescs[i].offStartInVDisk);
     4081            }
     4082            else
     4083                rc = vdIfError(pImage->pIfError, VERR_TOO_MUCH_DATA, RT_SRC_POS,
     4084                               N_("VMDK: Image path: '%s'. Partition table #%u at offset %RU64 in '%s' is to big: %RU64 bytes"),
     4085                                  pImage->pszFilename, i, paLocations[i].off, pszRawDrive, paLocations[i].cb);
     4086        }
     4087    }
     4088    else
     4089        rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4090                       N_("VMDK: Image path: '%s'. RTDvmMapQueryTableLocations failed on '%s' (%Rrc)"),
     4091                          pImage->pszFilename, pszRawDrive, rc);
     4092    RTMemFree(paLocations);
     4093    return rc;
     4094}
     4095
     4096/**
     4097 * Opens the volume manager for the raw drive when in selected-partition mode.
     4098 *
     4099 * @param   pImage      The VMDK image (for errors).
     4100 * @param   hRawDrive   The raw drive handle.
     4101 * @param   pszRawDrive The raw drive device path (for errors).
     4102 * @param   cbSector    The sector size.
     4103 * @param   phVolMgr    Where to return the handle to the volume manager on
     4104 *                      success.
     4105 * @returns VBox status code, errors have been reported.
     4106 * @internal
     4107 */
     4108static int vmdkRawDescOpenVolMgr(PVMDKIMAGE pImage, RTFILE hRawDrive, const char *pszRawDrive, uint32_t cbSector, PRTDVM phVolMgr)
     4109{
     4110    *phVolMgr = NIL_RTDVM;
     4111
     4112    RTVFSFILE hVfsFile = NIL_RTVFSFILE;
     4113    int rc = RTVfsFileFromRTFile(hRawDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, true /*fLeaveOpen*/, &hVfsFile);
     4114    if (RT_FAILURE(rc))
     4115        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4116                         N_("VMDK: Image path: '%s'.  RTVfsFileFromRTFile failed for '%s' handle (%Rrc)"),
     4117                         pImage->pszFilename, pszRawDrive, rc);
     4118
     4119    RTDVM hVolMgr = NIL_RTDVM;
     4120    rc = RTDvmCreate(&hVolMgr, hVfsFile, cbSector, 0 /*fFlags*/);
     4121
     4122    RTVfsFileRelease(hVfsFile);
     4123
     4124    if (RT_FAILURE(rc))
     4125        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4126                         N_("VMDK: Image path: '%s'. Failed to create volume manager instance for '%s' (%Rrc)"),
     4127                         pImage->pszFilename, pszRawDrive, rc);
     4128
     4129    rc = RTDvmMapOpen(hVolMgr);
     4130    if (RT_SUCCESS(rc))
     4131    {
     4132        *phVolMgr = hVolMgr;
     4133        return VINF_SUCCESS;
     4134    }
     4135    RTDvmRelease(hVolMgr);
     4136    return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: Image path: '%s'. RTDvmMapOpen failed for '%s' (%Rrc)"),
     4137                     pImage->pszFilename, pszRawDrive, rc);
     4138}
     4139
     4140/**
     4141 * Opens the raw drive device and get the sizes for it.
     4142 *
     4143 * @param   pImage          The image (for error reporting).
     4144 * @param   pszRawDrive     The device/whatever to open.
     4145 * @param   phRawDrive      Where to return the file handle.
     4146 * @param   pcbRawDrive     Where to return the size.
     4147 * @param   pcbSector       Where to return the sector size.
     4148 * @returns IPRT status code, errors have been reported.
     4149 * @internal
     4150 */
     4151static int vmkdRawDescOpenDevice(PVMDKIMAGE pImage, const char *pszRawDrive,
     4152                                 PRTFILE phRawDrive, uint64_t *pcbRawDrive, uint32_t *pcbSector)
     4153{
     4154    /*
     4155     * Open the device for the raw drive.
     4156     */
     4157    RTFILE hRawDrive = NIL_RTFILE;
     4158    int rc = RTFileOpen(&hRawDrive, pszRawDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
     4159    if (RT_FAILURE(rc))
     4160        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4161                         N_("VMDK: Image path: '%s'. Failed to open the raw drive '%s' for reading (%Rrc)"),
     4162                         pImage->pszFilename, pszRawDrive, rc);
     4163
     4164    /*
     4165     * Get the sector size.
     4166     */
     4167    uint32_t cbSector = 0;
     4168    rc = RTFileQuerySectorSize(hRawDrive, &cbSector);
     4169    if (RT_SUCCESS(rc))
     4170    {
     4171        /* sanity checks */
     4172        if (   cbSector >= 512
     4173            && cbSector <= _64K
     4174            && RT_IS_POWER_OF_TWO(cbSector))
     4175        {
     4176            /*
     4177             * Get the size.
     4178             */
     4179            uint64_t cbRawDrive = 0;
     4180            rc = RTFileQuerySize(hRawDrive, &cbRawDrive);
     4181            if (RT_SUCCESS(rc))
     4182            {
     4183                /* Check whether cbSize is actually sensible. */
     4184                if (cbRawDrive > cbSector && (cbRawDrive % cbSector) == 0)
     4185                {
     4186                    *phRawDrive  = hRawDrive;
     4187                    *pcbRawDrive = cbRawDrive;
     4188                    *pcbSector   = cbSector;
     4189                    return VINF_SUCCESS;
     4190                }
     4191                rc = vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4192                               N_("VMDK: Image path: '%s'.  Got a bogus size for the raw drive '%s': %RU64 (sector size %u)"),
     4193                               pImage->pszFilename, pszRawDrive, cbRawDrive, cbSector);
     4194            }
     4195            else
     4196                rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4197                               N_("VMDK: Image path: '%s'. Failed to query size of the drive '%s' (%Rrc)"),
     4198                               pImage->pszFilename, pszRawDrive, rc);
     4199        }
     4200        else
     4201            rc = vdIfError(pImage->pIfError, VERR_OUT_OF_RANGE, RT_SRC_POS,
     4202                           N_("VMDK: Image path: '%s'. Unsupported sector size for '%s': %u (%#x)"),
     4203                           pImage->pszFilename, pszRawDrive, cbSector, cbSector);
     4204    }
     4205    else
     4206        rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4207                       N_("VMDK: Image path: '%s'. Failed to get the sector size for '%s' (%Rrc)"),
     4208                       pImage->pszFilename, pszRawDrive, rc);
     4209    RTFileClose(hRawDrive);
     4210    return rc;
     4211}
     4212
     4213/**
     4214 * Reads the raw disk configuration, leaving initalization and cleanup to the
     4215 * caller (regardless of return status).
     4216 *
     4217 * @returns VBox status code, errors properly reported.
     4218 * @internal
     4219 */
     4220static int vmdkRawDescParseConfig(PVMDKIMAGE pImage, char **ppszRawDrive,
     4221                                  uint32_t *pfPartitions, uint32_t *pfPartitionsReadOnly,
     4222                                  void **ppvBootSector, size_t *pcbBootSector, bool *pfRelative,
     4223                                  char **ppszFreeMe)
     4224{
     4225    PVDINTERFACECONFIG pImgCfg = VDIfConfigGet(pImage->pVDIfsImage);
     4226    if (!pImgCfg)
     4227        return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4228                         N_("VMDK: Image path: '%s'. Getting config interface failed"), pImage->pszFilename);
     4229
     4230    /*
     4231     * RawDrive = path
     4232     */
     4233    int rc = VDCFGQueryStringAlloc(pImgCfg, "RawDrive", ppszRawDrive);
     4234    if (RT_FAILURE(rc))
     4235        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4236                         N_("VMDK: Image path: '%s'. Getting 'RawDrive' configuration failed (%Rrc)"), pImage->pszFilename, rc);
     4237    AssertPtrReturn(*ppszRawDrive, VERR_INTERNAL_ERROR_3);
     4238
     4239    /*
     4240     * Partitions=n[r][,...]
     4241     */
     4242    uint32_t const cMaxPartitionBits = sizeof(*pfPartitions) * 8 /* ASSUMES 8 bits per char */;
     4243    *pfPartitions = *pfPartitionsReadOnly = 0;
     4244
     4245    rc = VDCFGQueryStringAlloc(pImgCfg, "Partitions", ppszFreeMe);
     4246    if (RT_SUCCESS(rc))
     4247    {
     4248        char *psz = *ppszFreeMe;
     4249        while (*psz != '\0')
     4250        {
     4251            char *pszNext;
     4252            uint32_t u32;
     4253            rc = RTStrToUInt32Ex(psz, &pszNext, 0, &u32);
     4254            if (rc == VWRN_NUMBER_TOO_BIG || rc == VWRN_NEGATIVE_UNSIGNED)
     4255                rc = -rc;
     4256            if (RT_FAILURE(rc))
     4257                return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4258                                 N_("VMDK: Image path: '%s'. Parsing 'Partitions' config value failed. Incorrect value (%Rrc): %s"),
     4259                                 pImage->pszFilename, rc, psz);
     4260            if (u32 >= cMaxPartitionBits)
     4261                return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4262                                 N_("VMDK: Image path: '%s'. 'Partitions' config sub-value out of range: %RU32, max %RU32"),
     4263                                 pImage->pszFilename, u32, cMaxPartitionBits);
     4264            *pfPartitions |= RT_BIT_32(u32);
     4265            psz = pszNext;
     4266            if (*psz == 'r')
     4267            {
     4268                *pfPartitionsReadOnly |= RT_BIT_32(u32);
     4269                psz++;
     4270            }
     4271            if (*psz == ',')
     4272                psz++;
     4273            else if (*psz != '\0')
     4274                return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4275                                 N_("VMDK: Image path: '%s'. Malformed 'Partitions' config value, expected separator: %s"),
     4276                                 pImage->pszFilename, psz);
     4277        }
     4278
     4279        RTStrFree(*ppszFreeMe);
     4280        *ppszFreeMe = NULL;
     4281    }
     4282    else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
     4283        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4284                         N_("VMDK: Image path: '%s'. Getting 'Partitions' configuration failed (%Rrc)"), pImage->pszFilename, rc);
     4285
     4286    /*
     4287     * BootSector=base64
     4288     */
     4289    rc = VDCFGQueryStringAlloc(pImgCfg, "BootSector", ppszFreeMe);
     4290    if (RT_SUCCESS(rc))
     4291    {
     4292        ssize_t cbBootSector = RTBase64DecodedSize(*ppszFreeMe, NULL);
     4293        if (cbBootSector < 0)
     4294            return vdIfError(pImage->pIfError, VERR_INVALID_BASE64_ENCODING, RT_SRC_POS,
     4295                             N_("VMDK: Image path: '%s'. BASE64 decoding failed on the custom bootsector for '%s'"),
     4296                             pImage->pszFilename, *ppszRawDrive);
     4297        if (cbBootSector == 0)
     4298            return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4299                             N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is zero bytes big"),
     4300                             pImage->pszFilename, *ppszRawDrive);
     4301        if (cbBootSector > _4M) /* this is just a preliminary max */
     4302            return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4303                             N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is way too big: %zu bytes, max 4MB"),
     4304                             pImage->pszFilename, *ppszRawDrive, cbBootSector);
     4305
     4306        /* Refuse the boot sector if whole-drive.  This used to be done quietly,
     4307           however, bird disagrees and thinks the user should be told that what
     4308           he/she/it tries to do isn't possible.  There should be less head
     4309           scratching this way when the guest doesn't do the expected thing. */
     4310        if (!*pfPartitions)
     4311            return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4312                             N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is not supported for whole-drive configurations, only when selecting partitions"),
     4313                             pImage->pszFilename, *ppszRawDrive);
     4314
     4315        *pcbBootSector = (size_t)cbBootSector;
     4316        *ppvBootSector = RTMemAlloc((size_t)cbBootSector);
     4317        if (!*ppvBootSector)
     4318            return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
     4319                             N_("VMDK: Image path: '%s'. Failed to allocate %zd bytes for the custom bootsector for '%s'"),
     4320                             pImage->pszFilename, cbBootSector, *ppszRawDrive);
     4321
     4322        rc = RTBase64Decode(*ppszFreeMe, *ppvBootSector, cbBootSector, NULL /*pcbActual*/, NULL /*ppszEnd*/);
     4323        if (RT_FAILURE(rc))
     4324            return  vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
     4325                              N_("VMDK: Image path: '%s'. Base64 decoding of the custom boot sector for '%s' failed (%Rrc)"),
     4326                              pImage->pszFilename, *ppszRawDrive, rc);
     4327
     4328        RTStrFree(*ppszFreeMe);
     4329        *ppszFreeMe = NULL;
     4330    }
     4331    else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
     4332        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4333                         N_("VMDK: Image path: '%s'. Getting 'BootSector' configuration failed (%Rrc)"), pImage->pszFilename, rc);
     4334
     4335    /*
     4336     * Relative=0/1
     4337     */
     4338    *pfRelative = false;
     4339    rc = VDCFGQueryBool(pImgCfg, "Relative", pfRelative);
     4340    if (RT_SUCCESS(rc))
     4341    {
     4342        if (!*pfPartitions && *pfRelative != false)
     4343            return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4344                             N_("VMDK: Image path: '%s'. The 'Relative' option is not supported for whole-drive configurations, only when selecting partitions"),
     4345                             pImage->pszFilename);
     4346#if !defined(RT_OS_DARWIN) && !defined(RT_OS_LINUX) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_WINDOWS) /* PORTME */
     4347        if (*pfRelative == true)
     4348            return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
     4349                             N_("VMDK: Image path: '%s'. The 'Relative' option is not supported on this host OS"),
     4350                             pImage->pszFilename);
     4351#endif
     4352    }
     4353    else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
     4354        return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
     4355                         N_("VMDK: Image path: '%s'. Getting 'Relative' configuration failed (%Rrc)"), pImage->pszFilename, rc);
     4356    else
     4357#ifdef RT_OS_DARWIN /* different default on macOS, see ticketref:1461 (comment 20). */
     4358        *pfRelative = true;
     4359#else
     4360        *pfRelative = false;
     4361#endif
     4362
     4363    return VINF_SUCCESS;
     4364}
     4365
     4366/**
     4367 * Creates a raw drive (nee disk) descriptor.
     4368 *
     4369 * This was originally done in VBoxInternalManage.cpp, but was copied (not move)
     4370 * here much later.  That's one of the reasons why we produce a descriptor just
     4371 * like it does, rather than mixing directly into the vmdkCreateRawImage code.
     4372 *
     4373 * @returns VBox status code.
     4374 * @param   pImage      The image.
     4375 * @param   ppRaw       Where to return the raw drive descriptor.  Caller must
     4376 *                      free it using vmdkRawDescFree regardless of the status
     4377 *                      code.
     4378 * @internal
     4379 */
     4380static int vmdkMakeRawDescriptor(PVMDKIMAGE pImage, PVDISKRAW *ppRaw)
     4381{
     4382    /* Make sure it's NULL. */
     4383    *ppRaw = NULL;
     4384
     4385    /*
     4386     * Read the configuration.
     4387     */
     4388    char       *pszRawDrive         = NULL;
     4389    uint32_t    fPartitions         = 0;    /* zero if whole-drive */
     4390    uint32_t    fPartitionsReadOnly = 0;    /* (subset of fPartitions) */
     4391    void       *pvBootSector        = NULL;
     4392    size_t      cbBootSector        = 0;
     4393    bool        fRelative           = false;
     4394    char       *pszFreeMe           = NULL; /* lazy bird cleanup. */
     4395    int rc = vmdkRawDescParseConfig(pImage, &pszRawDrive, &fPartitions, &fPartitionsReadOnly,
     4396                                    &pvBootSector, &cbBootSector, &fRelative, &pszFreeMe);
     4397    RTStrFree(pszFreeMe);
     4398    if (RT_SUCCESS(rc))
     4399    {
     4400        /*
     4401         * Open the device, getting the sector size and drive size.
     4402         */
     4403        uint64_t  cbSize    = 0;
     4404        uint32_t  cbSector  = 0;
     4405        RTFILE    hRawDrive = NIL_RTFILE;
     4406        rc = vmkdRawDescOpenDevice(pImage, pszRawDrive, &hRawDrive, &cbSize, &cbSector);
     4407        if (RT_SUCCESS(rc))
     4408        {
     4409            /*
     4410             * Create the raw-drive descriptor
     4411             */
     4412            PVDISKRAW pRawDesc = (PVDISKRAW)RTMemAllocZ(sizeof(*pRawDesc));
     4413            if (pRawDesc)
     4414            {
     4415                pRawDesc->szSignature[0] = 'R';
     4416                pRawDesc->szSignature[1] = 'A';
     4417                pRawDesc->szSignature[2] = 'W';
     4418                //pRawDesc->szSignature[3] = '\0';
     4419                if (!fPartitions)
     4420                {
     4421                    /*
     4422                     * It's simple for when doing the whole drive.
     4423                     */
     4424                    pRawDesc->uFlags = VDISKRAW_DISK;
     4425                    rc = RTStrDupEx(&pRawDesc->pszRawDisk, pszRawDrive);
     4426                }
     4427                else
     4428                {
     4429                    /*
     4430                     * In selected partitions mode we've got a lot more work ahead of us.
     4431                     */
     4432                    pRawDesc->uFlags = VDISKRAW_NORMAL;
     4433                    //pRawDesc->pszRawDisk = NULL;
     4434                    //pRawDesc->cPartDescs = 0;
     4435                    //pRawDesc->pPartDescs = NULL;
     4436
     4437                    /* We need to parse the partition map to complete the descriptor: */
     4438                    RTDVM hVolMgr = NIL_RTDVM;
     4439                    rc = vmdkRawDescOpenVolMgr(pImage, hRawDrive, pszRawDrive, cbSector, &hVolMgr);
     4440                    if (RT_SUCCESS(rc))
     4441                    {
     4442                        RTDVMFORMATTYPE enmFormatType = RTDvmMapGetFormatType(hVolMgr);
     4443                        if (   enmFormatType == RTDVMFORMATTYPE_MBR
     4444                            || enmFormatType == RTDVMFORMATTYPE_GPT)
     4445                        {
     4446                            pRawDesc->enmPartitioningType = enmFormatType == RTDVMFORMATTYPE_MBR
     4447                                                          ? VDISKPARTTYPE_MBR : VDISKPARTTYPE_GPT;
     4448
     4449                            /* Add copies of the partition tables:  */
     4450                            rc = vmdkRawDescDoCopyPartitionTables(pImage, hVolMgr, pRawDesc, pszRawDrive, hRawDrive,
     4451                                                                  pvBootSector, cbBootSector);
     4452                            if (RT_SUCCESS(rc))
     4453                            {
     4454                                /* Add descriptors for the partitions/volumes, indicating which
     4455                                   should be accessible and how to access them: */
     4456                                RTDVMVOLUME hVolRelease = NIL_RTDVMVOLUME;
     4457                                rc = vmdkRawDescDoPartitions(pImage, hVolMgr, pRawDesc, hRawDrive, pszRawDrive, cbSector,
     4458                                                             fPartitions, fPartitionsReadOnly, fRelative, &hVolRelease);
     4459                                RTDvmVolumeRelease(hVolRelease);
     4460
     4461                                /* Finally, sort the partition and check consistency (overlaps, etc): */
     4462                                if (RT_SUCCESS(rc))
     4463                                    rc = vmdkRawDescPostProcessPartitions(pImage, pRawDesc, cbSize);
     4464                            }
     4465                        }
     4466                        else
     4467                            rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
     4468                                           N_("VMDK: Image path: '%s'. Unsupported partitioning for the disk '%s': %s"),
     4469                                           pImage->pszFilename, pszRawDrive, RTDvmMapGetFormatType(hVolMgr));
     4470                        RTDvmRelease(hVolMgr);
     4471                    }
     4472                }
     4473                if (RT_SUCCESS(rc))
     4474                {
     4475                    /*
     4476                     * We succeeded.
     4477                     */
     4478                    *ppRaw = pRawDesc;
     4479                    Log(("vmdkMakeRawDescriptor: fFlags=%#x enmPartitioningType=%d cPartDescs=%u pszRawDisk=%s\n",
     4480                         pRawDesc->uFlags, pRawDesc->enmPartitioningType, pRawDesc->cPartDescs, pRawDesc->pszRawDisk));
     4481                    if (pRawDesc->cPartDescs)
     4482                    {
     4483                        Log(("#      VMDK offset         Length  Device offset  PartDataPtr  Device\n"));
     4484                        for (uint32_t i = 0; i < pRawDesc->cPartDescs; i++)
     4485                            Log(("%2u  %14RU64 %14RU64 %14RU64 %#18p %s\n", i, pRawDesc->pPartDescs[i].offStartInVDisk,
     4486                                 pRawDesc->pPartDescs[i].cbData, pRawDesc->pPartDescs[i].offStartInDevice,
     4487                                 pRawDesc->pPartDescs[i].pvPartitionData, pRawDesc->pPartDescs[i].pszRawDevice));
     4488                    }
     4489                }
     4490                else
     4491                    vmdkRawDescFree(pRawDesc);
     4492            }
     4493            else
     4494                rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
     4495                               N_("VMDK: Image path: '%s'. Failed to allocate %u bytes for the raw drive descriptor"),
     4496                               pImage->pszFilename, sizeof(*pRawDesc));
     4497            RTFileClose(hRawDrive);
     4498        }
     4499    }
     4500    RTStrFree(pszRawDrive);
     4501    RTMemFree(pvBootSector);
     4502    return rc;
     4503}
     4504
     4505/**
    34144506 * Internal: create VMDK images for raw disk/partition access.
    34154507 */
     
    40095101    if (RT_SUCCESS(rc))
    40105102    {
    4011         if (    (uImageFlags & VD_IMAGE_FLAGS_FIXED)
    4012             &&  (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
     5103        if (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
    40135104        {
    40145105            /* Raw disk image (includes raw partition). */
    4015             const PVDISKRAW pRaw = (const PVDISKRAW)pszComment;
    4016             /* As the comment is misused, zap it so that no garbage comment
    4017              * is set below. */
    4018             pszComment = NULL;
     5106            PVDISKRAW pRaw = NULL;
     5107            rc = vmdkMakeRawDescriptor(pImage, &pRaw);
     5108            if (RT_FAILURE(rc))
     5109                return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could get raw descriptor for '%s'"), pImage->pszFilename);
     5110
    40195111            rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
     5112            vmdkRawDescFree(pRaw);
    40205113        }
    40215114        else if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
     
    53626455
    53636456    /* Check size. Maximum 256TB-64K for sparse images, otherwise unlimited. */
    5364     if (    !cbSize
    5365         ||  (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K))
     6457    if (   !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
     6458        && (   !cbSize
     6459            || (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K)))
    53666460        return VERR_VD_INVALID_SIZE;
    53676461
     
    65187612    s_aVmdkFileExtensions,
    65197613    /* paConfigInfo */
    6520     NULL,
     7614    s_aVmdkConfigInfo,
    65217615    /* pfnProbe */
    65227616    vmdkProbe,
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette