Changeset 85902 in vbox for trunk/src/VBox
- Timestamp:
- Aug 27, 2020 1:03:49 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Storage/VMDK.cpp
r85885 r85902 21 21 *********************************************************************************************************************************/ 22 22 #define LOG_GROUP LOG_GROUP_VD_VMDK 23 #include <VBox/log.h> /* before VBox/vd-ifs.h */ 23 24 #include <VBox/vd-plugin.h> 24 25 #include <VBox/err.h> 25 26 26 #include <VBox/log.h>27 27 #include <iprt/assert.h> 28 28 #include <iprt/alloc.h> 29 #include <iprt/base64.h> 30 #include <iprt/ctype.h> 31 #include <iprt/crc.h> 32 #include <iprt/dvm.h> 29 33 #include <iprt/uuid.h> 30 34 #include <iprt/path.h> 35 #include <iprt/rand.h> 31 36 #include <iprt/string.h> 32 #include <iprt/ rand.h>37 #include <iprt/sort.h> 33 38 #include <iprt/zip.h> 34 39 #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 35 47 36 48 #include "VDBackends.h" … … 532 544 }; 533 545 546 /** NULL-terminated array of configuration option. */ 547 static 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 534 559 535 560 /********************************************************************************************************************************* … … 3412 3437 3413 3438 /** 3439 * Frees a raw descriptor. 3440 * @internal 3441 */ 3442 static 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 */ 3472 static 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 */ 3496 static 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 */ 3508 static 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 */ 3551 static 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 */ 3768 static 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 */ 3844 static 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 */ 3996 static 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 */ 4108 static 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 */ 4151 static 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 */ 4220 static 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 */ 4380 static 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 /** 3414 4506 * Internal: create VMDK images for raw disk/partition access. 3415 4507 */ … … 4009 5101 if (RT_SUCCESS(rc)) 4010 5102 { 4011 if ( (uImageFlags & VD_IMAGE_FLAGS_FIXED) 4012 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)) 5103 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK) 4013 5104 { 4014 5105 /* 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 4019 5111 rc = vmdkCreateRawImage(pImage, pRaw, cbSize); 5112 vmdkRawDescFree(pRaw); 4020 5113 } 4021 5114 else if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) … … 5362 6455 5363 6456 /* 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))) 5366 6460 return VERR_VD_INVALID_SIZE; 5367 6461 … … 6518 7612 s_aVmdkFileExtensions, 6519 7613 /* paConfigInfo */ 6520 NULL,7614 s_aVmdkConfigInfo, 6521 7615 /* pfnProbe */ 6522 7616 vmdkProbe,
Note:
See TracChangeset
for help on using the changeset viewer.