drive.c (rufus-3.12) | : | drive.c (rufus-3.13) | ||
---|---|---|---|---|
skipping to change at line 76 | skipping to change at line 76 | |||
#endif | #endif | |||
PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_ BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS)); | PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_ BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS)); | |||
/* | /* | |||
* Globals | * Globals | |||
*/ | */ | |||
RUFUS_DRIVE_INFO SelectedDrive; | RUFUS_DRIVE_INFO SelectedDrive; | |||
extern BOOL installed_uefi_ntfs, write_as_esp; | extern BOOL installed_uefi_ntfs, write_as_esp; | |||
extern int nWindowsVersion, nWindowsBuildNumber; | extern int nWindowsVersion, nWindowsBuildNumber; | |||
uint64_t partition_offset[3]; | uint64_t partition_offset[PI_MAX]; | |||
uint64_t persistence_size = 0; | uint64_t persistence_size = 0; | |||
/* | /* | |||
* The following methods get or set the AutoMount setting (which is different fr om AutoRun) | * The following methods get or set the AutoMount setting (which is different fr om AutoRun) | |||
* Rufus needs AutoMount to be set as the format process may fail for fixed driv es otherwise. | * Rufus needs AutoMount to be set as the format process may fail for fixed driv es otherwise. | |||
* See https://github.com/pbatard/rufus/issues/386. | * See https://github.com/pbatard/rufus/issues/386. | |||
* | * | |||
* Reverse engineering diskpart and mountvol indicates that the former uses the IVdsService | * Reverse engineering diskpart and mountvol indicates that the former uses the IVdsService | |||
* ClearFlags()/SetFlags() to set VDS_SVF_AUTO_MOUNT_OFF whereas mountvol on use s | * ClearFlags()/SetFlags() to set VDS_SVF_AUTO_MOUNT_OFF whereas mountvol on use s | |||
* IOCTL_MOUNTMGR_SET_AUTO_MOUNT on "\\\\.\\MountPointManager". | * IOCTL_MOUNTMGR_SET_AUTO_MOUNT on "\\\\.\\MountPointManager". | |||
* As the latter is MUCH simpler this is what we'll use too | * As the latter is MUCH simpler this is what we'll use too | |||
*/ | */ | |||
BOOL SetAutoMount(BOOL enable) | BOOL SetAutoMount(BOOL enable) | |||
{ | { | |||
HANDLE hMountMgr; | HANDLE hMountMgr; | |||
DWORD size; | DWORD size; | |||
BOOL ret = FALSE; | BOOL ret = FALSE; | |||
hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WR | hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE | |||
ITE, | _SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |||
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRI | if (hMountMgr == INVALID_HANDLE_VALUE) | |||
BUTE_NORMAL, NULL); | ||||
if (hMountMgr == NULL) | ||||
return FALSE; | return FALSE; | |||
ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_SET_AUTO_MOUNT, &enable, sizeof(enable), NULL, 0, &size, NULL); | ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_SET_AUTO_MOUNT, &enable, sizeof(enable), NULL, 0, &size, NULL); | |||
CloseHandle(hMountMgr); | CloseHandle(hMountMgr); | |||
return ret; | return ret; | |||
} | } | |||
BOOL GetAutoMount(BOOL* enabled) | BOOL GetAutoMount(BOOL* enabled) | |||
{ | { | |||
HANDLE hMountMgr; | HANDLE hMountMgr; | |||
DWORD size; | DWORD size; | |||
BOOL ret = FALSE; | BOOL ret = FALSE; | |||
if (enabled == NULL) | if (enabled == NULL) | |||
return FALSE; | return FALSE; | |||
hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WR | hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE | |||
ITE, | _SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |||
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRI | if (hMountMgr == INVALID_HANDLE_VALUE) | |||
BUTE_NORMAL, NULL); | ||||
if (hMountMgr == NULL) | ||||
return FALSE; | return FALSE; | |||
ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT, NULL, 0 , enabled, sizeof(*enabled), &size, NULL); | ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT, NULL, 0 , enabled, sizeof(*enabled), &size, NULL); | |||
CloseHandle(hMountMgr); | CloseHandle(hMountMgr); | |||
return ret; | return ret; | |||
} | } | |||
/* | /* | |||
* Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as | * Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as | |||
* index would return a handle to C:, which we might then proceed to unknowingly | * index would return a handle to C:, which we might then proceed to unknowingly | |||
* clear the MBR of!), so we mitigate the risk by forcing our indexes to belong to | * clear the MBR of!), so we mitigate the risk by forcing our indexes to belong to | |||
skipping to change at line 169 | skipping to change at line 167 | |||
// We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is | // We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is | |||
// required for enumeration. | // required for enumeration. | |||
hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRI TE:0), | hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRI TE:0), | |||
FILE_SHARE_READ|(bWriteShare?FILE_SHARE_WRITE:0), | FILE_SHARE_READ|(bWriteShare?FILE_SHARE_WRITE:0), | |||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |||
if (hDrive != INVALID_HANDLE_VALUE) | if (hDrive != INVALID_HANDLE_VALUE) | |||
break; | break; | |||
if ((GetLastError() != ERROR_SHARING_VIOLATION) && (GetLastError( ) != ERROR_ACCESS_DENIED)) | if ((GetLastError() != ERROR_SHARING_VIOLATION) && (GetLastError( ) != ERROR_ACCESS_DENIED)) | |||
break; | break; | |||
if (i == 0) { | if (i == 0) { | |||
uprintf("Waiting for access on %s [%s]...", Path, DevPath | uprintf("Notice: Volume Device Path is %s", DevPath); | |||
); | uprintf("Waiting for access on %s...", Path); | |||
} else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES/3)) { | } else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES/3)) { | |||
// If we can't seem to get a hold of the drive for some t ime, try to enable FILE_SHARE_WRITE... | // If we can't seem to get a hold of the drive for some t ime, try to enable FILE_SHARE_WRITE... | |||
uprintf("Warning: Could not obtain exclusive rights. Retr ying with write sharing enabled..."); | uprintf("Warning: Could not obtain exclusive rights. Retr ying with write sharing enabled..."); | |||
bWriteShare = TRUE; | bWriteShare = TRUE; | |||
// Try to report the process that is locking the drive | // Try to report the process that is locking the drive | |||
// We also use bit 6 as a flag to indicate that SearchPro cess was called. | // We also use bit 6 as a flag to indicate that SearchPro cess was called. | |||
access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEO UT, TRUE, TRUE, FALSE) | 0x40; | access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEO UT, TRUE, TRUE, FALSE) | 0x40; | |||
} | } | |||
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); | Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); | |||
} | } | |||
skipping to change at line 441 | skipping to change at line 440 | |||
for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.Par titionOffset[i]); i++); | for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.Par titionOffset[i]); i++); | |||
if (i >= MAX_PARTITIONS) | if (i >= MAX_PARTITIONS) | |||
goto out; | goto out; | |||
static_sprintf(volume_name, "\\\\.\\PhysicalDrive%lu %I64u %I64u", DriveI ndex, | static_sprintf(volume_name, "\\\\.\\PhysicalDrive%lu %I64u %I64u", DriveI ndex, | |||
SelectedDrive.PartitionOffset[i], SelectedDrive.PartitionSize[i]) ; | SelectedDrive.PartitionOffset[i], SelectedDrive.PartitionSize[i]) ; | |||
ret = safe_strdup(volume_name); | ret = safe_strdup(volume_name); | |||
out: | out: | |||
return ret; | return ret; | |||
} | } | |||
static const char* VdsErrorString(HRESULT hr) { | ||||
SetLastError(hr); | ||||
return WindowsErrorString(); | ||||
} | ||||
/* | /* | |||
* Call on VDS to refresh the drive layout | * Call on VDS to refresh the drive layout | |||
*/ | */ | |||
BOOL RefreshLayout(DWORD DriveIndex) | BOOL RefreshLayout(DWORD DriveIndex) | |||
{ | { | |||
BOOL r = FALSE; | HRESULT hr = S_FALSE; | |||
HRESULT hr; | ||||
wchar_t wPhysicalName[24]; | wchar_t wPhysicalName[24]; | |||
IVdsServiceLoader *pLoader; | IVdsServiceLoader* pLoader = NULL; | |||
IVdsService *pService; | IVdsService* pService = NULL; | |||
IEnumVdsObject *pEnum; | IEnumVdsObject *pEnum; | |||
CheckDriveIndex(DriveIndex); | CheckDriveIndex(DriveIndex); | |||
wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive %lu", DriveIndex); | wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive %lu", DriveIndex); | |||
// Initialize COM | // Initialize COM | |||
IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | |||
IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVE L_CONNECT, | IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVE L_CONNECT, | |||
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); | RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); | |||
// Create a VDS Loader Instance | // Create a VDS Loader Instance | |||
hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCT X_REMOTE_SERVER, | hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCT X_REMOTE_SERVER, | |||
&IID_IVdsServiceLoader, (void **)&pLoader); | &IID_IVdsServiceLoader, (void **)&pLoader); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not create VDS Loader Instance: %s", VdsErrorStrin | |||
uprintf("Could not create VDS Loader Instance: %s", WindowsErrorS | g(hr)); | |||
tring()); | ||||
goto out; | goto out; | |||
} | } | |||
// Load the VDS Service | // Load the VDS Service | |||
hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); | hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); | |||
IVdsServiceLoader_Release(pLoader); | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not load VDS Service: %s", VdsErrorString(hr)); | |||
uprintf("Could not load VDS Service: %s", WindowsErrorString()); | ||||
goto out; | goto out; | |||
} | } | |||
// Wait for the Service to become ready if needed | // Wait for the Service to become ready if needed | |||
hr = IVdsService_WaitForServiceReady(pService); | hr = IVdsService_WaitForServiceReady(pService); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("VDS Service is not ready: %s", VdsErrorString(hr)); | |||
uprintf("VDS Service is not ready: %s", WindowsErrorString()); | ||||
goto out; | goto out; | |||
} | } | |||
// Query the VDS Service Providers | // Query the VDS Service Providers | |||
hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, & pEnum); | hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, & pEnum); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not query VDS Service Providers: %s", VdsErrorStri | |||
uprintf("Could not query VDS Service Providers: %s", WindowsError | ng(hr)); | |||
String()); | ||||
goto out; | goto out; | |||
} | } | |||
// Remove mountpoints | // Remove mountpoints | |||
hr = IVdsService_CleanupObsoleteMountPoints(pService); | hr = IVdsService_CleanupObsoleteMountPoints(pService); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not clean up VDS mountpoints: %s", VdsErrorString( | |||
uprintf("Could not clean up VDS mountpoints: %s", WindowsErrorStr | hr)); | |||
ing()); | ||||
goto out; | goto out; | |||
} | } | |||
// Invoke layout refresh | // Invoke layout refresh | |||
hr = IVdsService_Refresh(pService); | hr = IVdsService_Refresh(pService); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not refresh VDS layout: %s", VdsErrorString(hr)); | |||
uprintf("Could not refresh VDS layout: %s", WindowsErrorString()) | ||||
; | ||||
goto out; | goto out; | |||
} | } | |||
// Force re-enum | // Force re-enum | |||
hr = IVdsService_Reenumerate(pService); | hr = IVdsService_Reenumerate(pService); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | uprintf("Could not refresh VDS layout: %s", VdsErrorString(hr)); | |||
uprintf("Could not refresh VDS layout: %s", WindowsErrorString()) | ||||
; | ||||
goto out; | goto out; | |||
} | } | |||
r = TRUE; | ||||
out: | out: | |||
return r; | if (pService != NULL) | |||
IVdsService_Release(pService); | ||||
if (pLoader != NULL) | ||||
IVdsServiceLoader_Release(pLoader); | ||||
VDS_SET_ERROR(hr); | ||||
return (hr == S_OK); | ||||
} | } | |||
/* | /* | |||
* Delete all the partitions from a disk, using VDS | * Generic call to instantiate a VDS Disk Interface. Mostly copied from: | |||
* Mostly copied from https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90 | * https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-873 | |||
482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-erro | 1-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom | |||
r-enointerface-dcom?forum=vcgeneral | ?forum=vcgeneral | |||
* See also: https://docs.microsoft.com/en-us/windows/win32/vds/working-with-enu | ||||
meration-objects | ||||
*/ | */ | |||
BOOL DeletePartitions(DWORD DriveIndex) | static BOOL GetVdsDiskInterface(DWORD DriveIndex, const IID* InterfaceIID, void* * pInterfaceInstance, BOOL bSilent) | |||
{ | { | |||
BOOL r = FALSE, bNeverFound = TRUE; | HRESULT hr = S_FALSE; | |||
HRESULT hr; | ||||
ULONG ulFetched; | ULONG ulFetched; | |||
wchar_t wPhysicalName[24]; | wchar_t wPhysicalName[24]; | |||
IVdsServiceLoader *pLoader; | IVdsServiceLoader* pLoader; | |||
IVdsService *pService; | IVdsService* pService; | |||
IEnumVdsObject *pEnum; | IEnumVdsObject* pEnum; | |||
IUnknown *pUnk; | IUnknown* pUnk; | |||
*pInterfaceInstance = NULL; | ||||
CheckDriveIndex(DriveIndex); | CheckDriveIndex(DriveIndex); | |||
wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive %lu", DriveIndex); | wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive %lu", DriveIndex); | |||
// Initialize COM | // Initialize COM | |||
IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | |||
IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVE L_CONNECT, | IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVE L_CONNECT, | |||
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); | RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); | |||
// Create a VDS Loader Instance | // Create a VDS Loader Instance | |||
hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCT X_REMOTE_SERVER, | hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCT X_REMOTE_SERVER, | |||
&IID_IVdsServiceLoader, (void **)&pLoader); | &IID_IVdsServiceLoader, (void**)&pLoader); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not create VDS Loader Instance: %s", VdsErrorStri | |||
uprintf("Could not create VDS Loader Instance: %s", WindowsErrorS | ng(hr)); | |||
tring()); | ||||
goto out; | goto out; | |||
} | } | |||
// Load the VDS Service | // Load the VDS Service | |||
hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); | hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); | |||
IVdsServiceLoader_Release(pLoader); | IVdsServiceLoader_Release(pLoader); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not load VDS Service: %s", VdsErrorString(hr)); | |||
uprintf("Could not load VDS Service: %s", WindowsErrorString()); | ||||
goto out; | goto out; | |||
} | } | |||
// Wait for the Service to become ready if needed | // Wait for the Service to become ready if needed | |||
hr = IVdsService_WaitForServiceReady(pService); | hr = IVdsService_WaitForServiceReady(pService); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("VDS Service is not ready: %s", VdsErrorString(hr)); | |||
uprintf("VDS Service is not ready: %s", WindowsErrorString()); | ||||
goto out; | goto out; | |||
} | } | |||
// Query the VDS Service Providers | // Query the VDS Service Providers | |||
hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, & pEnum); | hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, & pEnum); | |||
IVdsService_Release(pService); | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS Service Providers: %s", VdsErrorStr | |||
uprintf("Could not query VDS Service Providers: %s", WindowsError | ing(hr)); | |||
String()); | ||||
goto out; | goto out; | |||
} | } | |||
while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { | while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { | |||
IVdsProvider *pProvider; | IVdsProvider* pProvider; | |||
IVdsSwProvider *pSwProvider; | IVdsSwProvider* pSwProvider; | |||
IEnumVdsObject *pEnumPack; | IEnumVdsObject* pEnumPack; | |||
IUnknown *pPackUnk; | IUnknown* pPackUnk; | |||
// Get VDS Provider | // Get VDS Provider | |||
hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&p Provider); | hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void**)&pP rovider); | |||
IUnknown_Release(pUnk); | IUnknown_Release(pUnk); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not get VDS Provider: %s", VdsErrorString | |||
uprintf("Could not get VDS Provider: %s", WindowsErrorStr | (hr)); | |||
ing()); | break; | |||
goto out; | ||||
} | } | |||
// Get VDS Software Provider | // Get VDS Software Provider | |||
hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider , (void **)&pSwProvider); | hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider , (void**)&pSwProvider); | |||
IVdsProvider_Release(pProvider); | IVdsProvider_Release(pProvider); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not get VDS Software Provider: %s", VdsEr | |||
uprintf("Could not get VDS Software Provider: %s", Window | rorString(hr)); | |||
sErrorString()); | break; | |||
goto out; | ||||
} | } | |||
// Get VDS Software Provider Packs | // Get VDS Software Provider Packs | |||
hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); | hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); | |||
IVdsSwProvider_Release(pSwProvider); | IVdsSwProvider_Release(pSwProvider); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not get VDS Software Provider Packs: %s", | |||
uprintf("Could not get VDS Software Provider Packs: %s", | VdsErrorString(hr)); | |||
WindowsErrorString()); | break; | |||
goto out; | ||||
} | } | |||
// Enumerate Provider Packs | // Enumerate Provider Packs | |||
while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) = = S_OK) { | while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) = = S_OK) { | |||
IVdsPack *pPack; | IVdsPack* pPack; | |||
IEnumVdsObject *pEnumDisk; | IEnumVdsObject* pEnumDisk; | |||
IUnknown *pDiskUnk; | IUnknown* pDiskUnk; | |||
hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (vo id **)&pPack); | hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (vo id**)&pPack); | |||
IUnknown_Release(pPackUnk); | IUnknown_Release(pPackUnk); | |||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS Software Provider P | |||
uprintf("Could not query VDS Software Provider Pa | ack: %s", VdsErrorString(hr)); | |||
ck: %s", WindowsErrorString()); | break; | |||
goto out; | ||||
} | } | |||
// Use the pack interface to access the disks | // Use the pack interface to access the disks | |||
hr = IVdsPack_QueryDisks(pPack, &pEnumDisk); | hr = IVdsPack_QueryDisks(pPack, &pEnumDisk); | |||
IVdsPack_Release(pPack); | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS disks: %s", VdsErro | |||
uprintf("Could not query VDS disks: %s", WindowsE | rString(hr)); | |||
rrorString()); | break; | |||
goto out; | ||||
} | } | |||
// List disks | // List disks | |||
while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFe tched) == S_OK) { | while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFe tched) == S_OK) { | |||
VDS_DISK_PROP diskprop; | VDS_DISK_PROP prop; | |||
VDS_PARTITION_PROP* prop_array; | IVdsDisk* pDisk; | |||
LONG i, prop_array_size; | ||||
IVdsDisk *pDisk; | ||||
IVdsAdvancedDisk *pAdvancedDisk; | ||||
// Get the disk interface. | // Get the disk interface. | |||
hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsD | hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsD | |||
isk, (void **)&pDisk); | isk, (void**)&pDisk); | |||
IUnknown_Release(pDiskUnk); | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS Disk Interf | |||
uprintf("Could not query VDS Disk Interfa | ace: %s", VdsErrorString(hr)); | |||
ce: %s", WindowsErrorString()); | break; | |||
goto out; | ||||
} | } | |||
// Get the disk properties | // Get the disk properties | |||
hr = IVdsDisk_GetProperties(pDisk, &diskprop); | hr = IVdsDisk_GetProperties(pDisk, &prop); | |||
if (hr != S_OK) { | if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOM | |||
VDS_SET_ERROR(hr); | PLETE)) { | |||
uprintf("Could not query VDS Disk Propert | IVdsDisk_Release(pDisk); | |||
ies: %s", WindowsErrorString()); | suprintf("Could not query VDS Disk Proper | |||
goto out; | ties: %s", VdsErrorString(hr)); | |||
break; | ||||
} | } | |||
// Isolate the disk we want | // Check if we are on the target disk | |||
if (_wcsicmp(wPhysicalName, diskprop.pwszName) != | hr = (HRESULT)_wcsicmp(wPhysicalName, prop.pwszNa | |||
0) { | me); | |||
IVdsDisk_Release(pDisk); | CoTaskMemFree(prop.pwszName); | |||
if (hr != S_OK) { | ||||
hr = S_OK; | ||||
continue; | continue; | |||
} | } | |||
bNeverFound = FALSE; | ||||
// Instantiate the AdvanceDisk interface for our | // Instantiate the requested VDS disk interface | |||
disk. | hr = IVdsDisk_QueryInterface(pDisk, InterfaceIID, | |||
hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdva | pInterfaceInstance); | |||
ncedDisk, (void **)&pAdvancedDisk); | ||||
IVdsDisk_Release(pDisk); | IVdsDisk_Release(pDisk); | |||
if (hr != S_OK) | ||||
suprintf("Could not access the requested | ||||
Disk interface: %s", VdsErrorString(hr)); | ||||
// With the interface found, we should be able to | ||||
return | ||||
break; | ||||
} | ||||
IEnumVdsObject_Release(pEnumDisk); | ||||
} | ||||
IEnumVdsObject_Release(pEnumPack); | ||||
} | ||||
IEnumVdsObject_Release(pEnum); | ||||
out: | ||||
VDS_SET_ERROR(hr); | ||||
return (hr == S_OK); | ||||
} | ||||
/* | ||||
* Delete one partition at offset PartitionOffset, or all partitions if the offs | ||||
et is 0. | ||||
*/ | ||||
BOOL DeletePartition(DWORD DriveIndex, ULONGLONG PartitionOffset, BOOL bSilent) | ||||
{ | ||||
HRESULT hr = S_FALSE; | ||||
VDS_PARTITION_PROP* prop_array; | ||||
LONG i, prop_array_size; | ||||
IVdsAdvancedDisk *pAdvancedDisk; | ||||
if (!GetVdsDiskInterface(DriveIndex, &IID_IVdsAdvancedDisk, (void**)&pAdv | ||||
ancedDisk, bSilent)) | ||||
return FALSE; | ||||
if (pAdvancedDisk == NULL) { | ||||
suprintf("No partition to delete on disk"); | ||||
return TRUE; | ||||
} | ||||
// Query the partition data, so we can get the start offset, which we nee | ||||
d for deletion | ||||
hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_a | ||||
rray_size); | ||||
if (hr == S_OK) { | ||||
suprintf("Deleting partition%s:", (PartitionOffset == 0) ? "s" : | ||||
""); | ||||
// Now go through each partition | ||||
for (i = 0; i < prop_array_size; i++) { | ||||
if ((PartitionOffset != 0) && (prop_array[i].ullOffset != | ||||
PartitionOffset)) | ||||
continue; | ||||
suprintf("● Partition %d (offset: %lld, size: %s)", prop_ | ||||
array[i].ulPartitionNumber, | ||||
prop_array[i].ullOffset, SizeToHumanReadable(prop | ||||
_array[i].ullSize, FALSE, FALSE)); | ||||
hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop | ||||
_array[i].ullOffset, TRUE, TRUE); | ||||
if (hr != S_OK) | ||||
suprintf("Could not delete partition: %s", VdsErr | ||||
orString(hr)); | ||||
} | ||||
} else { | ||||
suprintf("No partition to delete on disk"); | ||||
hr = S_OK; | ||||
} | ||||
CoTaskMemFree(prop_array); | ||||
IVdsAdvancedDisk_Release(pAdvancedDisk); | ||||
VDS_SET_ERROR(hr); | ||||
return (hr == S_OK); | ||||
} | ||||
/* | ||||
* Count on Microsoft for *COMPLETELY CRIPPLING* an API when alledgedly upgradin | ||||
g it... | ||||
* As illustrated when you do so with diskpart (which uses VDS behind the scenes | ||||
), VDS | ||||
* simply *DOES NOT* list all the volumes that the system can see, especially co | ||||
mpared | ||||
* to what mountvol (which uses FindFirstVolume()/FindNextVolume()) and other AP | ||||
Is do. | ||||
* Also for reference, if you want to list volumes through WMI in PowerShell: | ||||
* Get-WmiObject win32_volume | Format-Table -Property DeviceID,Name,Label,Capac | ||||
ity | ||||
*/ | ||||
BOOL ListVdsVolumes(BOOL bSilent) | ||||
{ | ||||
HRESULT hr = S_FALSE; | ||||
ULONG ulFetched; | ||||
IVdsServiceLoader* pLoader; | ||||
IVdsService* pService; | ||||
IEnumVdsObject* pEnum; | ||||
IUnknown* pUnk; | ||||
// Initialize COM | ||||
IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | ||||
IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVE | ||||
L_CONNECT, | ||||
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); | ||||
// Create a VDS Loader Instance | ||||
hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCT | ||||
X_REMOTE_SERVER, | ||||
&IID_IVdsServiceLoader, (void**)&pLoader); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not create VDS Loader Instance: %s", VdsErrorStri | ||||
ng(hr)); | ||||
goto out; | ||||
} | ||||
// Load the VDS Service | ||||
hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); | ||||
IVdsServiceLoader_Release(pLoader); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not load VDS Service: %s", VdsErrorString(hr)); | ||||
goto out; | ||||
} | ||||
// Wait for the Service to become ready if needed | ||||
hr = IVdsService_WaitForServiceReady(pService); | ||||
if (hr != S_OK) { | ||||
suprintf("VDS Service is not ready: %s", VdsErrorString(hr)); | ||||
goto out; | ||||
} | ||||
// Query the VDS Service Providers | ||||
hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, & | ||||
pEnum); | ||||
IVdsService_Release(pService); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not query VDS Service Providers: %s", VdsErrorStr | ||||
ing(hr)); | ||||
goto out; | ||||
} | ||||
while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { | ||||
IVdsProvider* pProvider; | ||||
IVdsSwProvider* pSwProvider; | ||||
IEnumVdsObject* pEnumPack; | ||||
IUnknown* pPackUnk; | ||||
// Get VDS Provider | ||||
hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void**)&pP | ||||
rovider); | ||||
IUnknown_Release(pUnk); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not get VDS Provider: %s", VdsErrorString | ||||
(hr)); | ||||
break; | ||||
} | ||||
// Get VDS Software Provider | ||||
hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider | ||||
, (void**)&pSwProvider); | ||||
IVdsProvider_Release(pProvider); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not get VDS Software Provider: %s", VdsEr | ||||
rorString(hr)); | ||||
break; | ||||
} | ||||
// Get VDS Software Provider Packs | ||||
hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); | ||||
IVdsSwProvider_Release(pSwProvider); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not get VDS Software Provider Packs: %s", | ||||
VdsErrorString(hr)); | ||||
break; | ||||
} | ||||
// Enumerate Provider Packs | ||||
while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) = | ||||
= S_OK) { | ||||
IVdsPack* pPack; | ||||
IEnumVdsObject* pEnumVolume; | ||||
IUnknown* pVolumeUnk; | ||||
hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (vo | ||||
id**)&pPack); | ||||
IUnknown_Release(pPackUnk); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not query VDS Software Provider P | ||||
ack: %s", VdsErrorString(hr)); | ||||
break; | ||||
} | ||||
// Use the pack interface to access the disks | ||||
hr = IVdsPack_QueryVolumes(pPack, &pEnumVolume); | ||||
if (hr != S_OK) { | ||||
suprintf("Could not query VDS volumes: %s", VdsEr | ||||
rorString(hr)); | ||||
break; | ||||
} | ||||
// List volumes | ||||
while (IEnumVdsObject_Next(pEnumVolume, 1, &pVolumeUnk, & | ||||
ulFetched) == S_OK) { | ||||
IVdsVolume* pVolume; | ||||
IVdsVolumeMF3* pVolumeMF3; | ||||
VDS_VOLUME_PROP prop; | ||||
LPWSTR* wszPathArray; | ||||
ULONG i, ulNumberOfPaths; | ||||
// Get the volume interface. | ||||
hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVd | ||||
sVolume, (void**)&pVolume); | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS Volume Inte | |||
uprintf("Could not access VDS Advanced Di | rface: %s", VdsErrorString(hr)); | |||
sk interface: %s", WindowsErrorString()); | break; | |||
goto out; | ||||
} | } | |||
// Query the partition data, so we can get the st | // Get the volume properties | |||
art offset, which we need for deletion | hr = IVdsVolume_GetProperties(pVolume, &prop); | |||
hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDi | if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOM | |||
sk, &prop_array, &prop_array_size); | PLETE)) { | |||
if (hr == S_OK) { | suprintf("Could not query VDS Volume Prop | |||
uprintf("Deleting ALL partition(s) from d | erties: %s", VdsErrorString(hr)); | |||
isk '%S':", diskprop.pwszName); | break; | |||
// Now go through each partition | ||||
for (i = 0; i < prop_array_size; i++) { | ||||
uprintf("● Partition %d (offset: | ||||
%lld, size: %s)", prop_array[i].ulPartitionNumber, | ||||
prop_array[i].ullOffset, | ||||
SizeToHumanReadable(prop_array[i].ullSize, FALSE, FALSE)); | ||||
hr = IVdsAdvancedDisk_DeleteParti | ||||
tion(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE); | ||||
if (hr != S_OK) { | ||||
r = FALSE; | ||||
VDS_SET_ERROR(hr); | ||||
uprintf("Could not delete | ||||
partitions: %s", WindowsErrorString()); | ||||
} | ||||
} | ||||
r = TRUE; | ||||
} else { | ||||
uprintf("No partition to delete on disk ' | ||||
%S'", diskprop.pwszName); | ||||
r = TRUE; | ||||
} | } | |||
CoTaskMemFree(prop_array); | ||||
#if 0 | uprintf("FOUND VOLUME: '%S'", prop.pwszName); | |||
// Issue a Clean while we're at it | CoTaskMemFree(prop.pwszName); | |||
HRESULT hr2 = E_FAIL; | IVdsVolume_Release(pVolume); | |||
ULONG completed; | ||||
IVdsAsync* pAsync; | // Get the volume MF3 interface. | |||
hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, | hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVd | |||
FALSE, FALSE, &pAsync); | sVolumeMF3, (void**)&pVolumeMF3); | |||
while (SUCCEEDED(hr)) { | ||||
if (IS_ERROR(FormatStatus)) { | ||||
IVdsAsync_Cancel(pAsync); | ||||
break; | ||||
} | ||||
hr = IVdsAsync_QueryStatus(pAsync, &hr2, | ||||
&completed); | ||||
if (SUCCEEDED(hr)) { | ||||
hr = hr2; | ||||
if (hr == S_OK) | ||||
break; | ||||
if (hr == VDS_E_OPERATION_PENDING | ||||
) | ||||
hr = S_OK; | ||||
} | ||||
Sleep(500); | ||||
} | ||||
if (hr != S_OK) { | if (hr != S_OK) { | |||
VDS_SET_ERROR(hr); | suprintf("Could not query VDS VolumeMF3 I | |||
uprintf("Could not clean disk: %s", Windo | nterface: %s", VdsErrorString(hr)); | |||
wsErrorString()); | break; | |||
} | } | |||
#endif | ||||
IVdsAdvancedDisk_Release(pAdvancedDisk); | // Get the volume properties | |||
goto out; | hr = IVdsVolumeMF3_QueryVolumeGuidPathnames(pVolu | |||
meMF3, &wszPathArray, &ulNumberOfPaths); | ||||
if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOM | ||||
PLETE)) { | ||||
suprintf("Could not query VDS VolumeMF3 G | ||||
UID PathNames: %s", VdsErrorString(hr)); | ||||
break; | ||||
} | ||||
hr = S_OK; | ||||
for (i = 0; i < ulNumberOfPaths; i++) | ||||
uprintf(" VOL GUID: '%S'", wszPathArray[ | ||||
i]); | ||||
CoTaskMemFree(wszPathArray); | ||||
IVdsVolume_Release(pVolumeMF3); | ||||
IUnknown_Release(pVolumeUnk); | ||||
} | } | |||
IEnumVdsObject_Release(pEnumVolume); | ||||
} | } | |||
IEnumVdsObject_Release(pEnumPack); | ||||
} | } | |||
IEnumVdsObject_Release(pEnum); | ||||
out: | out: | |||
if (bNeverFound) | VDS_SET_ERROR(hr); | |||
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERR | return (hr == S_OK); | |||
OR_PATH_NOT_FOUND; | ||||
return r; | ||||
} | } | |||
/* Wait for a logical drive to reappear - Used when a drive has just been repart itioned */ | /* Wait for a logical drive to reappear - Used when a drive has just been repart itioned */ | |||
BOOL WaitForLogical(DWORD DriveIndex, uint64_t PartitionOffset) | BOOL WaitForLogical(DWORD DriveIndex, uint64_t PartitionOffset) | |||
{ | { | |||
uint64_t EndTime; | uint64_t EndTime; | |||
char* LogicalPath = NULL; | char* LogicalPath = NULL; | |||
// GetLogicalName() calls may be slow, so use the system time to | // GetLogicalName() calls may be slow, so use the system time to | |||
// make sure we don't spend more than DRIVE_ACCESS_TIMEOUT in wait. | // make sure we don't spend more than DRIVE_ACCESS_TIMEOUT in wait. | |||
skipping to change at line 937 | skipping to change at line 1081 | |||
} | } | |||
// There's already a GetDriveType in the Windows API | // There's already a GetDriveType in the Windows API | |||
UINT GetDriveTypeFromIndex(DWORD DriveIndex) | UINT GetDriveTypeFromIndex(DWORD DriveIndex) | |||
{ | { | |||
UINT drive_type; | UINT drive_type; | |||
_GetDriveLettersAndType(DriveIndex, NULL, &drive_type); | _GetDriveLettersAndType(DriveIndex, NULL, &drive_type); | |||
return drive_type; | return drive_type; | |||
} | } | |||
// Removes all drive letters associated with the specific drive, and return | ||||
// either the first or last letter that was removed, according to bReturnLast. | ||||
char RemoveDriveLetters(DWORD DriveIndex, BOOL bReturnLast, BOOL bSilent) | ||||
{ | ||||
int i, len; | ||||
char drive_letters[27] = { 0 }, drive_name[4] = "#:\\"; | ||||
if (!GetDriveLetters(DriveIndex, drive_letters)) { | ||||
suprintf("Failed to get a drive letter"); | ||||
return 0; | ||||
} | ||||
if (drive_letters[0] == 0) { | ||||
suprintf("No drive letter was assigned..."); | ||||
return GetUnusedDriveLetter(); | ||||
} | ||||
len = (int)strlen(drive_letters); | ||||
if (len == 0) | ||||
return 0; | ||||
// Unmount all mounted volumes that belong to this drive | ||||
for (i = 0; i < len; i++) { | ||||
// Check that the current image isn't located on a drive we are t | ||||
rying to dismount | ||||
if ((boot_type == BT_IMAGE) && (drive_letters[i] == (PathGetDrive | ||||
NumberU(image_path) + 'A'))) { | ||||
if ((PathGetDriveNumberU(image_path) + 'A') == drive_lett | ||||
ers[i]) { | ||||
suprintf("ABORTED: Cannot use an image that is lo | ||||
cated on the target drive!"); | ||||
return 0; | ||||
} | ||||
} | ||||
drive_name[0] = drive_letters[i]; | ||||
// DefineDosDevice() cannot have a trailing backslash... | ||||
drive_name[2] = 0; | ||||
DefineDosDeviceA(DDD_REMOVE_DEFINITION, drive_name, NULL); | ||||
// ... but DeleteVolumeMountPoint() requires one. Go figure... | ||||
drive_name[2] = '\\'; | ||||
if (!DeleteVolumeMountPointA(drive_name)) | ||||
suprintf("Failed to delete mountpoint %s: %s", drive_name | ||||
, WindowsErrorString()); | ||||
} | ||||
return drive_letters[bReturnLast ? (len - 1) : 0]; | ||||
} | ||||
/* | /* | |||
* Return the next unused drive letter from the system or NUL on error. | * Return the next unused drive letter from the system or NUL on error. | |||
*/ | */ | |||
char GetUnusedDriveLetter(void) | char GetUnusedDriveLetter(void) | |||
{ | { | |||
DWORD size; | DWORD size; | |||
char drive_letter, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., pl us one NUL */ | char drive_letter, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., pl us one NUL */ | |||
size = GetLogicalDriveStringsA(sizeof(drives), drives); | size = GetLogicalDriveStringsA(sizeof(drives), drives); | |||
if (size == 0) { | if (size == 0) { | |||
skipping to change at line 1049 | skipping to change at line 1233 | |||
*label = VolumeLabel; | *label = VolumeLabel; | |||
} else if (GetVolumeInformationU(DrivePath, VolumeLabel, ARRAYSIZE(Volume Label), | } else if (GetVolumeInformationU(DrivePath, VolumeLabel, ARRAYSIZE(Volume Label), | |||
NULL, NULL, NULL, NULL, 0) && (VolumeLabel[0] != 0)) { | NULL, NULL, NULL, NULL, 0) && (VolumeLabel[0] != 0)) { | |||
*label = VolumeLabel; | *label = VolumeLabel; | |||
} else { | } else { | |||
// Might be an extfs label | // Might be an extfs label | |||
error = GetLastError(); | error = GetLastError(); | |||
*label = (char*)GetExtFsLabel(DriveIndex, 0); | *label = (char*)GetExtFsLabel(DriveIndex, 0); | |||
if (*label == NULL) { | if (*label == NULL) { | |||
SetLastError(error); | SetLastError(error); | |||
duprintf("Failed to read label: %s", WindowsErrorString() | if (error != ERROR_UNRECOGNIZED_VOLUME) | |||
); | duprintf("Failed to read label: %s", WindowsError | |||
String()); | ||||
*label = STR_NO_LABEL; | *label = STR_NO_LABEL; | |||
} | } | |||
} | } | |||
return TRUE; | return TRUE; | |||
} | } | |||
/* | /* | |||
* Return the drive size | * Return the drive size | |||
*/ | */ | |||
uint64_t GetDriveSize(DWORD DriveIndex) | uint64_t GetDriveSize(DWORD DriveIndex) | |||
skipping to change at line 1115 | skipping to change at line 1300 | |||
{ is_reactos_mbr, "ReactOS" }, | { is_reactos_mbr, "ReactOS" }, | |||
{ is_kolibrios_mbr, "KolibriOS" }, | { is_kolibrios_mbr, "KolibriOS" }, | |||
{ is_grub4dos_mbr, "Grub4DOS" }, | { is_grub4dos_mbr, "Grub4DOS" }, | |||
{ is_grub2_mbr, "Grub 2.0" }, | { is_grub2_mbr, "Grub 2.0" }, | |||
{ is_zero_mbr_not_including_disk_signature_or_copy_protect, "Zeroed" }, | { is_zero_mbr_not_including_disk_signature_or_copy_protect, "Zeroed" }, | |||
}; | }; | |||
// Returns TRUE if the drive seems bootable, FALSE otherwise | // Returns TRUE if the drive seems bootable, FALSE otherwise | |||
BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent) | BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent) | |||
{ | { | |||
const char* mbr_name = "Master Boot Record"; | ||||
FAKE_FD fake_fd = { 0 }; | FAKE_FD fake_fd = { 0 }; | |||
FILE* fp = (FILE*)&fake_fd; | FILE* fp = (FILE*)&fake_fd; | |||
int i; | int i; | |||
fake_fd._handle = (char*)hPhysicalDrive; | fake_fd._handle = (char*)hPhysicalDrive; | |||
set_bytes_per_sector(SelectedDrive.SectorSize); | set_bytes_per_sector(SelectedDrive.SectorSize); | |||
if (!is_br(fp)) { | if (!is_br(fp)) { | |||
suprintf("%s does not have an x86 %s", TargetName, mbr_name); | suprintf("%s does not have a Boot Marker", TargetName); | |||
return FALSE; | return FALSE; | |||
} | } | |||
for (i=0; i<ARRAYSIZE(known_mbr); i++) { | for (i=0; i<ARRAYSIZE(known_mbr); i++) { | |||
if (known_mbr[i].fn(fp)) { | if (known_mbr[i].fn(fp)) { | |||
suprintf("%s has a %s %s", TargetName, known_mbr[i].str, mbr_name); | suprintf("%s has a %s Master Boot Record", TargetName, kn own_mbr[i].str); | |||
return TRUE; | return TRUE; | |||
} | } | |||
} | } | |||
suprintf("%s has an unknown %s", TargetName, mbr_name); | suprintf("%s has an unknown Master Boot Record", TargetName); | |||
return TRUE; | return TRUE; | |||
} | } | |||
const struct {int (*fn)(FILE *fp); char* str;} known_pbr[] = { | const struct {int (*fn)(FILE *fp); char* str;} known_pbr[] = { | |||
{ entire_fat_16_br_matches, "FAT16 DOS" }, | { entire_fat_16_br_matches, "FAT16 DOS" }, | |||
{ entire_fat_16_fd_br_matches, "FAT16 FreeDOS" }, | { entire_fat_16_fd_br_matches, "FAT16 FreeDOS" }, | |||
{ entire_fat_16_ros_br_matches, "FAT16 ReactOS" }, | { entire_fat_16_ros_br_matches, "FAT16 ReactOS" }, | |||
{ entire_fat_32_br_matches, "FAT32 DOS" }, | { entire_fat_32_br_matches, "FAT32 DOS" }, | |||
{ entire_fat_32_nt_br_matches, "FAT32 NT" }, | { entire_fat_32_nt_br_matches, "FAT32 NT" }, | |||
{ entire_fat_32_fd_br_matches, "FAT32 FreeDOS" }, | { entire_fat_32_fd_br_matches, "FAT32 FreeDOS" }, | |||
skipping to change at line 1293 | skipping to change at line 1477 | |||
if (found) | if (found) | |||
break; | break; | |||
} | } | |||
} | } | |||
if (j > MAX_ESP_TOGGLE) | if (j > MAX_ESP_TOGGLE) | |||
goto out; | goto out; | |||
DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTIT ION_GENERIC_ESP; | DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTIT ION_GENERIC_ESP; | |||
} | } | |||
} else { | } else { | |||
for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) { | for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) { | |||
if (DriveLayout->PartitionEntry[i].StartingOffset.QuadPar t == PartitionOffset) | if (DriveLayout->PartitionEntry[i].StartingOffset.QuadPar t == PartitionOffset) { | |||
DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP; | DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP; | |||
break; | ||||
} | ||||
} | } | |||
} | } | |||
if (i >= DriveLayout->PartitionCount) { | if (i >= DriveLayout->PartitionCount) { | |||
uprintf("No partition to toggle"); | uprintf("No partition to toggle"); | |||
goto out; | goto out; | |||
} | } | |||
DriveLayout->PartitionEntry[i].RewritePartition = TRUE; // Just in case | DriveLayout->PartitionEntry[i].RewritePartition = TRUE; // Just in case | |||
r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)Dri veLayout, size, NULL, 0, &size, NULL); | r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)Dri veLayout, size, NULL, 0, &size, NULL); | |||
if (!r) { | if (!r) { | |||
skipping to change at line 1555 | skipping to change at line 1741 | |||
return TRUE; | return TRUE; | |||
} | } | |||
/* | /* | |||
* Mount the volume identified by drive_guid to mountpoint drive_name. | * Mount the volume identified by drive_guid to mountpoint drive_name. | |||
* If volume_name is already mounted, but with a different letter than the | * If volume_name is already mounted, but with a different letter than the | |||
* one requested then drive_name is updated to use that letter. | * one requested then drive_name is updated to use that letter. | |||
*/ | */ | |||
BOOL MountVolume(char* drive_name, char *volume_name) | BOOL MountVolume(char* drive_name, char *volume_name) | |||
{ | { | |||
char mounted_guid[52]; | char mounted_guid[52], dos_name[] = "?:"; | |||
#if defined(WINDOWS_IS_NOT_BUGGY) | #if defined(WINDOWS_IS_NOT_BUGGY) | |||
char mounted_letter[27] = { 0 }; | char mounted_letter[27] = { 0 }; | |||
DWORD size; | DWORD size; | |||
#endif | #endif | |||
if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '? | if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '? | |||
') || | ')) { | |||
(strncmp(volume_name, groot_name, groot_len) == 0)) | SetLastError(ERROR_INVALID_PARAMETER); | |||
return FALSE; | return FALSE; | |||
} | ||||
// If we are working with a "\\?\GLOBALROOT" device, SetVolumeMountPoint( | ||||
) | ||||
// is useless, so try with DefineDosDevice() instead. | ||||
if (_strnicmp(volume_name, groot_name, groot_len) == 0) { | ||||
dos_name[0] = drive_name[0]; | ||||
// Microsoft will also have to explain why "In no case is a trail | ||||
ing backslash allowed" [1] in | ||||
// DefineDosDevice(), instead of just checking if the driver para | ||||
meter is "X:\" and remove the | ||||
// backslash from a copy of the parameter in the bloody API call. | ||||
*THIS* really tells a lot | ||||
// about the level of thought and foresight that actually goes in | ||||
to the Windows APIs... | ||||
// [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi | ||||
/nf-fileapi-definedosdevicew | ||||
if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYST | ||||
EM, dos_name, &volume_name[14])) { | ||||
uprintf("Could not mount %s as %C:", volume_name, drive_n | ||||
ame[0]); | ||||
return FALSE; | ||||
} | ||||
uprintf("%s was successfully mounted as %C:", volume_name, drive_ | ||||
name[0]); | ||||
return TRUE; | ||||
} | ||||
// Great: Windows has a *MAJOR BUG* whereas, in some circumstances, GetVo lumePathNamesForVolumeName() | // Great: Windows has a *MAJOR BUG* whereas, in some circumstances, GetVo lumePathNamesForVolumeName() | |||
// can return the *WRONG* drive letter. And yes, we validated that this i s *NOT* an issue like stack | // can return the *WRONG* drive letter. And yes, we validated that this i s *NOT* an issue like stack | |||
// or buffer corruption and whatnot. It *IS* a Windows bug. So just drop the idea of updating the | // or buffer corruption and whatnot. It *IS* a Windows bug. So just drop the idea of updating the | |||
// drive letter if already mounted and use the passed target always. | // drive letter if already mounted and use the passed target always. | |||
#if defined(WINDOWS_IS_NOT_BUGGY) | #if defined(WINDOWS_IS_NOT_BUGGY) | |||
// Windows may already have the volume mounted but under a different lett er. | // Windows may already have the volume mounted but under a different lett er. | |||
// If that is the case, update drive_name to that letter. | // If that is the case, update drive_name to that letter. | |||
if ( (GetVolumePathNamesForVolumeNameA(volume_name, mounted_letter, sizeo f(mounted_letter), &size)) | if ( (GetVolumePathNamesForVolumeNameA(volume_name, mounted_letter, sizeo f(mounted_letter), &size)) | |||
&& (size > 1) && (mounted_letter[0] != drive_name[0]) ) { | && (size > 1) && (mounted_letter[0] != drive_name[0]) ) { | |||
skipping to change at line 1590 | skipping to change at line 1794 | |||
if (!SetVolumeMountPointA(drive_name, volume_name)) { | if (!SetVolumeMountPointA(drive_name, volume_name)) { | |||
if (GetLastError() == ERROR_DIR_NOT_EMPTY) { | if (GetLastError() == ERROR_DIR_NOT_EMPTY) { | |||
if (!GetVolumeNameForVolumeMountPointA(drive_name, mounte d_guid, sizeof(mounted_guid))) { | if (!GetVolumeNameForVolumeMountPointA(drive_name, mounte d_guid, sizeof(mounted_guid))) { | |||
uprintf("%s is already mounted, but volume GUID c ould not be checked: %s", | uprintf("%s is already mounted, but volume GUID c ould not be checked: %s", | |||
drive_name, WindowsErrorString()); | drive_name, WindowsErrorString()); | |||
} else if (safe_strcmp(volume_name, mounted_guid) != 0) { | } else if (safe_strcmp(volume_name, mounted_guid) != 0) { | |||
uprintf("%s is mounted, but volume GUID doesn't m atch:\r\n expected %s, got %s", | uprintf("%s is mounted, but volume GUID doesn't m atch:\r\n expected %s, got %s", | |||
drive_name, volume_name, mounted_guid); | drive_name, volume_name, mounted_guid); | |||
} else { | } else { | |||
uprintf("%s is already mounted as %C:", volume_na me, drive_name[0]); | duprintf("%s is already mounted as %C:", volume_n ame, drive_name[0]); | |||
return TRUE; | return TRUE; | |||
} | } | |||
uprintf("Retrying after dismount..."); | uprintf("Retrying after dismount..."); | |||
if (!DeleteVolumeMountPointA(drive_name)) | if (!DeleteVolumeMountPointA(drive_name)) | |||
uprintf("Warning: Could not delete volume mountpo int '%s': %s", drive_name, WindowsErrorString()); | uprintf("Warning: Could not delete volume mountpo int '%s': %s", drive_name, WindowsErrorString()); | |||
if (SetVolumeMountPointA(drive_name, volume_name)) | if (SetVolumeMountPointA(drive_name, volume_name)) | |||
return TRUE; | return TRUE; | |||
if ((GetLastError() == ERROR_DIR_NOT_EMPTY) && | if ((GetLastError() == ERROR_DIR_NOT_EMPTY) && | |||
GetVolumeNameForVolumeMountPointA(drive_name, mou nted_guid, sizeof(mounted_guid)) && | GetVolumeNameForVolumeMountPointA(drive_name, mou nted_guid, sizeof(mounted_guid)) && | |||
(safe_strcmp(volume_name, mounted_guid) == 0)) { | (safe_strcmp(volume_name, mounted_guid) == 0)) { | |||
skipping to change at line 1666 | skipping to change at line 1870 | |||
return FALSE; | return FALSE; | |||
} | } | |||
suprintf("Successfully unmounted '%s'", drive_name); | suprintf("Successfully unmounted '%s'", drive_name); | |||
return TRUE; | return TRUE; | |||
} | } | |||
/* | /* | |||
* Issue a complete remount of the volume. | * Issue a complete remount of the volume. | |||
* Note that drive_name *may* be altered when the volume gets remounted. | * Note that drive_name *may* be altered when the volume gets remounted. | |||
*/ | */ | |||
BOOL RemountVolume(char* drive_name) | BOOL RemountVolume(char* drive_name, BOOL bSilent) | |||
{ | { | |||
char volume_name[51]; | char volume_name[51]; | |||
// UDF requires a sync/flush, and it's also a good idea for other FS's | // UDF requires a sync/flush, and it's also a good idea for other FS's | |||
FlushDrive(drive_name[0]); | FlushDrive(drive_name[0]); | |||
if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(vol ume_name))) { | if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(vol ume_name))) { | |||
if (DeleteVolumeMountPointA(drive_name)) { | if (MountVolume(drive_name, volume_name)) { | |||
Sleep(200); | suprintf("Successfully remounted %s as %C:", volume_name, | |||
if (MountVolume(drive_name, volume_name)) { | drive_name[0]); | |||
uprintf("Successfully remounted %s as %C:", volum | ||||
e_name, drive_name[0]); | ||||
} else { | ||||
uprintf("Failed to remount %s as %C:", volume_nam | ||||
e, drive_name[0]); | ||||
// This will leave the drive inaccessible and mus | ||||
t be flagged as an error | ||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_ | ||||
STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); | ||||
return FALSE; | ||||
} | ||||
} else { | } else { | |||
uprintf("Could not remount %s as %C: %s", volume_name, dr | suprintf("Could not remount %s as %C: %s", volume_name, d | |||
ive_name[0], WindowsErrorString()); | rive_name[0], WindowsErrorString()); | |||
// Try to continue regardless | // This will leave the drive inaccessible and must be fla | |||
gged as an error | ||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE) | ||||
|APPERR(ERROR_CANT_REMOUNT_VOLUME); | ||||
return FALSE; | ||||
} | } | |||
} | } | |||
return TRUE; | return TRUE; | |||
} | } | |||
/* | /* | |||
* Zero the first 'size' bytes of a partition. This is needed because we haven't found a way to | * Zero the first 'size' bytes of a partition. This is needed because we haven't found a way to | |||
* properly reset Windows's cached view of a drive partitioning short of cycling the USB port | * properly reset Windows's cached view of a drive partitioning short of cycling the USB port | |||
* (especially IOCTL_DISK_UPDATE_PROPERTIES is *USELESS*), and therefore the OS will try to | * (especially IOCTL_DISK_UPDATE_PROPERTIES is *USELESS*), and therefore the OS will try to | |||
* read the file system data at an old location, even if the partition has just been deleted. | * read the file system data at an old location, even if the partition has just been deleted. | |||
* TODO: We should do something like this in DeletePartitions() too. | ||||
*/ | */ | |||
static BOOL ClearPartition(HANDLE hDrive, LARGE_INTEGER offset, DWORD size) | static BOOL ClearPartition(HANDLE hDrive, LARGE_INTEGER offset, DWORD size) | |||
{ | { | |||
BOOL r = FALSE; | BOOL r = FALSE; | |||
uint8_t* buffer = calloc(size, 1); | uint8_t* buffer = calloc(size, 1); | |||
if (buffer == NULL) | if (buffer == NULL) | |||
return FALSE; | return FALSE; | |||
if (!SetFilePointerEx(hDrive, offset, NULL, FILE_BEGIN)) { | if (!SetFilePointerEx(hDrive, offset, NULL, FILE_BEGIN)) { | |||
skipping to change at line 1854 | skipping to change at line 2051 | |||
} else if ((extra_partitions & XP_CASPER)) { | } else if ((extra_partitions & XP_CASPER)) { | |||
assert(persistence_size != 0); | assert(persistence_size != 0); | |||
extra_part_name = L"Linux Persistence"; | extra_part_name = L"Linux Persistence"; | |||
extra_part_size_in_tracks = persistence_size / bytes_per_ track; | extra_part_size_in_tracks = persistence_size / bytes_per_ track; | |||
} else if (extra_partitions & XP_COMPAT) { | } else if (extra_partitions & XP_COMPAT) { | |||
extra_part_name = L"BIOS Compatibility"; | extra_part_name = L"BIOS Compatibility"; | |||
extra_part_size_in_tracks = 1; // One track for the extr a partition | extra_part_size_in_tracks = 1; // One track for the extr a partition | |||
} else { | } else { | |||
assert(FALSE); | assert(FALSE); | |||
} | } | |||
// NB: Because we already subtracted the backup GPT size from the | ||||
main partition size and | ||||
// this extra partition is indexed on main size, it does not over | ||||
flow into the backup GPT. | ||||
main_part_size_in_sectors = ((main_part_size_in_sectors / Selecte dDrive.SectorsPerTrack) - | main_part_size_in_sectors = ((main_part_size_in_sectors / Selecte dDrive.SectorsPerTrack) - | |||
extra_part_size_in_tracks) * SelectedDrive.SectorsPerTrac k; | extra_part_size_in_tracks) * SelectedDrive.SectorsPerTrac k; | |||
} | } | |||
if (main_part_size_in_sectors <= 0) { | if (main_part_size_in_sectors <= 0) { | |||
uprintf("Error: Invalid %S size", main_part_name); | uprintf("Error: Invalid %S size", main_part_name); | |||
return FALSE; | return FALSE; | |||
} | } | |||
uprintf("● Creating %S (offset: %lld, size: %s)", main_part_name, DriveLa youtEx.PartitionEntry[pn].StartingOffset.QuadPart, | uprintf("● Creating %S (offset: %lld, size: %s)", main_part_name, DriveLa youtEx.PartitionEntry[pn].StartingOffset.QuadPart, | |||
SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.Sec torSize, TRUE, FALSE)); | SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.Sec torSize, TRUE, FALSE)); | |||
// Zero the beginning of this partition to avoid conflicting leftovers | // Zero the beginning of this partition to avoid conflicting leftovers | |||
End of changes. 74 change blocks. | ||||
215 lines changed or deleted | 453 lines changed or added |