From 1c39a80d72ce3f6914925ad18518857063dfa413 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 25 Apr 2019 18:58:55 +0100 Subject: [PATCH] [core] partial overhaul of the partitioning and formatting facilities * Add VDS formatting support (through an Alt-V cheat mode) * Add partition index support * Improve(?) Windows To Go support by following Microsoft recommended partition order * Code refactoring & cleanup --- src/drive.c | 486 ++++++++++++++++-------------- src/drive.h | 122 +++++++- src/format.c | 770 ++++++++++++++++++++++++++++++++--------------- src/msapi_utf8.h | 8 + src/rufus.c | 30 +- src/rufus.h | 15 +- src/rufus.rc | 10 +- src/settings.h | 4 +- src/stdio.c | 2 +- src/syslinux.c | 2 +- src/ui.c | 15 +- 11 files changed, 955 insertions(+), 509 deletions(-) diff --git a/src/drive.c b/src/drive.c index dc4d72fe..311fde3a 100644 --- a/src/drive.c +++ b/src/drive.c @@ -45,6 +45,11 @@ #include "fat32.h" #include "ntfs.h" +#define GLOBALROOT_NAME "\\\\?\\GLOBALROOT" +const char* sfd_name = "Super Floppy Disk"; +const char* groot_name = GLOBALROOT_NAME; +const size_t groot_len = sizeof(GLOBALROOT_NAME) - 1; + #if !defined(PARTITION_BASIC_DATA_GUID) const GUID PARTITION_BASIC_DATA_GUID = { 0xebd0a0a2L, 0xb9e5, 0x4433, {0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7} }; @@ -70,6 +75,8 @@ const IID IID_IVdsSwProvider = { 0x9aa58360, 0xce33, 0x4f92, { 0xb6, 0x58, 0xed, const IID IID_IVdsPack = { 0x3b69d7f5, 0x9d94, 0x4648, { 0x91, 0xca, 0x79, 0x93, 0x9b, 0xa2, 0x63, 0xbf } }; const IID IID_IVdsDisk = { 0x07e5c822, 0xf00c, 0x47a1, { 0x8f, 0xce, 0xb2, 0x44, 0xda, 0x56, 0xfd, 0x06 } }; const IID IID_IVdsAdvancedDisk = { 0x6e6f6b40, 0x977c, 0x4069, { 0xbd, 0xdd, 0xac, 0x71, 0x00, 0x59, 0xf8, 0xc0 } }; +const IID IID_IVdsVolume = { 0x88306BB2, 0xE71F, 0x478C, { 0x86, 0xA2, 0x79, 0xDA, 0x20, 0x0A, 0x0F, 0x11} }; +const IID IID_IVdsVolumeMF3 = { 0x6788FAF9, 0x214E, 0x4B85, { 0xBA, 0x59, 0x26, 0x69, 0x53, 0x61, 0x6E, 0x09 } }; #endif PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS)); @@ -79,8 +86,8 @@ PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_ */ RUFUS_DRIVE_INFO SelectedDrive; BOOL installed_uefi_ntfs; +DWORD partition_index[3]; uint64_t persistence_size = 0; -const char* sfd_name = "Super Floppy Disk"; /* * The following methods get or set the AutoMount setting (which is different from AutoRun) @@ -153,7 +160,9 @@ static HANDLE GetHandle(char* Path, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWr goto out; // Resolve a device path, so that we can look for that handle in case of access issues. - if (QueryDosDeviceA(&Path[4], DevPath, sizeof(DevPath)) == 0) + if (safe_strncmp(Path, groot_name, groot_len) == 0) + static_strcpy(DevPath, &Path[groot_len]); + else if (QueryDosDeviceA(&Path[4], DevPath, sizeof(DevPath)) == 0) strcpy(DevPath, "???"); for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) { @@ -237,14 +246,18 @@ out: * Return the path to access a partition on a specific disk, or NULL on error. * The string is allocated and must be freed (to ensure concurrent access) */ -char* GetPartitionName(DWORD DriveIndex, DWORD PartitionNumber) +char* GetPartitionName(DWORD DriveIndex, DWORD PartitionIndex) { BOOL success = FALSE; char partition_name[32]; CheckDriveIndex(DriveIndex); + if (PartitionIndex >= MAX_PARTITIONS) + goto out; + if (PartitionIndex == 0) + PartitionIndex = 1; - static_sprintf(partition_name, "\\Device\\Harddisk%lu\\Partition%lu", DriveIndex, PartitionNumber); + static_sprintf(partition_name, "\\Device\\Harddisk%lu\\Partition%lu", DriveIndex, PartitionIndex); success = TRUE; out: return (success) ? safe_strdup(partition_name) : NULL; @@ -263,25 +276,30 @@ HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, B } /* - * Return the first GUID volume name for the associated drive or NULL if not found + * Return the GUID volume name for the disk and partition specified, or NULL if not found. * See http://msdn.microsoft.com/en-us/library/cc542456.aspx - * The returned string is allocated and must be freed + * PartitionIndex starts at 1 (for the first partition). If PartitionIndex is zero, then + * the first partition found by this function (which *MAY NOT* be the actual first partition) + * is returned. The returned string is allocated and must be freed. */ -char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent) +char* GetLogicalName(DWORD DriveIndex, DWORD PartitionIndex, BOOL bKeepTrailingBackslash, BOOL bSilent) { - BOOL success = FALSE; - char volume_name[MAX_PATH]; + static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" }; + static const char* volume_start = "\\\\?\\"; + char *ret = NULL, volume_name[MAX_PATH], path[MAX_PATH]; HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE; - size_t len; - char path[MAX_PATH]; VOLUME_DISK_EXTENTS_REDEF DiskExtents; DWORD size; UINT drive_type; - int i, j; - static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" }; - static const char* volume_start = "\\\\?\\"; + StrArray found_name; + uint64_t found_offset[MAX_PARTITIONS] = { 0 }; + uint32_t i, j, k; + size_t len; + StrArrayCreate(&found_name, MAX_PARTITIONS); CheckDriveIndex(DriveIndex); + if (PartitionIndex > MAX_PARTITIONS) + goto out; for (i = 0; hDrive == INVALID_HANDLE_VALUE; i++) { if (i == 0) { @@ -295,7 +313,7 @@ char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent if (GetLastError() != ERROR_NO_MORE_FILES) { suprintf("Could not access next GUID volume: %s", WindowsErrorString()); } - goto out; + break; } } @@ -337,18 +355,68 @@ char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent continue; } safe_closehandle(hDrive); - if ((DiskExtents.NumberOfDiskExtents >= 1) && (DiskExtents.Extents[0].DiskNumber == DriveIndex)) { - if (bKeepTrailingBackslash) - volume_name[len-1] = '\\'; - success = TRUE; + if (DiskExtents.NumberOfDiskExtents == 0) { + suprintf("Ignoring volume '%s' because it has no extents...", volume_name); + continue; + } + if (DiskExtents.NumberOfDiskExtents != 1) { + // If we have more than one extent for a volume, it means that someone + // is using RAID-1 or something => Stay well away from such a volume! + suprintf("Ignoring volume '%s' because it has more than one extent (RAID?)...", volume_name); + continue; + } + if (DiskExtents.Extents[0].DiskNumber != DriveIndex) + // Not on our disk + continue; + + if (found_name.Index == MAX_PARTITIONS) { + uprintf("Error: Trying to process a disk with more than %d partitions!", MAX_PARTITIONS); + goto out; + } + + if (bKeepTrailingBackslash) + volume_name[len - 1] = '\\'; + found_offset[found_name.Index] = DiskExtents.Extents[0].StartingOffset.QuadPart; + StrArrayAdd(&found_name, volume_name, TRUE); + // uprintf("GOT %s @%lld", volume_name, DiskExtents.Extents[0].StartingOffset.QuadPart); + } + + if (found_name.Index == 0) + goto out; + + // Now process all the volumes we found, and find the one that matches our partition index + if (PartitionIndex == 0) { + i = 0; + } else for (i = 0, k = 0; i < found_name.Index; i++) { + for (j = 0; j < found_name.Index; j++) { + if (found_offset[i] > found_offset[j]) + k++; + } + if (k == ((int)PartitionIndex) - 1) break; + } + if (i < found_name.Index) { + ret = safe_strdup(found_name.String[i]); + } else { + // Some volumes, such as ESPs, are not listed by Windows, be it with VDS or other APIs. + // For these, we return the "\\?\GLOBALROOT\Device\HarddiskVolume#" identifier that + // matches our "Harddisk#Partition#", as reported by QueryDosDevice(). + static_sprintf(path, "Harddisk%luPartition%lu", DriveIndex, PartitionIndex); + static_strcpy(volume_name, groot_name); + if (!QueryDosDeviceA(path, &volume_name[groot_len], (DWORD)(MAX_PATH - groot_len)) || (strlen(volume_name) < 20)) { + uprintf("Could not find the DOS volume name for '%s': %s", path, WindowsErrorString()); + } else { + if (bKeepTrailingBackslash) + static_strcat(volume_name, "\\"); + ret = safe_strdup(volume_name); } } out: if (hVolume != INVALID_HANDLE_VALUE) FindVolumeClose(hVolume); - return (success)?safe_strdup(volume_name):NULL; + StrArrayDestroy(&found_name); + return ret; } /* @@ -378,7 +446,8 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &IID_IVdsServiceLoader, (void **)&pLoader); if (hr != S_OK) { - uprintf("Could not create VDS Loader Instance: hr=%X\n", hr); + VDS_SET_ERROR(hr); + uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString()); goto out; } @@ -386,14 +455,16 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); IVdsServiceLoader_Release(pLoader); if (hr != S_OK) { - uprintf("Could not load VDS Service: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not load VDS Service: %s", WindowsErrorString()); goto out; } // Query the VDS Service Providers hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum); if (hr != S_OK) { - uprintf("Could not query VDS Service Providers: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Service Providers: %s", WindowsErrorString()); goto out; } @@ -407,7 +478,8 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider); IUnknown_Release(pUnk); if (hr != S_OK) { - uprintf("Could not get VDS Provider: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Provider: %s", WindowsErrorString()); goto out; } @@ -415,7 +487,8 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider); IVdsProvider_Release(pProvider); if (hr != S_OK) { - uprintf("Could not get VDS Software Provider: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Software Provider: %s", WindowsErrorString()); goto out; } @@ -423,7 +496,8 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); IVdsSwProvider_Release(pSwProvider); if (hr != S_OK) { - uprintf("Could not get VDS Software Provider Packs: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Software Provider Packs: %s", WindowsErrorString()); goto out; } @@ -436,14 +510,16 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack); IUnknown_Release(pPackUnk); if (hr != S_OK) { - uprintf("Could not query VDS Software Provider Pack: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Software Provider Pack: %s", WindowsErrorString()); goto out; } // Use the pack interface to access the disks hr = IVdsPack_QueryDisks(pPack, &pEnumDisk); if (hr != S_OK) { - uprintf("Could not query VDS disks: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not query VDS disks: %s", WindowsErrorString()); goto out; } @@ -458,14 +534,16 @@ BOOL DeletePartitions(DWORD DriveIndex) // Get the disk interface. hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void **)&pDisk); if (hr != S_OK) { - uprintf("Could not query VDS Disk Interface: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Disk Interface: %s", WindowsErrorString()); goto out; } // Get the disk properties hr = IVdsDisk_GetProperties(pDisk, &diskprop); if (hr != S_OK) { - uprintf("Could not query VDS Disk Properties: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Disk Properties: %s", WindowsErrorString()); goto out; } @@ -479,65 +557,58 @@ BOOL DeletePartitions(DWORD DriveIndex) hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk, (void **)&pAdvancedDisk); IVdsDisk_Release(pDisk); if (hr != S_OK) { - uprintf("Could not access VDS Advanced Disk interface: 0x%08X", hr); + VDS_SET_ERROR(hr); + uprintf("Could not access VDS Advanced Disk interface: %s", WindowsErrorString()); goto out; } // Query the partition data, so we can get the start offset, which we need for deletion hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size); - if (hr != S_OK) { - uprintf("No partition to delete on disk '%ws'", diskprop.pwszName); - goto out; - } - - uprintf("Deleting ALL partitions from disk '%ws':", diskprop.pwszName); - - // Now go through each partition - r = (prop_array_size >= 1); - 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_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE); - if (hr != S_OK) { - r = FALSE; - uprintf("Could not delete partitions: 0x%08X", hr); + if (hr == S_OK) { + uprintf("Deleting ALL partition(s) from disk '%S':", diskprop.pwszName); + // 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_DeletePartition(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); -// NB: In the future, we could try something like this to format partitions: #if 0 - // Initiate formatting and wait for completion. - LPWSTR pwszLabel[8] = L"TEST"; - ULONGLONG Offset = 1024 * 1024; - BOOL QuickFormat = TRUE; - BOOL EnableCompression = FALSE; - IVdsAsync* pAsync; - hr = IVdsAdvancedDisk_FormatPartition(pAdvancedDisk, Offset, FileSystemType, - pwszLabel, 0, TRUE, QuickFormat, EnableCompression, &pAsync); - if (hr != S_OK) { - uprintf("Could not start formatting: 0x%08X", hr); - goto out; - } - VDS_ASYNC_OUTPUT AsyncOut; - ULONG ulPercentCompleted; + // Issue a Clean while we're at it HRESULT hr2 = E_FAIL; - do { - hr = IVdsAsync_QueryStatus(pAsync, &hr2, &ulPercentCompleted); + ULONG completed; + IVdsAsync* pAsync; + hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, FALSE, FALSE, &pAsync); + while (SUCCEEDED(hr)) { + if (IS_ERROR(FormatStatus)) { + IVdsAsync_Cancel(pAsync); + break; + } + hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed); if (SUCCEEDED(hr)) { - printf("%ld%%", ulPercentCompleted); - if ((hr2 != S_OK) && (hr2 != VDS_E_OPERATION_PENDING)) { - uprintf("hr2: %X", hr2); + hr = hr2; + if (hr == S_OK) break; - } - if (hr2 == S_OK) { - break; - } + if (hr == VDS_E_OPERATION_PENDING) + hr = S_OK; } Sleep(500); - } while (SUCCEEDED(hr)); - hr = IVdsAsync_Wait(pAsync, &hr2, &AsyncOut); - IVdsAsync_Release(pAsync); + } + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not clean disk: %s", WindowsErrorString()); + } #endif IVdsAdvancedDisk_Release(pAdvancedDisk); goto out; @@ -551,7 +622,7 @@ out: /* Wait for a logical drive to reappear - Used when a drive has just been repartitioned */ -BOOL WaitForLogical(DWORD DriveIndex) +BOOL WaitForLogical(DWORD DriveIndex, DWORD PartitionIndex) { uint64_t EndTime; char* LogicalPath = NULL; @@ -560,11 +631,13 @@ BOOL WaitForLogical(DWORD DriveIndex) // make sure we don't spend more than DRIVE_ACCESS_TIMEOUT in wait. EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT; do { - LogicalPath = GetLogicalName(DriveIndex, FALSE, TRUE); - if (LogicalPath != NULL) { + LogicalPath = GetLogicalName(DriveIndex, PartitionIndex, FALSE, TRUE); + // Need to filter out GlobalRoot devices as we don't want to wait on those + if ((LogicalPath != NULL) && (strncmp(LogicalPath, groot_name, groot_len) != 0)) { free(LogicalPath); return TRUE; } + free(LogicalPath); if (IS_ERROR(FormatStatus)) // User cancel return FALSE; Sleep(DRIVE_ACCESS_TIMEOUT/DRIVE_ACCESS_RETRIES); @@ -574,14 +647,14 @@ BOOL WaitForLogical(DWORD DriveIndex) } /* - * Obtain a handle to the first logical volume on the disk identified by DriveIndex + * Obtain a handle to the volume identified by DriveIndex + PartitionIndex * Returns INVALID_HANDLE_VALUE on error or NULL if no logical path exists (typical * of unpartitioned drives) */ -HANDLE GetLogicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare) +HANDLE GetLogicalHandle(DWORD DriveIndex, DWORD PartitionIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare) { HANDLE hLogical = INVALID_HANDLE_VALUE; - char* LogicalPath = GetLogicalName(DriveIndex, FALSE, FALSE); + char* LogicalPath = GetLogicalName(DriveIndex, PartitionIndex, FALSE, FALSE); if (LogicalPath == NULL) { uprintf("No logical drive found (unpartitioned?)"); @@ -956,7 +1029,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys SelectedDrive.nPartitions = 0; // Populate the filesystem data FileSystemName[0] = 0; - volume_name = GetLogicalName(DriveIndex, TRUE, FALSE); + volume_name = GetLogicalName(DriveIndex, 0, TRUE, FALSE); if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) { suprintf("No volume information for drive 0x%02x", DriveIndex); } @@ -1142,49 +1215,50 @@ BOOL UnmountVolume(HANDLE hDrive) /* * Mount the volume identified by drive_guid to mountpoint drive_name. - * If drive_guid is already mounted, but with a different letter than the - * one requested, drive_name is updated to use that letter. + * If volume_name is already mounted, but with a different letter than the + * one requested then drive_name is updated to use that letter. */ -BOOL MountVolume(char* drive_name, char *drive_guid) +BOOL MountVolume(char* drive_name, char *volume_name) { char mounted_guid[52]; char mounted_letter[27] = { 0 }; DWORD size; - if (drive_name[0] == '?') + if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '?') || + (strncmp(volume_name, groot_name, groot_len) == 0)) return FALSE; // Windows may already have the volume mounted but under a different letter. // If that is the case, update drive_name to that letter. - if ( (GetVolumePathNamesForVolumeNameA(drive_guid, mounted_letter, sizeof(mounted_letter), &size)) + if ( (GetVolumePathNamesForVolumeNameA(volume_name, mounted_letter, sizeof(mounted_letter), &size)) && (size > 1) && (mounted_letter[0] != drive_name[0]) ) { uprintf("%s is already mounted as %C: instead of %C: - Will now use this target instead...", - drive_guid, mounted_letter[0], drive_name[0]); + volume_name, mounted_letter[0], drive_name[0]); drive_name[0] = mounted_letter[0]; return TRUE; } - if (!SetVolumeMountPointA(drive_name, drive_guid)) { + if (!SetVolumeMountPointA(drive_name, volume_name)) { if (GetLastError() == ERROR_DIR_NOT_EMPTY) { if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) { uprintf("%s is already mounted, but volume GUID could not be checked: %s", drive_name, WindowsErrorString()); - } else if (safe_strcmp(drive_guid, mounted_guid) != 0) { + } else if (safe_strcmp(volume_name, mounted_guid) != 0) { uprintf("%s is mounted, but volume GUID doesn't match:\r\n expected %s, got %s", - drive_name, drive_guid, mounted_guid); + drive_name, volume_name, mounted_guid); } else { - uprintf("%s is already mounted as %C:", drive_guid, drive_name[0]); + uprintf("%s is already mounted as %C:", volume_name, drive_name[0]); return TRUE; } uprintf("Retrying after dismount..."); if (!DeleteVolumeMountPointA(drive_name)) uprintf("Warning: Could not delete volume mountpoint: %s", WindowsErrorString()); - if (SetVolumeMountPointA(drive_name, drive_guid)) + if (SetVolumeMountPointA(drive_name, volume_name)) return TRUE; if ((GetLastError() == ERROR_DIR_NOT_EMPTY) && GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid)) && - (safe_strcmp(drive_guid, mounted_guid) == 0)) { - uprintf("%s was remounted as %C: (second time lucky!)", drive_guid, drive_name[0]); + (safe_strcmp(volume_name, mounted_guid) == 0)) { + uprintf("%s was remounted as %C: (second time lucky!)", volume_name, drive_name[0]); return TRUE; } } @@ -1194,85 +1268,39 @@ BOOL MountVolume(char* drive_name, char *drive_guid) } /* - * Mount partition #part_nr, residing on the same disk as drive_name to an available - * drive letter. Returns the newly allocated drive string. - * We need to do this because, for instance, EFI System partitions are not assigned - * Volume GUIDs by the OS, and we need to have a letter assigned, for when we invoke - * bcdtool for Windows To Go. All in all, the process looks like this: - * 1. F: = \Device\HarddiskVolume9 (SINGLE LOOKUP) - * 2. Harddisk5Partition1 = \Device\HarddiskVolume9 (FULL LOOKUP) - * 3. Harddisk5Partition2 = \Device\HarddiskVolume10 (SINGLE LOOKUP) - * 4. DefineDosDevice(letter, \Device\HarddiskVolume10) + * Alternate version of MountVolume required for ESP's, since Windows (including VDS) does + * *NOT* provide any means of mounting these volume but through DefineDosDevice(). Also + * note the bcdboot is very finicky about what it may or may not handle, even if the + * mount was successful (e.g. '\Device\HarddiskVolume###' vs 'Device\HarddiskVolume###'). + * Returned string is static (no concurrency) and should not be freed. */ -char* AltMountVolume(const char* drive_name, uint8_t part_nr) +char* AltMountVolume(DWORD DriveIndex, DWORD PartitionIndex) { + char* ret = NULL, *volume_name = NULL; static char mounted_drive[] = "?:"; - const DWORD bufsize = 65536; - char *buffer = NULL, *p, target[2][MAX_PATH], *ret = NULL; - size_t i; mounted_drive[0] = GetUnusedDriveLetter(); if (mounted_drive[0] == 0) { uprintf("Could not find an unused drive letter"); goto out; } - - target[0][0] = 0; - // Convert our drive letter to something like "\Device\HarddiskVolume9" - if (!QueryDosDeviceA(drive_name, target[0], MAX_PATH) || (strlen(target[0]) == 0)) { - uprintf("Could not get the DOS volume name for '%s': %s", drive_name, WindowsErrorString()); + volume_name = GetLogicalName(DriveIndex, PartitionIndex, FALSE, TRUE); + if ((volume_name == NULL) || (strncmp(volume_name, groot_name, groot_len) != 0)) { + uprintf("Unexpected volume name: '%s'", volume_name); goto out; } - // Now parse the whole DOS device list to find the 'Harddisk#Partition#' that matches the above - // TODO: realloc if someone ever manages to burst through 64K of DOS devices - buffer = malloc(bufsize); - if (buffer == NULL) - goto out; - - buffer[0] = 0; - if (!QueryDosDeviceA(NULL, buffer, bufsize)) { - uprintf("Could not get the DOS device list: %s", WindowsErrorString()); + // bcdboot sure won't like it if you forget the starting '\' + if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, mounted_drive, &volume_name[14])) { + uprintf("Could not mount '%s' to '%s': %s", &volume_name[14], mounted_drive, WindowsErrorString()); goto out; } - p = buffer; - while (strlen(p) != 0) { - if ((strncmp("Harddisk", p, 8) == 0) && (strstr(&p[9], "Partition") != NULL)) { - target[1][0] = 0; - if (QueryDosDeviceA(p, target[1], MAX_PATH) && (strlen(target[1]) != 0)) - if ((strcmp(target[1], target[0]) == 0) && (p[1] != ':')) - break; - } - p += strlen(p) + 1; - } - - i = strlen(p); - if (i == 0) { - uprintf("Could not find partition mapping for %s", target[0]); - goto out; - } - - while ((--i > 0) && (isdigit(p[i]))); - p[++i] = '0' + part_nr; - p[++i] = 0; - - target[0][0] = 0; - if (!QueryDosDeviceA(p, target[0], MAX_PATH) || (strlen(target[0]) == 0)) { - uprintf("Could not find the DOS volume name for partition '%s': %s", p, WindowsErrorString()); - goto out; - } - - if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, mounted_drive, target[0])) { - uprintf("Could not mount '%s' to '%s': %s", target[0], mounted_drive, WindowsErrorString()); - goto out; - } - - uprintf("Successfully mounted '%s' (USB partition %d) as '%s'", target[0], part_nr, mounted_drive); + uprintf("Successfully mounted '%s' (Partition %d) as '%s'", &volume_name[14], PartitionIndex, mounted_drive); ret = mounted_drive; out: - safe_free(buffer); + free(volume_name); return ret; } @@ -1297,23 +1325,23 @@ BOOL AltUnmountVolume(const char* drive_name) */ BOOL RemountVolume(char* drive_name) { - char drive_guid[51]; + char volume_name[51]; // UDF requires a sync/flush, and it's also a good idea for other FS's FlushDrive(drive_name[0]); - if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) { + if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(volume_name))) { if (DeleteVolumeMountPointA(drive_name)) { Sleep(200); - if (MountVolume(drive_name, drive_guid)) { - uprintf("Successfully remounted %s as %C:", drive_guid, drive_name[0]); + if (MountVolume(drive_name, volume_name)) { + uprintf("Successfully remounted %s as %C:", volume_name, drive_name[0]); } else { - uprintf("Failed to remount %s as %C:", drive_guid, drive_name[0]); + uprintf("Failed to remount %s as %C:", volume_name, drive_name[0]); // This will leave the drive inaccessible and must be flagged as an error FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); return FALSE; } } else { - uprintf("Could not remount %s as %C: %s", drive_guid, drive_name[0], WindowsErrorString()); + uprintf("Could not remount %s as %C: %s", volume_name, drive_name[0], WindowsErrorString()); // Try to continue regardless } } @@ -1328,7 +1356,7 @@ typedef struct _DRIVE_LAYOUT_INFORMATION_EX4 { DRIVE_LAYOUT_INFORMATION_MBR Mbr; DRIVE_LAYOUT_INFORMATION_GPT Gpt; } Type; - PARTITION_INFORMATION_EX PartitionEntry[4]; + PARTITION_INFORMATION_EX PartitionEntry[MAX_PARTITIONS]; } DRIVE_LAYOUT_INFORMATION_EX4,*PDRIVE_LAYOUT_INFORMATION_EX4; /* @@ -1341,13 +1369,14 @@ typedef struct _DRIVE_LAYOUT_INFORMATION_EX4 { BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL mbr_uefi_marker, uint8_t extra_partitions) { const char* PartitionTypeName[] = { "MBR", "GPT", "SFD" }; + const wchar_t* extra_part_name; unsigned char* buffer; size_t uefi_ntfs_size = 0; CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}}; DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0}; BOOL r; DWORD i, size, bufsize, pn = 0; - LONGLONG main_part_size_in_sectors, extra_part_size_in_tracks = 0, ms_efi_size; + LONGLONG main_part_size_in_sectors, extra_part_size_in_tracks = 0, ms_esp_size; const LONGLONG bytes_per_track = ((LONGLONG)SelectedDrive.SectorsPerTrack) * SelectedDrive.SectorSize; PrintInfoDebug(0, MSG_238, PartitionTypeName[partition_style]); @@ -1361,6 +1390,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m if (uefi_ntfs_size == 0) return FALSE; } + memset(partition_index, 0, sizeof(partition_index)); // Compute the start offset of our first partition if ((partition_style == PARTITION_STYLE_GPT) || (!IsChecked(IDC_OLD_BIOS_FIXES))) { @@ -1371,14 +1401,43 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = bytes_per_track; } + // If required, set the ESP (which Microsoft wants to be the first) + if (extra_partitions & XP_ESP) { + uprintf("● Creating EFI System Partition"); + // The size of the ESP depends on the minimum size we're able to format in FAT32, which + // in turn depends on the cluster size used, which in turn depends on the disk sector size. + // Plus some people are complaining that the *OFFICIAL MINIMUM SIZE* as documented by Microsoft at + // https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions + // is too small. See: https://github.com/pbatard/rufus/issues/979 + if (SelectedDrive.SectorSize <= 4096) + ms_esp_size = 300 * MB; + else + ms_esp_size = 1200 * MB; // That'll teach you to have a nonstandard disk! + extra_part_size_in_tracks = (ms_esp_size + bytes_per_track - 1) / bytes_per_track; + DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = extra_part_size_in_tracks * bytes_per_track; + + if (partition_style == PARTITION_STYLE_GPT) { + DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_SYSTEM_GUID; + IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); + // coverity[strcpy_overrun] + wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"EFI System Partition"); + } else { + DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef; + } + pn++; + partition_index[PI_ESP] = pn; + DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn - 1].StartingOffset.QuadPart + + DriveLayoutEx.PartitionEntry[pn - 1].PartitionLength.QuadPart; + } + // If required, set the MSR partition (GPT only - must be created before the data part) if ((partition_style == PARTITION_STYLE_GPT) && (extra_partitions & XP_MSR)) { - uprintf("Adding MSR partition"); + uprintf("● Creating MSR Partition"); DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = 128*MB; DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_MSFT_RESERVED_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); // coverity[strcpy_overrun] - wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Microsoft reserved partition"); + wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Microsoft Reserved Partition"); // We must zero the beginning of this partition, else we get FAT leftovers and stuff if (SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) { @@ -1390,49 +1449,45 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m free(buffer); } } - pn++; DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart + DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart; } + // Clear the extra partitions we processed + extra_partitions &= ~(XP_ESP|XP_MSR); // Set our main data partition main_part_size_in_sectors = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) / // Need 33 sectors at the end for secondary GPT SelectedDrive.SectorSize - ((partition_style == PARTITION_STYLE_GPT)?33:0); - if (main_part_size_in_sectors <= 0) - return FALSE; - - // Adjust the size according to extra partitions (which we always align to a track) - if (extra_partitions) { - uprintf("Adding %s partition", (extra_partitions & XP_CASPER) ? "casper-rw": "extra"); - if (extra_partitions & XP_EFI) { - // The size of the EFI partition depends on the minimum size we're able to format in FAT32, which - // in turn depends on the cluster size used, which in turn depends on the disk sector size. - // Plus some people are complaining that the *OFFICIAL MINIMUM SIZE* as documented by Microsoft at - // https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions - // is too small. See: https://github.com/pbatard/rufus/issues/979 - if (SelectedDrive.SectorSize <= 4096) - ms_efi_size = 300*MB; - else - ms_efi_size = 1200*MB; // That'll teach you to have a nonstandard disk! - extra_part_size_in_tracks = (ms_efi_size + bytes_per_track - 1) / bytes_per_track; - } else if (extra_partitions & XP_UEFI_NTFS) { + if (!extra_partitions) { + uprintf("● Creating Main Data Partition: %lld tracks (%s)", main_part_size_in_sectors / SelectedDrive.SectorsPerTrack, + SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.SectorSize, TRUE, FALSE)); + } else { + // Adjust the size according to extra partitions (which we always align to a track) + if (extra_partitions & XP_UEFI_NTFS) { + extra_part_name = L"UEFI:NTFS"; extra_part_size_in_tracks = (max(MIN_EXTRA_PART_SIZE, uefi_ntfs_size) + bytes_per_track - 1) / bytes_per_track; - } else if (extra_partitions & XP_COMPAT) { - extra_part_size_in_tracks = 1; // One track for the extra partition } else if ((extra_partitions & XP_CASPER)) { assert(persistence_size != 0); + extra_part_name = L"Linux Persistence"; extra_part_size_in_tracks = persistence_size / bytes_per_track; + } else if (extra_partitions & XP_COMPAT) { + extra_part_name = L"BIOS Compatibility"; + extra_part_size_in_tracks = 1; // One track for the extra partition } - uprintf("Reserved %" PRIi64" tracks (%s) for %s partition", extra_part_size_in_tracks, - SizeToHumanReadable(extra_part_size_in_tracks * bytes_per_track, TRUE, FALSE), - (extra_partitions & XP_CASPER) ? "casper-rw" : "extra"); main_part_size_in_sectors = ((main_part_size_in_sectors / SelectedDrive.SectorsPerTrack) - extra_part_size_in_tracks) * SelectedDrive.SectorsPerTrack; - if (main_part_size_in_sectors <= 0) - return FALSE; + uprintf("● Creating Main Data Partition: %lld tracks (%s)", main_part_size_in_sectors / SelectedDrive.SectorsPerTrack, + SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.SectorSize, TRUE, FALSE)); + uprintf("● Creating %S Partition: %lld tracks (%s)", extra_part_name, extra_part_size_in_tracks, + SizeToHumanReadable(extra_part_size_in_tracks * bytes_per_track, TRUE, FALSE)); } + if (main_part_size_in_sectors <= 0) { + uprintf("Error: Invalid Main Partition size!"); + return FALSE; + } + DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = main_part_size_in_sectors * SelectedDrive.SectorSize; if (partition_style == PARTITION_STYLE_MBR) { DriveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = (boot_type != BT_NON_BOOTABLE); @@ -1446,6 +1501,11 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m case FS_REFS: DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07; break; + case FS_EXT2: + case FS_EXT3: + case FS_EXT4: + DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83; + break; case FS_FAT32: DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0c; // FAT32 LBA break; @@ -1456,61 +1516,49 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m } else { DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); - wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Microsoft Basic Data"); + wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Basic Data"); } pn++; + partition_index[PI_MAIN] = pn; // Set the optional extra partition if (extra_partitions) { + // Don't forget to set our peristent partition index! + if (extra_partitions & XP_CASPER) + partition_index[PI_CASPER] = pn + 1; // Should end on a track boundary DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart + DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart; DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = (extra_partitions & XP_UEFI_NTFS)?uefi_ntfs_size: extra_part_size_in_tracks * bytes_per_track; if (partition_style == PARTITION_STYLE_GPT) { - const wchar_t* name = L"Basic Data"; - const GUID* guid = &PARTITION_BASIC_DATA_GUID; - if (extra_partitions & XP_EFI) { - guid = &PARTITION_SYSTEM_GUID; - name = L"EFI system partition"; - } else if (extra_partitions & XP_CASPER) { - // TODO: We may also want to use PARTITION_LINUX_HOME_GUID as fallback - // to automout as /home in case casper-rw fails. - name = L"casper-rw"; // Just in case - } else if (extra_partitions & XP_UEFI_NTFS) { - name = L"UEFI:NTFS"; - } else { - assert(FALSE); - } - DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = *guid; + DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); - wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, name); + wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, extra_part_name); } else { - BYTE type = 0; if (extra_partitions & XP_UEFI_NTFS) { - type = 0xef; + DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef; } else if (extra_partitions & XP_CASPER) { - type = 0x83; + DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83; } else if (extra_partitions & XP_COMPAT) { - type = RUFUS_EXTRA_PARTITION_TYPE; + DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = RUFUS_EXTRA_PARTITION_TYPE; // Set the one track compatibility partition to be all hidden sectors DriveLayoutEx.PartitionEntry[pn].Mbr.HiddenSectors = SelectedDrive.SectorsPerTrack; } else { assert(FALSE); } - DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = type; } // We need to write the UEFI:NTFS partition before we refresh the disk if (extra_partitions & XP_UEFI_NTFS) { - uprintf("Writing UEFI:NTFS partition..."); + uprintf("Writing %S data...", extra_part_name); if (!SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) { - uprintf("Unable to set position"); + uprintf("Could not set position"); return FALSE; } buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img", &bufsize, FALSE); if (buffer == NULL) { - uprintf("Could not access uefi-ntfs.img"); + uprintf("Could not access source image"); return FALSE; } if(!WriteFileWithRetry(hDrive, buffer, bufsize, &size, WRITE_RETRIES)) { @@ -1524,7 +1572,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m // Initialize the remaining partition data for (i = 0; i < pn; i++) { - DriveLayoutEx.PartitionEntry[i].PartitionNumber = i+1; + DriveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1; DriveLayoutEx.PartitionEntry[i].PartitionStyle = partition_style; DriveLayoutEx.PartitionEntry[i].RewritePartition = TRUE; } @@ -1552,14 +1600,14 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m CreateDisk.PartitionStyle = PARTITION_STYLE_GPT; IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId)); - CreateDisk.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; + CreateDisk.Gpt.MaxPartitionCount = MAX_PARTITIONS; DriveLayoutEx.PartitionStyle = PARTITION_STYLE_GPT; DriveLayoutEx.PartitionCount = pn; // At the very least, a GPT disk has 34 reserved sectors at the beginning and 33 at the end. DriveLayoutEx.Type.Gpt.StartingUsableOffset.QuadPart = 34 * SelectedDrive.SectorSize; DriveLayoutEx.Type.Gpt.UsableLength.QuadPart = SelectedDrive.DiskSize - (34+33) * SelectedDrive.SectorSize; - DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; + DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_PARTITIONS; DriveLayoutEx.Type.Gpt.DiskId = CreateDisk.Gpt.DiskId; break; } diff --git a/src/drive.h b/src/drive.h index 098c2138..e3859583 100644 --- a/src/drive.h +++ b/src/drive.h @@ -33,13 +33,27 @@ CTL_CODE(MOUNTMGRCONTROLTYPE, 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define XP_MSR 0x01 -#define XP_EFI 0x02 +#define XP_ESP 0x02 #define XP_UEFI_NTFS 0x04 #define XP_COMPAT 0x08 #define XP_CASPER 0x10 +#define PI_MAIN 0 +#define PI_ESP 1 +#define PI_CASPER 2 + +// The following should match VDS_FSOF_FLAGS as much as possible +#define FP_FORCE 0x00000001 +#define FP_QUICK 0x00000002 +#define FP_COMPRESSION 0x00000004 +#define FP_DUPLICATE_METADATA 0x00000008 +#define FP_LARGE_FAT32 0x00010000 +#define FP_NO_BOOT 0x00020000 + #define FILE_FLOPPY_DISKETTE 0x00000004 +#define VDS_SET_ERROR(hr) do { if (hr == S_FALSE) hr = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; FormatStatus = hr; SetLastError(hr); } while(0) + #if !defined(__MINGW32__) typedef enum _FSINFOCLASS { FileFsVolumeInformation = 1, @@ -87,6 +101,25 @@ typedef interface IVdsDisk IVdsDisk; typedef interface IVdsAdvancedDisk IVdsAdvancedDisk; typedef interface IVdsAdviseSink IVdsAdviseSink; typedef interface IVdsAsync IVdsAsync; +typedef interface IVdsVolume IVdsVolume; +typedef interface IVdsVolumeMF3 IVdsVolumeMF3; + +extern const IID CLSID_VdsLoader; +extern const IID IID_IVdsServiceLoader; +extern const IID IID_IVdsProvider; +extern const IID IID_IVdsSwProvider; +extern const IID IID_IVdsPack; +extern const IID IID_IVdsDisk; +extern const IID IID_IVdsAdvancedDisk; +extern const IID IID_IVdsVolume; +extern const IID IID_IVdsVolumeMF3; + +#ifndef VDS_S_PROPERTIES_INCOMPLETE +#define VDS_S_PROPERTIES_INCOMPLETE ((HRESULT)0x00042715L) +#endif +#ifndef VDS_E_OPERATION_PENDING +#define VDS_E_OPERATION_PENDING ((HRESULT)0x80042409L) +#endif typedef struct IVdsServiceLoaderVtbl { HRESULT(STDMETHODCALLTYPE *QueryInterface)(__RPC__in IVdsServiceLoader *This, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); @@ -212,21 +245,77 @@ interface IVdsAdvancedDisk { CONST_VTBL struct IVdsAdvancedDiskVtbl *lpVtbl; }; -#define IVdsServiceLoader_LoadService(This,pwszMachineName,ppService) (This)->lpVtbl->LoadService(This,pwszMachineName,ppService) +typedef struct IVdsVolumeVtbl { + HRESULT (STDMETHODCALLTYPE *QueryInterface)(__RPC__in IVdsVolume *This, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); + ULONG (STDMETHODCALLTYPE *AddRef)(__RPC__in IVdsVolume *This); + ULONG (STDMETHODCALLTYPE *Release)(__RPC__in IVdsVolume *This); + HRESULT (STDMETHODCALLTYPE *GetProperties)(__RPC__in IVdsVolume *This, __RPC__out VDS_VOLUME_PROP *pVolumeProperties); + HRESULT (STDMETHODCALLTYPE *GetPack)(__RPC__in IVdsVolume *This, __RPC__deref_out_opt IVdsPack **ppPack); + HRESULT (STDMETHODCALLTYPE *QueryPlexes)(__RPC__in IVdsVolume *This, __RPC__deref_out_opt IEnumVdsObject **ppEnum); + HRESULT (STDMETHODCALLTYPE *Extend)(__RPC__in IVdsVolume *This, __RPC__in_ecount_full_opt(lNumberOfDisks) VDS_INPUT_DISK *pInputDiskArray, LONG lNumberOfDisks, __RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *Shrink)(__RPC__in IVdsVolume *This, ULONGLONG ullNumberOfBytesToRemove, __RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *AddPlex)(__RPC__in IVdsVolume *This, VDS_OBJECT_ID VolumeId,__RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *BreakPlex)(__RPC__in IVdsVolume *This, VDS_OBJECT_ID plexId, __RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *RemovePlex)(__RPC__in IVdsVolume *This, VDS_OBJECT_ID plexId, __RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *Delete)(__RPC__in IVdsVolume *This, BOOL bForce); + HRESULT (STDMETHODCALLTYPE *SetFlags)(__RPC__in IVdsVolume *This, ULONG ulFlags, BOOL bRevertOnClose); + HRESULT (STDMETHODCALLTYPE *ClearFlags)(__RPC__in IVdsVolume *This, ULONG ulFlags); +} IVdsVolumeVtbl; +interface IVdsVolume { + CONST_VTBL struct IVdsVolumeVtbl *lpVtbl; +}; + +typedef struct IVdsVolumeMF3Vtbl { + HRESULT (STDMETHODCALLTYPE *QueryInterface)(__RPC__in IVdsVolumeMF3 *This, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); + ULONG (STDMETHODCALLTYPE *AddRef)(__RPC__in IVdsVolumeMF3 *This); + ULONG (STDMETHODCALLTYPE *Release)(__RPC__in IVdsVolumeMF3 *This); + HRESULT (STDMETHODCALLTYPE *QueryVolumeGuidPathnames)(__RPC__in IVdsVolumeMF3 *This, __RPC__deref_out_ecount_full_opt_string(*pulNumberOfPaths) LPWSTR **pwszPathArray, __RPC__out ULONG *pulNumberOfPaths); + HRESULT (STDMETHODCALLTYPE *FormatEx2)(__RPC__in IVdsVolumeMF3 *This, __RPC__in_opt_string LPWSTR pwszFileSystemTypeName, USHORT usFileSystemRevision, ULONG ulDesiredUnitAllocationSize, __RPC__in_opt_string LPWSTR pwszLabel, DWORD Options, __RPC__deref_out_opt IVdsAsync **ppAsync); + HRESULT (STDMETHODCALLTYPE *OfflineVolume)(__RPC__in IVdsVolumeMF3 *This); +} IVdsVolumeMF3Vtbl; +interface IVdsVolumeMF3 { + CONST_VTBL struct IVdsVolumeMF3Vtbl *lpVtbl; +}; + +typedef struct IVdsAsyncVtbl { + HRESULT (STDMETHODCALLTYPE *QueryInterface)(__RPC__in IVdsAsync *This, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); + ULONG (STDMETHODCALLTYPE *AddRef)(__RPC__in IVdsAsync *This); + ULONG (STDMETHODCALLTYPE *Release)(__RPC__in IVdsAsync *This); + HRESULT (STDMETHODCALLTYPE *Cancel)(__RPC__in IVdsAsync *This); + HRESULT (STDMETHODCALLTYPE *Wait)(__RPC__in IVdsAsync *This, __RPC__out HRESULT *pHrResult, __RPC__out VDS_ASYNC_OUTPUT *pAsyncOut); + HRESULT (STDMETHODCALLTYPE *QueryStatus)(__RPC__in IVdsAsync *This, __RPC__out HRESULT *pHrResult, __RPC__out ULONG *pulPercentCompleted); +} IVdsAsyncVtbl; +interface IVdsAsync { + CONST_VTBL struct IVdsAsyncVtbl *lpVtbl; +}; + +#define IVdsServiceLoader_LoadService(This, pwszMachineName, ppService) (This)->lpVtbl->LoadService(This, pwszMachineName, ppService) #define IVdsServiceLoader_Release(This) (This)->lpVtbl->Release(This) -#define IVdsService_QueryProviders(This,masks,ppEnum) (This)->lpVtbl->QueryProviders(This,masks,ppEnum) -#define IVdsSwProvider_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IVdsService_QueryProviders(This, masks, ppEnum) (This)->lpVtbl->QueryProviders(This, masks, ppEnum) +#define IVdsSwProvider_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) #define IVdsProvider_Release(This) (This)->lpVtbl->Release(This) -#define IVdsSwProvider_QueryPacks(This,ppEnum) (This)->lpVtbl->QueryPacks(This,ppEnum) +#define IVdsSwProvider_QueryPacks(This, ppEnum) (This)->lpVtbl->QueryPacks(This, ppEnum) #define IVdsSwProvider_Release(This) (This)->lpVtbl->Release(This) -#define IVdsPack_QueryDisks(This,ppEnum) (This)->lpVtbl->QueryDisks(This,ppEnum) -#define IVdsDisk_GetProperties(This,pDiskProperties) (This)->lpVtbl->GetProperties(This,pDiskProperties) +#define IVdsPack_QueryDisks(This, ppEnum) (This)->lpVtbl->QueryDisks(This, ppEnum) +#define IVdsDisk_GetProperties(This, pDiskProperties) (This)->lpVtbl->GetProperties(This, pDiskProperties) #define IVdsDisk_Release(This) (This)->lpVtbl->Release(This) -#define IVdsDisk_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) -#define IVdsAdvancedDisk_QueryPartitions(This,ppPartitionPropArray,plNumberOfPartitions) (This)->lpVtbl->QueryPartitions(This,ppPartitionPropArray,plNumberOfPartitions) -#define IVdsAdvancedDisk_DeletePartition(This,ullOffset,bForce,bForceProtected) (This)->lpVtbl->DeletePartition(This,ullOffset,bForce,bForceProtected) +#define IVdsDisk_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IVdsAdvancedDisk_QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) (This)->lpVtbl->QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) +#define IVdsAdvancedDisk_DeletePartition(This, ullOffset, bForce, bForceProtected) (This)->lpVtbl->DeletePartition(This, ullOffset, bForce, bForceProtected) +#define IVdsAdvancedDisk_Clean(This, bForce, bForceOEM, bFullClean, ppAsync) (This)->lpVtbl->Clean(This, bForce, bForceOEM, bFullClean, ppAsync) #define IVdsAdvancedDisk_Release(This) (This)->lpVtbl->Release(This) -#define IEnumVdsObject_Next(This,celt,ppObjectArray,pcFetched) (This)->lpVtbl->Next(This,celt,ppObjectArray,pcFetched) +#define IEnumVdsObject_Next(This, celt, ppObjectArray, pcFetched) (This)->lpVtbl->Next(This, celt, ppObjectArray, pcFetched) +#define IVdsPack_QueryVolumes(This, ppEnum) (This)->lpVtbl->QueryVolumes(This, ppEnum) +#define IVdsVolume_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IVdsVolume_Release(This) (This)->lpVtbl->Release(This) +#define IVdsVolumeMF3_QueryVolumeGuidPathnames(This, pwszPathArray, pulNumberOfPaths) (This)->lpVtbl->QueryVolumeGuidPathnames(This,pwszPathArray,pulNumberOfPaths) +#define IVdsVolumeMF3_FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) (This)->lpVtbl->FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) +#define IVdsVolumeMF3_Release(This) (This)->lpVtbl->Release(This) +#define IVdsVolume_GetProperties(This, pVolumeProperties) (This)->lpVtbl->GetProperties(This,pVolumeProperties) +#define IVdsAsync_Cancel(This) (This)->lpVtbl->Cancel(This) +#define IVdsAsync_QueryStatus(This,pHrResult,pulPercentCompleted) (This)->lpVtbl->QueryStatus(This,pHrResult,pulPercentCompleted) +#define IVdsAsync_Wait(This,pHrResult,pAsyncOut) (This)->lpVtbl->Wait(This,pHrResult,pAsyncOut) +#define IVdsAsync_Release(This) (This)->lpVtbl->Release(This) #endif static __inline BOOL UnlockDrive(HANDLE hDrive) { @@ -256,16 +345,17 @@ typedef struct { } RUFUS_DRIVE_INFO; extern RUFUS_DRIVE_INFO SelectedDrive; extern uint64_t persistence_size; +extern DWORD partition_index[3]; BOOL SetAutoMount(BOOL enable); BOOL GetAutoMount(BOOL* enabled); char* GetPhysicalName(DWORD DriveIndex); -char* GetPartitionName(DWORD DriveIndex, DWORD PartitionNumber); +char* GetPartitionName(DWORD DriveIndex, DWORD PartitionIndex); BOOL DeletePartitions(DWORD DriveIndex); HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare); -char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent); -BOOL WaitForLogical(DWORD DriveIndex); -HANDLE GetLogicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare); +char* GetLogicalName(DWORD DriveIndex, DWORD PartitionIndex, BOOL bKeepTrailingBackslash, BOOL bSilent); +BOOL WaitForLogical(DWORD DriveIndex, DWORD PartitionIndex); +HANDLE GetLogicalHandle(DWORD DriveIndex, DWORD PartitionIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare); int GetDriveNumber(HANDLE hDrive, char* path); BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters); UINT GetDriveTypeFromIndex(DWORD DriveIndex); @@ -279,7 +369,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys BOOL UnmountVolume(HANDLE hDrive); BOOL MountVolume(char* drive_name, char *drive_guid); BOOL AltUnmountVolume(const char* drive_name); -char* AltMountVolume(const char* drive_name, uint8_t part_nr); +char* AltMountVolume(DWORD DriveIndex, DWORD PartitionIndex); BOOL RemountVolume(char* drive_name); BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL mbr_uefi_marker, uint8_t extra_partitions); BOOL InitializeDisk(HANDLE hDrive); diff --git a/src/format.c b/src/format.c index f228c5a8..638e73f6 100644 --- a/src/format.c +++ b/src/format.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "rufus.h" #include "missing.h" @@ -55,6 +56,7 @@ /* * Globals */ +const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", "ReFS", "ext2", "ext3", "ext4" }; DWORD FormatStatus = 0, LastWriteError = 0; badblocks_report report = { 0 }; static uint64_t LastRefresh = 0; @@ -62,8 +64,9 @@ static float format_percent = 0.0f; static int task_number = 0; extern const int nb_steps[FS_MAX]; extern uint32_t dur_mins, dur_secs; -static int fs_index = 0, wintogo_index = -1, wininst_index = 0; +static int actual_fs_type, wintogo_index = -1, wininst_index = 0; extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing, write_as_image; +extern BOOL use_vds; uint8_t *grub2_buf = NULL; long grub2_len; static BOOL WritePBR(HANDLE hLogicalDrive); @@ -105,6 +108,8 @@ static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, if (IS_ERROR(FormatStatus)) return FALSE; + assert((actual_fs_type >= 0) && (actual_fs_type < FS_MAX)); + switch(Command) { case FCC_PROGRESS: percent = (DWORD*)pData; @@ -112,28 +117,23 @@ static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, UpdateProgress(OP_FORMAT, 1.0f * (*percent)); break; case FCC_STRUCTURE_PROGRESS: // No progress on quick format - if (task_number < nb_steps[fs_index] - 1) { + if (task_number < nb_steps[actual_fs_type] - 1) { if (task_number == 0) uprintf("Creating file system..."); - PrintInfo(0, MSG_218, ++task_number, nb_steps[fs_index]); - format_percent += 100.0f / (1.0f * nb_steps[fs_index]); + PrintInfo(0, MSG_218, ++task_number, nb_steps[actual_fs_type]); + format_percent += 100.0f / (1.0f * nb_steps[actual_fs_type]); UpdateProgress(OP_CREATE_FS, format_percent); } break; case FCC_DONE: - PrintInfo(0, MSG_218, nb_steps[fs_index], nb_steps[fs_index]); + PrintInfo(0, MSG_218, nb_steps[actual_fs_type], nb_steps[actual_fs_type]); UpdateProgress(OP_CREATE_FS, 100.0f); if(*(BOOLEAN*)pData == FALSE) { uprintf("Error while formatting"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; } break; - case FCC_DONE_WITH_STRUCTURE: // We get this message when formatting Small FAT16 - // pData Seems to be a struct with at least one (32 BIT!!!) string pointer to the size in MB - // uprintf("Done with that sort of thing: Action=%d pData=%0p\n", Action, pData); - // /!\ THE FOLLOWING ONLY WORKS ON VISTA OR LATER - DO NOT ENABLE ON XP! - // DumpBufferHex(pData, 8); - // uprintf("Volume size: %s MB\n", (char*)(LONG_PTR)(*(ULONG32*)pData)); + case FCC_DONE_WITH_STRUCTURE: break; case FCC_INCOMPATIBLE_FILE_SYSTEM: uprintf("Incompatible File System"); @@ -243,61 +243,71 @@ static BOOLEAN __stdcall ChkdskCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DW } /* - * Converts an UTF-16 label to a valid FAT/NTFS one + * Converts an UTF-8 label to a valid FAT/NTFS one */ -static void ToValidLabel(WCHAR* name, BOOL bFAT) +static void ToValidLabel(char* Label, BOOL bFAT) { size_t i, j, k; BOOL found; WCHAR unauthorized[] = L"*?,;:/\\|+=<>[]\""; WCHAR to_underscore[] = L"\t."; + WCHAR *wLabel = utf8_to_wchar(Label); - if (name == NULL) + if (wLabel == NULL) return; - for (i=0, k=0; i= 0x80) { - name[k++] = '_'; + if (wLabel[i] >= 0x80) { + wLabel[k++] = L'_'; found = TRUE; } - if (found) continue; + if (found) + continue; } found = FALSE; - for (j=0; j 11) + wLabel[11] = 0; + for (i = 0, j = 0; wLabel[i] != 0 ; i++) + if (wLabel[i] == '_') + j++; + if (i < 2*j) { // If the final label is mostly underscore, use the proposed label - uprintf("FAT label is mostly underscores. Using '%s' label instead.\n", SelectedDrive.proposed_label); - for(i=0; SelectedDrive.proposed_label[i]!=0; i++) - name[i] = SelectedDrive.proposed_label[i]; - name[i] = 0; + uprintf("FAT label is mostly underscores. Using '%s' label instead.", SelectedDrive.proposed_label); + for(i = 0; SelectedDrive.proposed_label[i] != 0; i++) + wLabel[i] = SelectedDrive.proposed_label[i]; + wLabel[i] = 0; } - } else { - name[32] = 0; + } else if (wcslen(wLabel) > 32) { + wLabel[32] = 0; } // Needed for disk by label isolinux.cfg workaround - wchar_to_utf8_no_alloc(name, img_report.usb_label, sizeof(img_report.usb_label)); + wchar_to_utf8_no_alloc(wLabel, img_report.usb_label, sizeof(img_report.usb_label)); + safe_strcpy(Label, strlen(Label), img_report.usb_label); + free(wLabel); } /* @@ -361,11 +371,11 @@ static DWORD GetFATSizeSectors(DWORD DskSize, DWORD ReservedSecCnt, DWORD SecPer * Large FAT32 volume formatting from fat32format by Tom Thornhill * http://www.ridgecrop.demon.co.uk/index.htm?fat32format.htm */ -static BOOL FormatFAT32(DWORD DriveIndex) +static BOOL FormatFAT32(DWORD DriveIndex, DWORD PartitionIndex, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) { BOOL r = FALSE; DWORD i; - HANDLE hLogicalVolume; + HANDLE hLogicalVolume = NULL; DWORD cbRet; DISK_GEOMETRY dgDrive; BYTE geometry_ex[256]; // DISK_GEOMETRY_EX is variable size @@ -378,13 +388,11 @@ static BOOL FormatFAT32(DWORD DriveIndex) DWORD BackupBootSect = 6; DWORD VolumeId = 0; // calculated before format char* VolumeName = NULL; - WCHAR wLabel[64], *wVolumeName = NULL; DWORD BurstSize = 128; // Zero in blocks of 64K typically // Calculated later DWORD FatSize = 0; DWORD BytesPerSect = 0; - DWORD ClusterSize = 0; DWORD SectorsPerCluster = 0; DWORD TotalSectors = 0; DWORD SystemAreaSize = 0; @@ -401,15 +409,20 @@ static BOOL FormatFAT32(DWORD DriveIndex) // Debug temp vars ULONGLONG FatNeeded, ClusterCount; + if (safe_strncmp(FSName, "FAT", 3) != 0) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; + goto out; + } PrintInfoDebug(0, MSG_222, "Large FAT32"); LastRefresh = 0; VolumeId = GetVolumeID(); // Open the drive and lock it - hLogicalVolume = GetLogicalHandle(DriveIndex, TRUE, TRUE, FALSE); - if (IS_ERROR(FormatStatus)) goto out; + hLogicalVolume = GetLogicalHandle(DriveIndex, PartitionIndex, TRUE, TRUE, FALSE); + if (IS_ERROR(FormatStatus)) + goto out; if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) - die("Invalid logical volume handle\n", ERROR_INVALID_HANDLE); + die("Invalid logical volume handle", ERROR_INVALID_HANDLE); // Try to disappear the volume while we're formatting it UnmountVolume(hLogicalVolume); @@ -419,8 +432,8 @@ static BOOL FormatFAT32(DWORD DriveIndex) sizeof(dgDrive), &cbRet, NULL)) { if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, xdgDrive, sizeof(geometry_ex), &cbRet, NULL)) { - uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY error: %s\n", WindowsErrorString()); - die("Failed to get device geometry (both regular and _ex)\n", ERROR_NOT_SUPPORTED); + uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY error: %s", WindowsErrorString()); + die("Failed to get device geometry (both regular and _ex)", ERROR_NOT_SUPPORTED); } memcpy(&dgDrive, &xdgDrive->Geometry, sizeof(dgDrive)); } @@ -431,8 +444,8 @@ static BOOL FormatFAT32(DWORD DriveIndex) sizeof(piDrive), &cbRet, NULL)) { if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &xpiDrive, sizeof(xpiDrive), &cbRet, NULL)) { - uprintf("IOCTL_DISK_GET_PARTITION_INFO error: %s\n", WindowsErrorString()); - die("Failed to get partition info (both regular and _ex)\n", ERROR_NOT_SUPPORTED); + uprintf("IOCTL_DISK_GET_PARTITION_INFO error: %s", WindowsErrorString()); + die("Failed to get partition info (both regular and _ex)", ERROR_NOT_SUPPORTED); } memset(&piDrive, 0, sizeof(piDrive)); @@ -450,7 +463,7 @@ static BOOL FormatFAT32(DWORD DriveIndex) if (qTotalSectors < 65536) { // Most FAT32 implementations would probably mount this volume just fine, // but the spec says that we shouldn't do this, so we won't - die("This drive is too small for FAT32 - there must be at least 64K clusters\n", APPERR(ERROR_INVALID_CLUSTER_SIZE)); + die("This drive is too small for FAT32 - there must be at least 64K clusters", APPERR(ERROR_INVALID_CLUSTER_SIZE)); } if (qTotalSectors >= 0xffffffff) { @@ -459,7 +472,7 @@ static BOOL FormatFAT32(DWORD DriveIndex) // There would need to be an extra field in the FSInfo sector, and the old sector count could // be set to 0xffffffff. This is non standard though, the Windows FAT driver FASTFAT.SYS won't // understand this. Perhaps a future version of FAT32 and FASTFAT will handle this. - die("This drive is too big for FAT32 - max 2TB supported\n", APPERR(ERROR_INVALID_VOLUME_SIZE)); + die("This drive is too big for FAT32 - max 2TB supported", APPERR(ERROR_INVALID_VOLUME_SIZE)); } // coverity[tainted_data] @@ -467,7 +480,7 @@ static BOOL FormatFAT32(DWORD DriveIndex) pFAT32FsInfo = (FAT_FSINFO*) calloc(BytesPerSect, 1); pFirstSectOfFat = (DWORD*) calloc(BytesPerSect, 1); if (!pFAT32BootSect || !pFAT32FsInfo || !pFirstSectOfFat) { - die("Failed to allocate memory\n", ERROR_NOT_ENOUGH_MEMORY); + die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); } // fill out the boot sector and fs info @@ -476,8 +489,6 @@ static BOOL FormatFAT32(DWORD DriveIndex) pFAT32BootSect->sJmpBoot[2]=0x90; memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8); pFAT32BootSect->wBytsPerSec = (WORD) BytesPerSect; - - ClusterSize = (DWORD)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)); SectorsPerCluster = ClusterSize / BytesPerSect; pFAT32BootSect->bSecPerClus = (BYTE) SectorsPerCluster ; @@ -558,13 +569,13 @@ static BOOL FormatFAT32(DWORD DriveIndex) // Sanity check for a cluster count of >2^28, since the upper 4 bits of the cluster values in // the FAT are reserved. if (ClusterCount > 0x0FFFFFFF) { - die("This drive has more than 2^28 clusters, try to specify a larger cluster size or use the default\n", + die("This drive has more than 2^28 clusters, try to specify a larger cluster size or use the default", ERROR_INVALID_CLUSTER_SIZE); } // Sanity check - < 64K clusters means that the volume will be misdetected as FAT16 if (ClusterCount < 65536) { - die("FAT32 must have at least 65536 clusters, try to specify a smaller cluster size or use the default\n", + die("FAT32 must have at least 65536 clusters, try to specify a smaller cluster size or use the default", ERROR_INVALID_CLUSTER_SIZE); } @@ -575,42 +586,42 @@ static BOOL FormatFAT32(DWORD DriveIndex) FatNeeded += (BytesPerSect-1); FatNeeded /= BytesPerSect; if (FatNeeded > FatSize) { - die("This drive is too big for large FAT32 format\n", APPERR(ERROR_INVALID_VOLUME_SIZE)); + die("This drive is too big for large FAT32 format", APPERR(ERROR_INVALID_VOLUME_SIZE)); } // Now we're committed - print some info first - uprintf("Size : %s %u sectors\n", SizeToHumanReadable(piDrive.PartitionLength.QuadPart, TRUE, FALSE), TotalSectors); - uprintf("Cluster size %d bytes, %d Bytes Per Sector\n", SectorsPerCluster*BytesPerSect, BytesPerSect); - uprintf("Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff); - uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs\n", ReservedSectCount, FatSize, NumFATs); - uprintf("%d Total clusters\n", ClusterCount); + uprintf("Size : %s %u sectors", SizeToHumanReadable(piDrive.PartitionLength.QuadPart, TRUE, FALSE), TotalSectors); + uprintf("Cluster size %d bytes, %d Bytes Per Sector", SectorsPerCluster*BytesPerSect, BytesPerSect); + uprintf("Volume ID is %x:%x", VolumeId>>16, VolumeId&0xffff); + uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs", ReservedSectCount, FatSize, NumFATs); + uprintf("%d Total clusters", ClusterCount); // Fix up the FSInfo sector pFAT32FsInfo->dFree_Count = (UserAreaSize/SectorsPerCluster) - 1; pFAT32FsInfo->dNxt_Free = 3; // clusters 0-1 reserved, we used cluster 2 for the root dir - uprintf("%d Free Clusters\n", pFAT32FsInfo->dFree_Count); + uprintf("%d Free Clusters", pFAT32FsInfo->dFree_Count); // Work out the Cluster count // First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster SystemAreaSize = ReservedSectCount + (NumFATs*FatSize) + SectorsPerCluster; - uprintf("Clearing out %d sectors for reserved sectors, FATs and root cluster...\n", SystemAreaSize); + uprintf("Clearing out %d sectors for reserved sectors, FATs and root cluster...", SystemAreaSize); // Not the most effective, but easy on RAM pZeroSect = (BYTE*)calloc(BytesPerSect, BurstSize); if (!pZeroSect) { - die("Failed to allocate memory\n", ERROR_NOT_ENOUGH_MEMORY); + die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); } for (i=0; i<(SystemAreaSize+BurstSize-1); i+=BurstSize) { UPDATE_PERCENT(MSG_217, (100.0f*i) / (1.0f*(SystemAreaSize + BurstSize))); CHECK_FOR_USER_CANCEL; if (write_sectors(hLogicalVolume, BytesPerSect, i, BurstSize, pZeroSect) != (BytesPerSect*BurstSize)) { - die("Error clearing reserved sectors\n", ERROR_WRITE_FAULT); + die("Error clearing reserved sectors", ERROR_WRITE_FAULT); } } - uprintf ("Initializing reserved sectors and FATs...\n"); + uprintf ("Initializing reserved sectors and FATs..."); // Now we should write the boot sector and fsinfo twice, once at 0 and once at the backup boot sect position for (i=0; i<2; i++) { int SectorStart = (i==0) ? 0 : BackupBootSect; @@ -621,36 +632,34 @@ static BOOL FormatFAT32(DWORD DriveIndex) // Write the first fat sector in the right places for ( i=0; i 3)) { - if (version == 4) - uprintf("ext4 file system is not supported, will use ext3 instead"); - else if (version != 0) - uprintf("invalid ext file system version requested, will use ext3"); - version = 3; } - extfs_name[3] = '0' + version; - PrintInfoDebug(0, MSG_222, extfs_name); + if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) { + if (strcmp(FSName, FileSystemLabel[FS_EXT4]) == 0) + uprintf("ext4 file system is not supported, will use ext3 instead"); + else + uprintf("invalid ext file system version requested, will use ext3"); + } + + if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) + FSName = FileSystemLabel[FS_EXT3]; + + PrintInfoDebug(0, MSG_222, FSName); LastRefresh = 0; // Figure out the volume size and block size - r = ext2fs_get_device_size2(path, KB, &size); + r = ext2fs_get_device_size2(volume_name, KB, &size); if ((r != 0) || (size == 0)) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; uprintf("Could not read device size: %s", error_message(r)); goto out; } @@ -873,9 +887,12 @@ BOOL FormatExtFs(const char* label, uint32_t version) break; } assert(i < ARRAYSIZE(ext2fs_default)); - size /= ext2fs_default[i].block_size; + // NB: We validated that BlockSize is a power of two in FormatPartition() + if (BlockSize == 0) + BlockSize = ext2fs_default[i].block_size; + size /= BlockSize; for (features.s_log_block_size = 0; EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE; features.s_log_block_size++) { - if (EXT2_BLOCK_SIZE(&features) == ext2fs_default[i].block_size) + if (EXT2_BLOCK_SIZE(&features) == BlockSize) break; } assert(EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE); @@ -897,15 +914,16 @@ BOOL FormatExtFs(const char* label, uint32_t version) ext2fs_set_feature_filetype(&features); ext2fs_set_feature_sparse_super(&features); ext2fs_set_feature_large_file(&features); - if (version >= 3) + if (FSName[3] != '2') ext2fs_set_feature_journal(&features); features.s_backup_bgs[0] = ~0; features.s_default_mount_opts = EXT2_DEFM_XATTR_USER | EXT2_DEFM_ACL; // Now that we have set our base features, initialize a virtual superblock - r = ext2fs_initialize(path, EXT2_FLAG_EXCLUSIVE | EXT2_FLAG_64BITS, &features, manager, &ext2fs); + r = ext2fs_initialize(volume_name, EXT2_FLAG_EXCLUSIVE | EXT2_FLAG_64BITS, &features, manager, &ext2fs); if (r != 0) { - uprintf("Could not initialize %s features: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_DATA; + uprintf("Could not initialize %s features: %s", FSName, error_message(r)); goto out; } @@ -915,7 +933,8 @@ BOOL FormatExtFs(const char* label, uint32_t version) r = io_channel_write_blk64(ext2fs->io, 0, 16, buf); safe_free(buf); if (r != 0) { - uprintf("Could not zero %s superblock area: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + uprintf("Could not zero %s superblock area: %s", FSName, error_message(r)); goto out; } @@ -927,21 +946,23 @@ BOOL FormatExtFs(const char* label, uint32_t version) ext2fs->super->s_max_mnt_count = -1; ext2fs->super->s_creator_os = EXT2_OS_WINDOWS; ext2fs->super->s_errors = EXT2_ERRORS_CONTINUE; - static_strcpy(ext2fs->super->s_volume_name, label); + if (Label != NULL) + static_strcpy(ext2fs->super->s_volume_name, Label); r = ext2fs_allocate_tables(ext2fs); if (r != 0) { - uprintf("Could not allocate %s tables: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_DATA; + uprintf("Could not allocate %s tables: %s", FSName, error_message(r)); goto out; } r = ext2fs_convert_subcluster_bitmap(ext2fs, &ext2fs->block_map); if (r != 0) { - uprintf("Could set %s cluster bitmap: %s", extfs_name, error_message(r)); + uprintf("Could set %s cluster bitmap: %s", FSName, error_message(r)); goto out; } ext2_percent_start = 0.0f; - ext2_percent_share = (version < 3) ? 100.0f : 50.0f; + ext2_percent_share = (FSName[3] == '2') ? 100.0f : 50.0f; uprintf("Creating %d inode sets: [1 marker = %0.1f set(s)]", ext2fs->group_desc_count, max((float)ext2fs->group_desc_count / ext2_max_marker, 1.0f)); for (i = 0; i < (int)ext2fs->group_desc_count; i++) { @@ -952,6 +973,7 @@ BOOL FormatExtFs(const char* label, uint32_t version) * EXT2_BLOCK_SIZE(ext2fs->super), EXT2_BLOCK_SIZE(ext2fs->super)); r = ext2fs_zero_blocks2(ext2fs, cur, count, &cur, &count); if (r != 0) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; uprintf("\r\nCould not zero inode set at position %llu (%d blocks): %s", cur, count, error_message(r)); goto out; } @@ -961,13 +983,15 @@ BOOL FormatExtFs(const char* label, uint32_t version) // Create root and lost+found dirs r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); if (r != 0) { - uprintf("Failed to create %s root dir: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_DIR_NOT_ROOT; + uprintf("Failed to create %s root dir: %s", FSName, error_message(r)); goto out; } ext2fs->umask = 077; r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, 0, "lost+found"); if (r != 0) { - uprintf("Failed to create %s 'lost+found' dir: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_DIR_NOT_ROOT; + uprintf("Failed to create %s 'lost+found' dir: %s", FSName, error_message(r)); goto out; } @@ -978,17 +1002,19 @@ BOOL FormatExtFs(const char* label, uint32_t version) r = ext2fs_mark_inode_bitmap2(ext2fs->inode_map, EXT2_BAD_INO); if (r != 0) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; uprintf("Could not set inode bitmaps: %s", error_message(r)); goto out; } ext2fs_inode_alloc_stats(ext2fs, EXT2_BAD_INO, 1); r = ext2fs_update_bb_inode(ext2fs, NULL); if (r != 0) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; uprintf("Could not set inode stats: %s", error_message(r)); goto out; } - if (version >= 3) { + if (FSName[3] != '2') { // Create the journal ext2_percent_start = 50.0f; journal_size = ext2fs_default_journal_size(ext2fs_blocks_count(ext2fs->super)); @@ -996,10 +1022,11 @@ BOOL FormatExtFs(const char* label, uint32_t version) uprintf("Creating %d journal blocks: [1 marker = %0.1f block(s)]", journal_size, max((float)journal_size / ext2_max_marker, 1.0f)); // Even with EXT2_MKJOURNAL_LAZYINIT, this call is absolutely dreadful in terms of speed... - r = ext2fs_add_journal_inode(ext2fs, journal_size, EXT2_MKJOURNAL_NO_MNT_CHECK | EXT2_MKJOURNAL_LAZYINIT); + r = ext2fs_add_journal_inode(ext2fs, journal_size, EXT2_MKJOURNAL_NO_MNT_CHECK | ((Flags & FP_QUICK) ? EXT2_MKJOURNAL_LAZYINIT : 0)); uprintfs("\r\n"); if (r != 0) { - uprintf("Could not create %s journal: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + uprintf("Could not create %s journal: %s", FSName, error_message(r)); goto out; } } @@ -1007,7 +1034,8 @@ BOOL FormatExtFs(const char* label, uint32_t version) // Finally we can call close() to get the file system gets created r = ext2fs_close(ext2fs); if (r != 0) { - uprintf("Could not create %s volume: %s", extfs_name, error_message(r)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + uprintf("Could not create %s volume: %s", FSName, error_message(r)); goto out; } UPDATE_PERCENT(MSG_217, 100.0f); @@ -1017,42 +1045,263 @@ BOOL FormatExtFs(const char* label, uint32_t version) out: ext2fs_free(ext2fs); free(buf); - free(path); + free(volume_name); return ret; } +/* + * Call on VDS to format a partition + */ +static BOOL FormatDriveVds(DWORD DriveIndex, DWORD PartitionIndex, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) +{ + BOOL r = FALSE; + HRESULT hr; + ULONG ulFetched; + IVdsServiceLoader *pLoader; + IVdsService *pService; + IEnumVdsObject *pEnum; + IUnknown *pUnk; + char* VolumeName = NULL; + WCHAR *wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName); + + if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) { + PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs); + } else { + PrintInfoDebug(0, MSG_222, FSName); + } + LastRefresh = 0; + VolumeName = GetLogicalName(DriveIndex, PartitionIndex, TRUE, TRUE); + wVolumeName = utf8_to_wchar(VolumeName); + if (wVolumeName == NULL) { + uprintf("Could not read volume name"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_GEN_FAILURE; + goto out; + } + + // Initialize COM + IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); + IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL)); + + // Create a VDS Loader Instance + hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, + &IID_IVdsServiceLoader, (void **)&pLoader); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString()); + goto out; + } + + // Load the VDS Service + hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); + IVdsServiceLoader_Release(pLoader); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not load VDS Service: %s", WindowsErrorString()); + goto out; + } + + // Query the VDS Service Providers + hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Service Providers: %s", WindowsErrorString()); + goto out; + } + + while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { + IVdsProvider *pProvider; + IVdsSwProvider *pSwProvider; + IEnumVdsObject *pEnumPack; + IUnknown *pPackUnk; + CHECK_FOR_USER_CANCEL; + + // Get VDS Provider + hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider); + IUnknown_Release(pUnk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Provider: %s", WindowsErrorString()); + goto out; + } + + // Get VDS Software Provider + hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider); + IVdsProvider_Release(pProvider); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Software Provider: %s", WindowsErrorString()); + goto out; + } + + // Get VDS Software Provider Packs + hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); + IVdsSwProvider_Release(pSwProvider); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not get VDS Software Provider Packs: %s", WindowsErrorString()); + goto out; + } + + // Enumerate Provider Packs + while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) { + IVdsPack *pPack; + IEnumVdsObject *pEnumVolume; + IUnknown *pVolumeUnk; + CHECK_FOR_USER_CANCEL; + + hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack); + IUnknown_Release(pPackUnk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Software Provider Pack: %s", WindowsErrorString()); + goto out; + } + + // Use the pack interface to access the volumes + hr = IVdsPack_QueryVolumes(pPack, &pEnumVolume); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not query VDS volumes: %s", WindowsErrorString()); + goto out; + } + + // List volumes + while (IEnumVdsObject_Next(pEnumVolume, 1, &pVolumeUnk, &ulFetched) == S_OK) { + BOOL match; + HRESULT hr2 = E_FAIL; + VDS_VOLUME_PROP VolumeProps; + LPWSTR *wszPathArray; + ULONG ulPercentCompleted, ulNumberOfPaths; + USHORT usFsVersion = 0; + IVdsVolume *pVolume; + IVdsAsync* pAsync; + IVdsVolumeMF3 *pVolumeMF3; + CHECK_FOR_USER_CANCEL; + + // Get the volume interface. + hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVdsVolume, (void **)&pVolume); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not query VDS Volume Interface: %s", WindowsErrorString()); + goto out; + } + + hr = IVdsVolume_GetProperties(pVolume, &VolumeProps); + if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) { + VDS_SET_ERROR(hr); + IVdsVolume_Release(pVolume); + uprintf("Could not query VDS Volume Properties: %s", WindowsErrorString()); + continue; + } + CoTaskMemFree(VolumeProps.pwszName); + + // Instantiate the IVdsVolumeMF3 interface for our volume. + hr = IVdsVolume_QueryInterface(pVolume, &IID_IVdsVolumeMF3, (void **)&pVolumeMF3); + IVdsVolume_Release(pVolume); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not access VDS VolumeMF3 interface: %s", WindowsErrorString()); + continue; + } + + // Query the volume GUID + hr = IVdsVolumeMF3_QueryVolumeGuidPathnames(pVolumeMF3, &wszPathArray, &ulNumberOfPaths); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + uprintf("Could not query VDS VolumeGuidPathnames: %s", WindowsErrorString()); + continue; + } + + if (ulNumberOfPaths > 1) + uprintf("Notice: Volume %S has more than one GUID...", wszPathArray[0]); + + match = (wcscmp(wVolumeName, wszPathArray[0]) == 0); + CoTaskMemFree(wszPathArray); + if (!match) + continue; + + if (strcmp(Label, FileSystemLabel[FS_UDF]) == 0) + usFsVersion = ReadSetting32(SETTING_USE_UDF_VERSION); + if (ClusterSize < 0x200) { + ClusterSize = 0; + uprintf("Using default cluster size"); + } else { + uprintf("Using cluster size: %d bytes", ClusterSize); + } + format_percent = 0.0f; + uprintf("%s format was selected", (Flags & FP_QUICK) ? "Quick" : "Slow"); + if (Flags & FP_COMPRESSION) + uprintf("NTFS compression is enabled"); + + hr = IVdsVolumeMF3_FormatEx2(pVolumeMF3, wFSName, usFsVersion, ClusterSize, wLabel, Flags, &pAsync); + while (SUCCEEDED(hr)) { + if (IS_ERROR(FormatStatus)) { + IVdsAsync_Cancel(pAsync); + break; + } + hr = IVdsAsync_QueryStatus(pAsync, &hr2, &ulPercentCompleted); + if (SUCCEEDED(hr)) { + if (Flags & FP_QUICK) { + // Progress report on quick format is useless, so we'll just pretend we have 2 tasks + PrintInfo(0, MSG_218, (ulPercentCompleted < 100) ? 1 : 2, 2); + UpdateProgress(OP_CREATE_FS, (float)ulPercentCompleted); + } else { + PrintInfo(0, MSG_217, (float)ulPercentCompleted); + UpdateProgress(OP_FORMAT, (float)ulPercentCompleted); + } + hr = hr2; + if (hr == S_OK) + break; + if (hr == VDS_E_OPERATION_PENDING) + hr = S_OK; + } + Sleep(500); + } + if (!SUCCEEDED(hr)) { + VDS_SET_ERROR(hr); + uprintf("Could not format drive: %s", WindowsErrorString()); + goto out; + } + + IVdsAsync_Release(pAsync); + IVdsVolumeMF3_Release(pVolumeMF3); + + if (!IS_ERROR(FormatStatus)) { + uprintf("Format completed."); + r = TRUE; + } + goto out; + } + } + } + +out: + safe_free(VolumeName); + safe_free(wVolumeName); + safe_free(wLabel); + safe_free(wFSName); + return r; +} + /* * Call on fmifs.dll's FormatEx() to format the drive */ -static BOOL FormatDrive(DWORD DriveIndex) +static BOOL FormatDrive(DWORD DriveIndex, DWORD PartitionIndex, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) { BOOL r = FALSE; PF_DECL(FormatEx); PF_DECL(EnableVolumeCompression); - char FSType[64], path[MAX_PATH]; char *locale, *VolumeName = NULL; - WCHAR* wVolumeName = NULL; - WCHAR wFSType[64]; - WCHAR wLabel[64]; - ULONG ulClusterSize; - size_t i, index; + WCHAR* wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName); + size_t i; - GetWindowTextU(hFileSystem, FSType, ARRAYSIZE(FSType)); - // Skip the RIGHT_TO_LEFT_EMBEDDING mark from LTR languages - index = (strncmp(FSType, RIGHT_TO_LEFT_EMBEDDING, sizeof(RIGHT_TO_LEFT_EMBEDDING) - 1) == 0) ? (sizeof(RIGHT_TO_LEFT_EMBEDDING) - 1) : 0; - // Might have a (Default) suffix => remove it - for (i=strlen(FSType); i>2; i--) { - if (FSType[i] == '(') { - FSType[i-1] = 0; - break; - } - } - if ((fs_type == FS_UDF) && !((dur_mins == 0) && (dur_secs == 0))) { - PrintInfoDebug(0, MSG_220, &FSType[index], dur_mins, dur_secs); + if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) { + PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs); } else { - PrintInfoDebug(0, MSG_222, &FSType[index]); + PrintInfoDebug(0, MSG_222, FSName); } - VolumeName = GetLogicalName(DriveIndex, TRUE, TRUE); + VolumeName = GetLogicalName(DriveIndex, PartitionIndex, TRUE, TRUE); wVolumeName = utf8_to_wchar(VolumeName); if (wVolumeName == NULL) { uprintf("Could not read volume name\n"); @@ -1063,16 +1312,6 @@ static BOOL FormatDrive(DWORD DriveIndex) // a trailing backslash, but EnableCompression() fails without... wVolumeName[wcslen(wVolumeName)-1] = 0; // Remove trailing backslash - // Check if Windows picked the UEFI:NTFS partition - // NB: No need to do this for Large FAT32, as this only applies to NTFS - static_strcpy(path, VolumeName); - static_strcat(path, "EFI\\Rufus\\ntfs_x64.efi"); - if (PathFileExistsA(path)) { - uprintf("Windows selected the UEFI:NTFS partition for formatting - Retry needed", VolumeName); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_RETRY; - goto out; - } - // LoadLibrary("fmifs.dll") appears to changes the locale, which can lead to // problems with tolower(). Make sure we restore the locale. For more details, // see http://comments.gmane.org/gmane.comp.gnu.mingw.user/39300 @@ -1081,46 +1320,39 @@ static BOOL FormatDrive(DWORD DriveIndex) PF_INIT(EnableVolumeCompression, fmifs); setlocale(LC_ALL, locale); - GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType)); - // Again, skip the RIGHT_TO_LEFT_EMBEDDING mark if present - index = (wFSType[0] == 0x202b) ? 1 : 0; - // We may have a " (Default)" trail - for (i=0; i 0x100) || (PartitionIndex >= MAX_PARTITIONS) || (FSType >= FS_MAX) || + // The following validates that UnitAllocationSize is a power of 2 + ((UnitAllocationSize != 0) && (UnitAllocationSize & (UnitAllocationSize - 1)))) { + ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; + return FALSE; + } + actual_fs_type = FSType; + if ((FSType == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32) || (Flags & FP_LARGE_FAT32))) + return FormatFAT32(DriveIndex, PartitionIndex, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags); + else if (FSType >= FS_EXT2) + return FormatExtFs(DriveIndex, PartitionIndex, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags); + else if (use_vds) + return FormatDriveVds(DriveIndex, PartitionIndex, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags); + else + return FormatDrive(DriveIndex, PartitionIndex, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags); +} + /* * Call on fmifs.dll's Chkdsk() to fixup the filesystem */ @@ -1457,19 +1710,19 @@ static BOOL WritePBR(HANDLE hLogicalVolume) int i; FAKE_FD fake_fd = { 0 }; FILE* fp = (FILE*)&fake_fd; - const char* using_msg = "Using %s %s partition boot record\n"; + const char* using_msg = "Using %s %s partition boot record"; fake_fd._handle = (char*)hLogicalVolume; set_bytes_per_sector(SelectedDrive.SectorSize); - switch (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))) { + switch (actual_fs_type) { case FS_FAT16: uprintf(using_msg, bt_to_name(), "FAT16"); if (!is_fat_16_fs(fp)) { - uprintf("New volume does not have a FAT16 boot sector - aborting\n"); + uprintf("New volume does not have a FAT16 boot sector - aborting"); break; } - uprintf("Confirmed new volume has a FAT16 boot sector\n"); + uprintf("Confirmed new volume has a FAT16 boot sector"); if (boot_type == BT_FREEDOS) { if (!write_fat_16_fd_br(fp, 0)) break; } else if (boot_type == BT_REACTOS) { @@ -1786,11 +2039,9 @@ out: // http://technet.microsoft.com/en-ie/library/jj721578.aspx // As opposed to the technet guide above, we no longer set internal drives offline, -// due to people wondering why they can't see them by default, and also due to dism -// incompatibilities from one version of Windows to the next. -// Maybe when we use wimlib we'll review this, but for now just turn it off. +// due to people wondering why they can't see them by default. //#define SET_INTERNAL_DRIVES_OFFLINE -static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) +static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp) { #ifdef SET_INTERNAL_DRIVES_OFFLINE static char san_policy_path[] = "?:\\san_policy.xml"; @@ -1798,16 +2049,13 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) static char unattend_path[] = "?:\\Windows\\System32\\sysprep\\unattend.xml"; char *mounted_iso, *ms_efi = NULL, image[128], cmd[MAX_PATH]; unsigned char *buffer; - wchar_t wVolumeName[] = L"?:"; DWORD bufsize; ULONG cluster_size; FILE* fd; - PF_DECL(FormatEx); - PF_INIT(FormatEx, Fmifs); uprintf("Windows To Go mode selected"); // Additional sanity checks - if ( (use_ms_efi) && (SelectedDrive.MediaType != FixedMedia) && (nWindowsBuildNumber < 15000)) { + if ((use_esp) && (SelectedDrive.MediaType != FixedMedia) && (nWindowsBuildNumber < 15000)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED; return FALSE; } @@ -1832,16 +2080,8 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) } UnMountISO(); - if (use_ms_efi) { - uprintf("Setting up MS EFI system partition"); - if (pfFormatEx == NULL) - return FALSE; - ms_efi = AltMountVolume(drive_name, 3); // MSR, main, EFI - if (ms_efi == NULL) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); - return FALSE; - } - uprintf("Formatting EFI system partition %s", ms_efi); + if (use_esp) { + uprintf("Setting up EFI System Partition"); // According to Ubuntu (https://bugs.launchpad.net/ubuntu/+source/partman-efi/+bug/811485) you want to use FAT32. // However, you have to be careful that the cluster size needs to be greater or equal to the sector size, which // in turn has an impact on the minimum EFI partition size we can create (see ms_efi_size_MB in drive.c) @@ -1851,27 +2091,35 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) cluster_size = 4096; else // Go for broke cluster_size = (ULONG)SelectedDrive.SectorSize; - fs_index = 1; // FAT32 - task_number = 0; - wVolumeName[0] = ms_efi[0]; - // Boy do you *NOT* want to specify a label here, and spend HOURS figuring out why your EFI partition cannot boot... - pfFormatEx(wVolumeName, SelectedDrive.MediaType, L"FAT32", L"", TRUE, cluster_size, FormatExCallback); - if (IS_ERROR(FormatStatus)) { - uprintf("Failed to format EFI partition"); - AltUnmountVolume(ms_efi); + // Also, we use the Large FAT32 facility Microsoft APIs are *UTTERLY USELESS* for achieving what we want: + // VDS cannot list ESP volumes (talk about allegedly improving on the old disk and volume APIs, only to + // completely neuter it) and IVdsDiskPartitionMF::FormatPartitionEx(), which is what you are supposed to + // use for ESPs, explicitly states: "This method cannot be used to format removable media." + if (!FormatPartition(DriveIndex, partition_index[PI_ESP], cluster_size, FS_FAT32, "", + FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT)) { + uprintf("Could not format EFI System Partition"); return FALSE; } Sleep(200); } + if (use_esp) { + // Need to have the ESP mounted to invoke bcdboot + ms_efi = AltMountVolume(DriveIndex, partition_index[PI_ESP]); + if (ms_efi == NULL) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER); + return FALSE; + } + } + // We invoke the 'bcdboot' command from the host, as the one from the drive produces problems (#558) // Also, since Rufus should (usually) be running as a 32 bit app, on 64 bit systems, we need to use // 'C:\Windows\Sysnative' and not 'C:\Windows\System32' to invoke bcdboot, as 'C:\Windows\System32' // will get converted to 'C:\Windows\SysWOW64' behind the scenes, and there is no bcdboot.exe there. static_sprintf(cmd, "%s\\bcdboot.exe %s\\Windows /v /f %s /s %s", sysnative_dir, drive_name, HAS_BOOTMGR_BIOS(img_report) ? (HAS_BOOTMGR_EFI(img_report) ? "ALL" : "BIOS") : "UEFI", - (use_ms_efi)?ms_efi:drive_name); + (use_esp)?ms_efi:drive_name); uprintf("Enabling boot using command '%s'", cmd); if (RunCommand(cmd, sysnative_dir, usb_debug) != 0) { // Try to continue... but report a failure @@ -1879,7 +2127,7 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT); } - if (use_ms_efi) { + if (use_esp) { Sleep(200); AltUnmountVolume(ms_efi); } @@ -1915,10 +2163,10 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) _RT_RCDATA, "unattend.xml", &bufsize, FALSE); unattend_path[0] = drive_name[0]; fd = fopenU(unattend_path, "wb"); - if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) { - uprintf("Could not write '%s'\n", unattend_path); - } - fclose(fd); + if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) + uprintf("Could not write '%s'", unattend_path); + if (fd != NULL) + fclose(fd); PrintInfo(0, MSG_267, 100.0f); UpdateProgress(OP_DOS, 100.0f); @@ -1955,7 +2203,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) bled_exit(); if ((bled_ret < 0) && (SCODE_CODE(FormatStatus) != ERROR_CANCELLED)) { // Unfortunately, different compression backends return different negative error codes - uprintf("Could not write compressed image: %" PRIi64, bled_ret); + uprintf("Could not write compressed image: %lld", bled_ret); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; goto out; } @@ -2060,7 +2308,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) if (s) uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize); else - uprintf("Write error at sector %" PRIi64 ": %s", wb / SelectedDrive.SectorSize, WindowsErrorString()); + uprintf("Write error at sector %lld: %s", wb / SelectedDrive.SectorSize, WindowsErrorString()); if (i < WRITE_RETRIES) { li.QuadPart = wb; uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000); @@ -2102,16 +2350,16 @@ DWORD WINAPI FormatThread(void* param) { int i, r; BOOL ret, use_large_fat32, windows_to_go; - DWORD DriveIndex = (DWORD)(uintptr_t)param; + DWORD DriveIndex = (DWORD)(uintptr_t)param, ClusterSize, Flags; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; HANDLE hSourceImage = INVALID_HANDLE_VALUE; SYSTEMTIME lt; FILE* log_fd; uint8_t *buffer = NULL, extra_partitions = 0; - char *bb_msg, *guid_volume = NULL; + char *bb_msg, *volume_name = NULL; char drive_name[] = "?:\\"; - char drive_letters[27], fs_name[32]; + char drive_letters[27], fs_name[32], label[64]; char logfile[MAX_PATH], *userdir; char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; char kolibri_dst[] = "?:\\MTLD_F32"; @@ -2127,14 +2375,14 @@ DWORD WINAPI FormatThread(void* param) if ((windows_to_go) && (target_type == TT_UEFI) && (partition_type == PARTITION_STYLE_GPT)) // According to Microsoft, every GPT disk (we RUN Windows from) must have an MSR due to not having hidden sectors // http://msdn.microsoft.com/en-us/library/windows/hardware/dn640535.aspx#gpt_faq_what_disk_require_msr - extra_partitions = XP_MSR | XP_EFI; + extra_partitions = XP_ESP | XP_MSR; else if ( (fs_type == FS_NTFS) && ((boot_type == BT_UEFI_NTFS) || ((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report) && ((target_type == TT_UEFI) || (windows_to_go) || (allow_dual_uefi_bios)))) ) extra_partitions = XP_UEFI_NTFS; - else if (IsChecked(IDC_OLD_BIOS_FIXES)) - extra_partitions = XP_COMPAT; else if ((boot_type == BT_IMAGE) && !write_as_image && HAS_PERSISTENCE(img_report) && persistence_size) extra_partitions = XP_CASPER; + else if (IsChecked(IDC_OLD_BIOS_FIXES)) + extra_partitions = XP_COMPAT; PrintInfoDebug(0, MSG_225); hPhysicalDrive = GetPhysicalHandle(DriveIndex, lock_drive, FALSE, !lock_drive); @@ -2171,18 +2419,22 @@ DWORD WINAPI FormatThread(void* param) } } if (!DeleteVolumeMountPointA(drive_name)) { - uprintf("Failed to delete mountpoint %s: %", drive_name, WindowsErrorString()); + uprintf("Failed to delete mountpoint %s: %s", drive_name, WindowsErrorString()); // Try to continue. We will bail out if this causes an issue. } } } - uprintf("Will use '%c:' as volume mountpoint", drive_name[0]); + uprintf("Will use '%C:' as volume mountpoint", drive_name[0]); // It kind of blows, but we have to relinquish access to the physical drive // for VDS to be able to delete the partitions that reside on it... safe_unlockclose(hPhysicalDrive); PrintInfoDebug(0, MSG_239); - DeletePartitions(DriveIndex); + if (!DeletePartitions(DriveIndex)) { + SetLastError(FormatStatus); + uprintf("Notice: Could not delete partitions: %s", WindowsErrorString()); + FormatStatus = 0; + } // Now get RW access to the physical drive... hPhysicalDrive = GetPhysicalHandle(DriveIndex, lock_drive, TRUE, !lock_drive); @@ -2190,9 +2442,10 @@ DWORD WINAPI FormatThread(void* param) FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; goto out; } + RefreshDriveLayout(hPhysicalDrive); // ...and get a lock to the logical drive so that we can actually write something - hLogicalVolume = GetLogicalHandle(DriveIndex, TRUE, FALSE, !lock_drive); + hLogicalVolume = GetLogicalHandle(DriveIndex, 0, TRUE, FALSE, !lock_drive); if (hLogicalVolume == INVALID_HANDLE_VALUE) { uprintf("Could not lock volume"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; @@ -2311,11 +2564,11 @@ DWORD WINAPI FormatThread(void* param) safe_unlockclose(hPhysicalDrive); safe_unlockclose(hLogicalVolume); Sleep(200); - WaitForLogical(DriveIndex); + WaitForLogical(DriveIndex, 0); if (GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), TRUE)) { - guid_volume = GetLogicalName(DriveIndex, TRUE, TRUE); - if ((guid_volume != NULL) && (MountVolume(drive_name, guid_volume))) - uprintf("Remounted %s as %C:", guid_volume, drive_name[0]); + volume_name = GetLogicalName(DriveIndex, 0, TRUE, TRUE); + if ((volume_name != NULL) && (MountVolume(drive_name, volume_name))) + uprintf("Remounted %s as %C:", volume_name, drive_name[0]); } goto out; } @@ -2340,33 +2593,59 @@ DWORD WINAPI FormatThread(void* param) } hLogicalVolume = INVALID_HANDLE_VALUE; + // VDS wants us to unlock the phys + if (use_vds) + safe_unlockclose(hPhysicalDrive); + // Wait for the logical drive we just created to appear uprintf("Waiting for logical drive to reappear..."); Sleep(200); - if (!WaitForLogical(DriveIndex)) + if (!WaitForLogical(DriveIndex, partition_index[PI_MAIN])) uprintf("Logical drive was not found!"); // We try to continue even if this fails, just in case CHECK_FOR_USER_CANCEL; // Format Casper partition if required. Do it before we format anything with // a file system that Windows will recognize, to avoid concurrent access. if (extra_partitions & XP_CASPER) { - if (!FormatExtFs("casper-rw", ReadSetting32(SETTING_USE_EXTFS_VERSION))) { + uint32_t ext_version = ReadSetting32(SETTING_USE_EXT_VERSION); + if ((ext_version < 2) || (ext_version > 4)) + ext_version = 3; + if (!FormatPartition(DriveIndex, partition_index[PI_CASPER], 0, FS_EXT2 + (ext_version - 2), + "casper-rw", IsChecked(IDC_QUICK_FORMAT) ? FP_QUICK : 0)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } } - // If FAT32 is requested and we have a large drive (>32 GB) use - // large FAT32 format, else use MS's FormatEx. - // TODO: We'll want a single call for ext2/ext3/ext4, large FAT32 and everything (after we switch to VDS?) - ret = use_large_fat32?FormatFAT32(DriveIndex):FormatDrive(DriveIndex); + GetWindowTextU(hLabel, label, sizeof(label)); + ToValidLabel(label, (fs_type == FS_FAT16) || (fs_type == FS_FAT32) || (fs_type == FS_EXFAT)); + ClusterSize = (DWORD)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)); + if (ClusterSize < 0x200) + ClusterSize = 0; // 0 = default cluster size + + Flags = FP_FORCE; + if (IsChecked(IDC_QUICK_FORMAT)) + Flags |= FP_QUICK; + if ((fs_type == FS_NTFS) && (enable_ntfs_compression)) + Flags |= FP_COMPRESSION; + + ret = FormatPartition(DriveIndex, partition_index[PI_MAIN], ClusterSize, fs_type, label, Flags); if (!ret) { - // Error will be set by FormatDrive() in FormatStatus + // Error will be set by FormatPartition() in FormatStatus uprintf("Format error: %s", StrError(FormatStatus, TRUE)); goto out; } + if (use_vds) { + // Get RW access back to the physical drive... + hPhysicalDrive = GetPhysicalHandle(DriveIndex, lock_drive, TRUE, !lock_drive); + if (hPhysicalDrive == INVALID_HANDLE_VALUE) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; + goto out; + } + } + // Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted if ((partition_type == PARTITION_STYLE_MBR) || ((boot_type != BT_NON_BOOTABLE) && (partition_type == PARTITION_STYLE_GPT))) { PrintInfoDebug(0, MSG_228); // "Writing master boot record..." @@ -2378,20 +2657,20 @@ DWORD WINAPI FormatThread(void* param) UpdateProgress(OP_FIX_MBR, -1.0f); } Sleep(200); - WaitForLogical(DriveIndex); + WaitForLogical(DriveIndex, 0); // Try to continue CHECK_FOR_USER_CANCEL; - guid_volume = GetLogicalName(DriveIndex, TRUE, TRUE); - if (guid_volume == NULL) { - uprintf("Could not get GUID volume name"); + volume_name = GetLogicalName(DriveIndex, partition_index[PI_MAIN], TRUE, TRUE); + if (volume_name == NULL) { + uprintf("Could not get volume name"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_VOLUME_ID; goto out; } - uprintf("Found volume GUID %s", guid_volume); + uprintf("Found volume %s", volume_name); - if (!MountVolume(drive_name, guid_volume)) { - uprintf("Could not remount %s as %C: %s\n", guid_volume, drive_name[0], WindowsErrorString()); + if (!MountVolume(drive_name, volume_name)) { + uprintf("Could not remount %s as %C: %s\n", volume_name, drive_name[0], WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_MOUNT_VOLUME); goto out; } @@ -2400,7 +2679,7 @@ DWORD WINAPI FormatThread(void* param) // Disable file indexing, unless it was force-enabled by the user if ((!enable_file_indexing) && ((fs_type == FS_NTFS) || (fs_type == FS_UDF) || (fs_type == FS_REFS))) { uprintf("Disabling file indexing..."); - if (!SetFileAttributesA(guid_volume, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) + if (!SetFileAttributesA(volume_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) uprintf("Could not disable file indexing: %s", WindowsErrorString()); } @@ -2431,7 +2710,8 @@ DWORD WINAPI FormatThread(void* param) } else { // We still have a lock, which we need to modify the volume boot record // => no need to reacquire the lock... - hLogicalVolume = GetLogicalHandle(DriveIndex, FALSE, TRUE, FALSE); + // TODO: Shouldn't PI always be 1 here? + hLogicalVolume = GetLogicalHandle(DriveIndex, partition_index[PI_MAIN], FALSE, TRUE, FALSE); if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) { uprintf("Could not re-mount volume for partition boot record access"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; @@ -2482,7 +2762,7 @@ DWORD WINAPI FormatThread(void* param) drive_name[2] = 0; // Ensure our drive is something like 'D:' if (windows_to_go) { PrintInfoDebug(0, MSG_268); - if (!SetupWinToGo(drive_name, (extra_partitions & XP_EFI))) { + if (!SetupWinToGo(DriveIndex, drive_name, (extra_partitions & XP_ESP))) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); goto out; @@ -2547,17 +2827,17 @@ DWORD WINAPI FormatThread(void* param) out: zero_drive = FALSE; - safe_free(guid_volume); + safe_free(volume_name); safe_free(buffer); safe_closehandle(hSourceImage); safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); // This can take a while if (IS_ERROR(FormatStatus)) { - guid_volume = GetLogicalName(DriveIndex, TRUE, FALSE); - if (guid_volume != NULL) { - if (MountVolume(drive_name, guid_volume)) + volume_name = GetLogicalName(DriveIndex, 0, TRUE, FALSE); + if (volume_name != NULL) { + if (MountVolume(drive_name, volume_name)) uprintf("Re-mounted volume as %C: after error", drive_name[0]); - free(guid_volume); + free(volume_name); } } PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 67c3648d..79ec88d2 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -85,6 +85,10 @@ static __inline char* wchar_to_utf8(const wchar_t* wstr) int size = 0; char* str = NULL; + // Convert the empty string too + if (wstr[0] == 0) + return calloc(1, 1); + // Find out the size we need to allocate for our converted string size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); if (size <= 1) // An empty string would be size 1 @@ -113,6 +117,10 @@ static __inline wchar_t* utf8_to_wchar(const char* str) if (str == NULL) return NULL; + // Convert the empty string too + if (str[0] == 0) + return calloc(1, sizeof(wchar_t)); + // Find out the size we need to allocate for our converted string size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); if (size <= 1) // An empty string would be size 1 diff --git a/src/rufus.c b/src/rufus.c index 4f3fa807..dc03e93b 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -51,7 +51,6 @@ #include "../res/grub2/grub2_version.h" static const char* cmdline_hogger = "rufus.com"; -static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", "ReFS" }; static const char* ep_reg = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; static const char* vs_reg = "Software\\Microsoft\\VisualStudio"; static BOOL existing_key = FALSE; // For LGP set/restore @@ -84,6 +83,7 @@ extern long grub2_len; extern char* szStatusMessage; extern const char* old_c32_name[NB_OLD_C32]; extern const char* cert_name[3]; +extern const char* FileSystemLabel[FS_MAX]; /* * Globals @@ -108,7 +108,7 @@ BOOL enable_HDDs = FALSE, enable_ntfs_compression = FALSE, no_confirmation_on_ca BOOL advanced_mode_device, advanced_mode_format, allow_dual_uefi_bios, detect_fakes, enable_vmdk, force_large_fat32, usb_debug; BOOL use_fake_units, preserve_timestamps = FALSE, fast_zeroing = FALSE, app_changed_size = FALSE; BOOL zero_drive = FALSE, list_non_usb_removable_drives = FALSE, enable_file_indexing, large_drive = FALSE; -BOOL write_as_image = FALSE, installed_uefi_ntfs = FALSE, enable_fido = FALSE; +BOOL write_as_image = FALSE, installed_uefi_ntfs = FALSE, enable_fido = FALSE, use_vds = FALSE; float fScale = 1.0f; int dialog_showing = 0, selection_default = BT_IMAGE, windows_to_go_selection = 0, persistence_unit_selection = -1; int default_fs, fs_type, boot_type, partition_type, target_type; // file system, boot type, partition type, target type @@ -121,7 +121,7 @@ char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *shor char image_option_txt[128], *fido_url = NULL; StrArray DriveID, DriveLabel, DriveHub, BlockingProcess, ImageList; // Number of steps for each FS for FCC_STRUCTURE_PROGRESS -const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10 }; +const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 }; const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" }; // TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG, @@ -1859,10 +1859,6 @@ out: return ret; } - -#ifdef RUFUS_TEST -extern BOOL FormatExtFs(const char* label, uint32_t version); -#endif /* * Main dialog callback */ @@ -1898,7 +1894,15 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { - FormatExtFs("casper-rw", 3); +// uprintf(" IID_IVdsVolume: %s", GuidToString(&IID_IVdsVolume)); +// uprintf(" IID_IVdsVolumeMF3: %s", GuidToString(&IID_IVdsVolumeMF3)); + nDeviceIndex = ComboBox_GetCurSel(hDeviceList); +// AltMountVolume2("G:", 3); + AltMountVolume((DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex), 3); +// AltUnmountVolume("P:"); +// uprintf("%s", GetLogicalName((DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex), 0, FALSE, FALSE)); +// uprintf("%s", GetLogicalName((DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex), 1, FALSE, FALSE)); +// uprintf("%s", GetLogicalName((DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex), 2, FALSE, FALSE)); break; } #endif @@ -3026,6 +3030,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine advanced_mode_format = ReadSettingBool(SETTING_ADVANCED_MODE_FORMAT); preserve_timestamps = ReadSettingBool(SETTING_PRESERVE_TIMESTAMPS); use_fake_units = !ReadSettingBool(SETTING_USE_PROPER_SIZE_UNITS); + use_vds = ReadSettingBool(SETTING_USE_VDS); usb_debug = ReadSettingBool(SETTING_ENABLE_USB_DEBUG); detect_fakes = !ReadSettingBool(SETTING_DISABLE_FAKE_DRIVES_CHECK); allow_dual_uefi_bios = ReadSettingBool(SETTING_ENABLE_WIN_DUAL_EFI_BIOS); @@ -3198,7 +3203,7 @@ relaunch: // Do our own event processing and process "magic" commands while(GetMessage(&msg, NULL, 0, 0)) { - // ** ***** **** ** ***** **** + // ** ***** **** ** ********** // .,ABCDEFGHIJKLMNOPQRSTUVWXYZ // Ctrl-A => Select the log data @@ -3357,6 +3362,13 @@ relaunch: GetDevices(0); continue; } + // Alt-V => Use VDS facilities for formatting + if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'V')) { + use_vds = !use_vds; + WriteSettingBool(SETTING_USE_VDS, use_vds); + PrintStatusTimeout("VDS", use_vds); + continue; + } // Alt-W => Enable VMWare disk detection if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'W')) { enable_vmdk = !enable_vmdk; diff --git a/src/rufus.h b/src/rufus.h index bae4fc2f..f084b813 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -22,13 +22,16 @@ #if defined(_MSC_VER) // Disable some VS Code Analysis warnings #pragma warning(disable: 4996) // Ignore deprecated -#pragma warning(disable: 28159) // I'll keep using GetVersionEx(), thank you very much! #pragma warning(disable: 6258) // I know what I'm using TerminateThread for +#pragma warning(disable: 26451) // Stop bugging me with casts already! +#pragma warning(disable: 28159) // I'll keep using GetVersionEx(), thank you very much... #endif #pragma once -/* Features not ready for prime time and that may *DESTROY* your data - USE AT YOUR OWN RISKS! */ +/* + * Features not ready for prime time and that may *DESTROY* your data - USE AT YOUR OWN RISKS! + */ //#define RUFUS_TEST #define APPLICATION_NAME "Rufus" @@ -67,7 +70,7 @@ #define MAX_LOG_SIZE 0x7FFFFFFE #define MAX_REFRESH 25 // How long we should wait to refresh UI elements (in ms) #define MAX_GUID_STRING_LENGTH 40 -#define MAX_GPT_PARTITIONS 128 +#define MAX_PARTITIONS 16 // Maximum number of partitions we handle #define MAX_SECTORS_TO_CLEAR 128 // nb sectors to zap when clearing the MBR/GPT (must be >34) #define MAX_WININST 4 // Max number of install[.wim|.esd] we can handle on an image #define MBR_UEFI_MARKER 0x49464555 // 'U', 'E', 'F', 'I', as a 32 bit little endian longword @@ -80,7 +83,6 @@ #define CHECK_DRIVE_TIMEOUT 2000 #define MARQUEE_TIMER_REFRESH 10 // Time between progress bar marquee refreshes, in ms #define FS_DEFAULT FS_FAT32 -#define CASPER_PARTITION_DEFAULT 2 #define SINGLE_CLUSTERSIZE_DEFAULT 0x00000100 #define BADLOCKS_PATTERN_TYPES 3 #define BADBLOCK_PATTERN_COUNT 4 @@ -118,7 +120,7 @@ #endif #define IsChecked(CheckBox_ID) (IsDlgButtonChecked(hMainDialog, CheckBox_ID) == BST_CHECKED) #define MB_IS_RTL (right_to_left_mode?MB_RTLREADING|MB_RIGHT:0) -#define CHECK_FOR_USER_CANCEL if (IS_ERROR(FormatStatus)) goto out +#define CHECK_FOR_USER_CANCEL if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) goto out // Bit masks used for the display of additional image options in the UI #define IMOP_WINTOGO 0x01 #define IMOP_PERSISTENCE 0x02 @@ -243,6 +245,9 @@ enum fs_type { FS_UDF, FS_EXFAT, FS_REFS, + FS_EXT2, + FS_EXT3, + FS_EXT4, FS_MAX }; diff --git a/src/rufus.rc b/src/rufus.rc index aff23282..bf5b9984 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.6.1527" +CAPTION "Rufus 3.6.1528" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,6,1527,0 - PRODUCTVERSION 3,6,1527,0 + FILEVERSION 3,6,1528,0 + PRODUCTVERSION 3,6,1528,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://akeo.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.6.1527" + VALUE "FileVersion", "3.6.1528" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", " 2011-2019 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus-3.6.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.6.1527" + VALUE "ProductVersion", "3.6.1528" END END BLOCK "VarFileInfo" diff --git a/src/settings.h b/src/settings.h index 0d3a1f93..9096e0a0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -43,8 +43,10 @@ extern char* ini_file; #define SETTING_LAST_UPDATE "LastUpdateCheck" #define SETTING_LOCALE "Locale" #define SETTING_UPDATE_INTERVAL "UpdateCheckInterval" -#define SETTING_USE_EXTFS_VERSION "UseExtFsVersion" +#define SETTING_USE_EXT_VERSION "UseExtVersion" #define SETTING_USE_PROPER_SIZE_UNITS "UseProperSizeUnits" +#define SETTING_USE_UDF_VERSION "UseUdfVersion" +#define SETTING_USE_VDS "UseVds" #define SETTING_PRESERVE_TIMESTAMPS "PreserveTimestamps" #define SETTING_VERBOSE_UPDATES "VerboseUpdateCheck" diff --git a/src/stdio.c b/src/stdio.c index 00652726..84d34998 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -272,7 +272,7 @@ const char* _StrError(DWORD error_code) return lmprintf(MSG_050); } if (SCODE_FACILITY(error_code) != FACILITY_STORAGE) { - uprintf("StrError: non storage - %08X (%X)\n", error_code, SCODE_FACILITY(error_code)); +// uprintf("StrError: non storage - %08X (%X)\n", error_code, SCODE_FACILITY(error_code)); SetLastError(error_code); return WindowsErrorString(); } diff --git a/src/syslinux.c b/src/syslinux.c index 924c5316..3fefc4f5 100644 --- a/src/syslinux.c +++ b/src/syslinux.c @@ -136,7 +136,7 @@ BOOL InstallSyslinux(DWORD drive_index, char drive_letter, int file_system) * weird reason.the Syslinux install process *MUST* have FILE_SHARE_WRITE * on the volume, or else creating 'ldlinux.sys' will fail... */ - d_handle = GetLogicalHandle(drive_index, FALSE, TRUE, TRUE); + d_handle = GetLogicalHandle(drive_index, 0, FALSE, TRUE, TRUE); if ((d_handle == INVALID_HANDLE_VALUE) || (d_handle == NULL)) { uprintf("Could open volume for Syslinux installation"); goto out; diff --git a/src/ui.c b/src/ui.c index 487b26ef..904cf8b3 100644 --- a/src/ui.c +++ b/src/ui.c @@ -43,7 +43,7 @@ UINT_PTR UM_LANGUAGE_MENU_MAX = UM_LANGUAGE_MENU; HIMAGELIST hUpImageList, hDownImageList; -extern BOOL enable_fido; +extern BOOL enable_fido, use_vds; int advanced_device_section_height, advanced_format_section_height; // (empty) check box width, (empty) drop down width, button height (for and without dropdown match) int cbw, ddw, ddbh = 0, bh = 0; @@ -1168,20 +1168,21 @@ void InitProgress(BOOL bOnlyFormat) nb_slots[OP_ZERO_MBR] = 1; nb_slots[OP_PARTITION] = 1; nb_slots[OP_FIX_MBR] = 1; - nb_slots[OP_CREATE_FS] = + nb_slots[OP_CREATE_FS] = (use_vds) ? 2 : nb_steps[ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))]; // So, yeah, if you're doing slow format, or using Large FAT32, and have persistence, you'll see // the progress bar revert during format on account that we reuse the same operation for both // partitions. Maybe one day I'll be bothered to handle two separate OP_FORMAT ops... - if ((!IsChecked(IDC_QUICK_FORMAT)) || (persistence_size != 0) - || ((fs_type == FS_FAT32) && ((SelectedDrive.DiskSize >= LARGE_FAT32_SIZE) || (force_large_fat32)))) { + if ((!IsChecked(IDC_QUICK_FORMAT)) || (persistence_size != 0) || + ((fs_type == FS_FAT32) && ((SelectedDrive.DiskSize >= LARGE_FAT32_SIZE) || (force_large_fat32)))) { nb_slots[OP_FORMAT] = -1; + nb_slots[OP_CREATE_FS] = 0; } nb_slots[OP_FINALIZE] = ((selection_default == BT_IMAGE) && (fs_type == FS_NTFS)) ? 3 : 2; } } - for (i = 0; i 0) { slots_discrete += nb_slots[i] * 1.0f; } @@ -1190,7 +1191,7 @@ void InitProgress(BOOL bOnlyFormat) } } - for (i = 0; i 0) { @@ -1203,7 +1204,7 @@ void InitProgress(BOOL bOnlyFormat) // If there's no analog, adjust our discrete ends to fill the whole bar if (slots_analog == 0.0f) { - for (i = 0; i