"Fossies" - the Fresh Open Source Software Archive

Member "rufus-3.13/src/drive.c" (20 Nov 2020, 86100 Bytes) of package /linux/misc/rufus-3.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "drive.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

    1 /*
    2  * Rufus: The Reliable USB Formatting Utility
    3  * Drive access function calls
    4  * Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
    5  *
    6  * This program is free software: you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation, either version 3 of the License, or
    9  * (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   18  */
   19 #ifdef _CRTDBG_MAP_ALLOC
   20 #include <stdlib.h>
   21 #include <crtdbg.h>
   22 #endif
   23 
   24 #include <windows.h>
   25 #include <windowsx.h>
   26 #include <stdio.h>
   27 #include <string.h>
   28 #include <ctype.h>
   29 #include <assert.h>
   30 #if !defined(__MINGW32__)
   31 #include <initguid.h>
   32 #endif
   33 #include <vds.h>
   34 
   35 #include "rufus.h"
   36 #include "missing.h"
   37 #include "resource.h"
   38 #include "settings.h"
   39 #include "msapi_utf8.h"
   40 #include "localization.h"
   41 
   42 #include "file.h"
   43 #include "drive.h"
   44 #include "mbr_types.h"
   45 #include "gpt_types.h"
   46 #include "br.h"
   47 #include "fat16.h"
   48 #include "fat32.h"
   49 #include "ntfs.h"
   50 
   51 #define GLOBALROOT_NAME "\\\\?\\GLOBALROOT"
   52 const char* sfd_name = "Super Floppy Disk";
   53 const char* groot_name = GLOBALROOT_NAME;
   54 const size_t groot_len = sizeof(GLOBALROOT_NAME) - 1;
   55 
   56 
   57 #if defined(__MINGW32__)
   58 const IID CLSID_VdsLoader = { 0x9c38ed61, 0xd565, 0x4728, { 0xae, 0xee, 0xc8, 0x09, 0x52, 0xf0, 0xec, 0xde } };
   59 const IID IID_IVdsServiceLoader = { 0xe0393303, 0x90d4, 0x4a97, { 0xab, 0x71, 0xe9, 0xb6, 0x71, 0xee, 0x27, 0x29 } };
   60 const IID IID_IVdsProvider = { 0x10c5e575, 0x7984, 0x4e81, { 0xa5, 0x6b, 0x43, 0x1f, 0x5f, 0x92, 0xae, 0x42 } };
   61 const IID IID_IVdsSwProvider = { 0x9aa58360, 0xce33, 0x4f92, { 0xb6, 0x58, 0xed, 0x24, 0xb1, 0x44, 0x25, 0xb8 } };
   62 const IID IID_IVdsPack = { 0x3b69d7f5, 0x9d94, 0x4648, { 0x91, 0xca, 0x79, 0x93, 0x9b, 0xa2, 0x63, 0xbf } };
   63 const IID IID_IVdsDisk = { 0x07e5c822, 0xf00c, 0x47a1, { 0x8f, 0xce, 0xb2, 0x44, 0xda, 0x56, 0xfd, 0x06 } };
   64 const IID IID_IVdsAdvancedDisk = { 0x6e6f6b40, 0x977c, 0x4069, { 0xbd, 0xdd, 0xac, 0x71, 0x00, 0x59, 0xf8, 0xc0 } };
   65 const IID IID_IVdsVolume = { 0x88306BB2, 0xE71F, 0x478C, { 0x86, 0xA2, 0x79, 0xDA, 0x20, 0x0A, 0x0F, 0x11} };
   66 const IID IID_IVdsVolumeMF3 = { 0x6788FAF9, 0x214E, 0x4B85, { 0xBA, 0x59, 0x26, 0x69, 0x53, 0x61, 0x6E, 0x09 } };
   67 #endif
   68 
   69 PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS));
   70 
   71 /*
   72  * Globals
   73  */
   74 RUFUS_DRIVE_INFO SelectedDrive;
   75 extern BOOL installed_uefi_ntfs, write_as_esp;
   76 extern int nWindowsVersion, nWindowsBuildNumber;
   77 uint64_t partition_offset[PI_MAX];
   78 uint64_t persistence_size = 0;
   79 
   80 /*
   81  * The following methods get or set the AutoMount setting (which is different from AutoRun)
   82  * Rufus needs AutoMount to be set as the format process may fail for fixed drives otherwise.
   83  * See https://github.com/pbatard/rufus/issues/386.
   84  *
   85  * Reverse engineering diskpart and mountvol indicates that the former uses the IVdsService
   86  * ClearFlags()/SetFlags() to set VDS_SVF_AUTO_MOUNT_OFF whereas mountvol on uses
   87  * IOCTL_MOUNTMGR_SET_AUTO_MOUNT on "\\\\.\\MountPointManager".
   88  * As the latter is MUCH simpler this is what we'll use too
   89  */
   90 BOOL SetAutoMount(BOOL enable)
   91 {
   92     HANDLE hMountMgr;
   93     DWORD size;
   94     BOOL ret = FALSE;
   95 
   96     hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   97     if (hMountMgr == INVALID_HANDLE_VALUE)
   98         return FALSE;
   99     ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_SET_AUTO_MOUNT, &enable, sizeof(enable), NULL, 0, &size, NULL);
  100     CloseHandle(hMountMgr);
  101     return ret;
  102 }
  103 
  104 BOOL GetAutoMount(BOOL* enabled)
  105 {
  106     HANDLE hMountMgr;
  107     DWORD size;
  108     BOOL ret = FALSE;
  109 
  110     if (enabled == NULL)
  111         return FALSE;
  112     hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  113     if (hMountMgr == INVALID_HANDLE_VALUE)
  114         return FALSE;
  115     ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT, NULL, 0, enabled, sizeof(*enabled), &size, NULL);
  116     CloseHandle(hMountMgr);
  117     return ret;
  118 }
  119 
  120 /*
  121  * Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as
  122  * index would return a handle to C:, which we might then proceed to unknowingly
  123  * clear the MBR of!), so we mitigate the risk by forcing our indexes to belong to
  124  * the specific range [DRIVE_INDEX_MIN; DRIVE_INDEX_MAX].
  125  */
  126 #define CheckDriveIndex(DriveIndex) do {                                            \
  127     if ((int)DriveIndex < 0) goto out;                                              \
  128     assert((DriveIndex >= DRIVE_INDEX_MIN) && (DriveIndex <= DRIVE_INDEX_MAX));     \
  129     if ((DriveIndex < DRIVE_INDEX_MIN) || (DriveIndex > DRIVE_INDEX_MAX)) goto out; \
  130     DriveIndex -= DRIVE_INDEX_MIN; } while (0)
  131 
  132 /*
  133  * Open a drive or volume with optional write and lock access
  134  * Return INVALID_HANDLE_VALUE (/!\ which is DIFFERENT from NULL /!\) on failure.
  135  */
  136 static HANDLE GetHandle(char* Path, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
  137 {
  138     int i;
  139     BYTE access_mask = 0;
  140     DWORD size;
  141     uint64_t EndTime;
  142     HANDLE hDrive = INVALID_HANDLE_VALUE;
  143     char DevPath[MAX_PATH];
  144 
  145     if ((safe_strlen(Path) < 5) || (Path[0] != '\\') || (Path[1] != '\\') || (Path[3] != '\\'))
  146         goto out;
  147 
  148     // Resolve a device path, so that we can look for that handle in case of access issues.
  149     if (safe_strncmp(Path, groot_name, groot_len) == 0)
  150         static_strcpy(DevPath, &Path[groot_len]);
  151     else if (QueryDosDeviceA(&Path[4], DevPath, sizeof(DevPath)) == 0)
  152         strcpy(DevPath, "???");
  153 
  154     for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) {
  155         // Try without FILE_SHARE_WRITE (unless specifically requested) so that
  156         // we won't be bothered by the OS or other apps when we set up our data.
  157         // However this means we might have to wait for an access gap...
  158         // We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is
  159         // required for enumeration.
  160         hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRITE:0),
  161             FILE_SHARE_READ|(bWriteShare?FILE_SHARE_WRITE:0),
  162             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  163         if (hDrive != INVALID_HANDLE_VALUE)
  164             break;
  165         if ((GetLastError() != ERROR_SHARING_VIOLATION) && (GetLastError() != ERROR_ACCESS_DENIED))
  166             break;
  167         if (i == 0) {
  168             uprintf("Notice: Volume Device Path is %s", DevPath);
  169             uprintf("Waiting for access on %s...", Path);
  170         } else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES/3)) {
  171             // If we can't seem to get a hold of the drive for some time, try to enable FILE_SHARE_WRITE...
  172             uprintf("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled...");
  173             bWriteShare = TRUE;
  174             // Try to report the process that is locking the drive
  175             // We also use bit 6 as a flag to indicate that SearchProcess was called.
  176             access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40;
  177         }
  178         Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
  179     }
  180     if (hDrive == INVALID_HANDLE_VALUE) {
  181         uprintf("Could not open %s: %s", Path, WindowsErrorString());
  182         goto out;
  183     }
  184 
  185     if (bWriteAccess) {
  186         uprintf("Opened %s for %s write access", Path, bWriteShare?"shared":"exclusive");
  187     }
  188 
  189     if (bLockDrive) {
  190         if (DeviceIoControl(hDrive, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &size, NULL)) {
  191             uprintf("I/O boundary checks disabled");
  192         }
  193 
  194         EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT;
  195         do {
  196             if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &size, NULL))
  197                 goto out;
  198             if (IS_ERROR(FormatStatus)) // User cancel
  199                 break;
  200             Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
  201         } while (GetTickCount64() < EndTime);
  202         // If we reached this section, either we didn't manage to get a lock or the user cancelled
  203         uprintf("Could not lock access to %s: %s", Path, WindowsErrorString());
  204         // See if we can report the processes are accessing the drive
  205         if (!IS_ERROR(FormatStatus) && (access_mask == 0))
  206             access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE);
  207         // Try to continue if the only access rights we saw were for read-only
  208         if ((access_mask & 0x07) != 0x01)
  209             safe_closehandle(hDrive);
  210     }
  211 
  212 out:
  213     return hDrive;
  214 }
  215 
  216 /*
  217  * Return the path to access the physical drive, or NULL on error.
  218  * The string is allocated and must be freed (to ensure concurrent access)
  219  */
  220 char* GetPhysicalName(DWORD DriveIndex)
  221 {
  222     BOOL success = FALSE;
  223     char physical_name[24];
  224 
  225     CheckDriveIndex(DriveIndex);
  226     static_sprintf(physical_name, "\\\\.\\PhysicalDrive%lu", DriveIndex);
  227     success = TRUE;
  228 out:
  229     return (success)?safe_strdup(physical_name):NULL;
  230 }
  231 
  232 /*
  233  * Return a handle to the physical drive identified by DriveIndex
  234  */
  235 HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
  236 {
  237     HANDLE hPhysical = INVALID_HANDLE_VALUE;
  238     char* PhysicalPath = GetPhysicalName(DriveIndex);
  239     hPhysical = GetHandle(PhysicalPath, bLockDrive, bWriteAccess, bWriteShare);
  240     safe_free(PhysicalPath);
  241     return hPhysical;
  242 }
  243 
  244 /*
  245  * Return the GUID volume name for the disk and partition specified, or NULL if not found.
  246  * See http://msdn.microsoft.com/en-us/library/cc542456.aspx
  247  * If PartitionOffset is 0, the offset is ignored and the first partition found is returned.
  248  * The returned string is allocated and must be freed.
  249  */
  250 char* GetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent)
  251 {
  252     static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" };
  253     static const char* volume_start = "\\\\?\\";
  254     char *ret = NULL, volume_name[MAX_PATH], path[MAX_PATH];
  255     BOOL bPrintHeader = TRUE;
  256     HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE;
  257     VOLUME_DISK_EXTENTS_REDEF DiskExtents;
  258     DWORD size;
  259     UINT drive_type;
  260     StrArray found_name;
  261     uint64_t found_offset[MAX_PARTITIONS] = { 0 };
  262     uint32_t i, j;
  263     size_t len;
  264 
  265     StrArrayCreate(&found_name, MAX_PARTITIONS);
  266     CheckDriveIndex(DriveIndex);
  267 
  268     for (i = 0; hDrive == INVALID_HANDLE_VALUE; i++) {
  269         if (i == 0) {
  270             hVolume = FindFirstVolumeA(volume_name, sizeof(volume_name));
  271             if (hVolume == INVALID_HANDLE_VALUE) {
  272                 suprintf("Could not access first GUID volume: %s", WindowsErrorString());
  273                 goto out;
  274             }
  275         } else {
  276             if (!FindNextVolumeA(hVolume, volume_name, sizeof(volume_name))) {
  277                 if (GetLastError() != ERROR_NO_MORE_FILES) {
  278                     suprintf("Could not access next GUID volume: %s", WindowsErrorString());
  279                 }
  280                 break;
  281             }
  282         }
  283 
  284         // Sanity checks
  285         len = safe_strlen(volume_name);
  286         assert(len > 4);
  287         assert(safe_strnicmp(volume_name, volume_start, 4) == 0);
  288         assert(volume_name[len - 1] == '\\');
  289 
  290         drive_type = GetDriveTypeA(volume_name);
  291         if ((drive_type != DRIVE_REMOVABLE) && (drive_type != DRIVE_FIXED))
  292             continue;
  293 
  294         volume_name[len-1] = 0;
  295 
  296         if (QueryDosDeviceA(&volume_name[4], path, sizeof(path)) == 0) {
  297             suprintf("Failed to get device path for GUID volume '%s': %s", volume_name, WindowsErrorString());
  298             continue;
  299         }
  300 
  301         for (j=0; (j<ARRAYSIZE(ignore_device)) &&
  302             (_strnicmp(path, ignore_device[j], safe_strlen(ignore_device[j])) != 0); j++);
  303         if (j < ARRAYSIZE(ignore_device)) {
  304             suprintf("Skipping GUID volume for '%s'", path);
  305             continue;
  306         }
  307 
  308         hDrive = CreateFileA(volume_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
  309             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  310         if (hDrive == INVALID_HANDLE_VALUE) {
  311             suprintf("Could not open GUID volume '%s': %s", volume_name, WindowsErrorString());
  312             continue;
  313         }
  314 
  315         if ((!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
  316             &DiskExtents, sizeof(DiskExtents), &size, NULL)) || (size <= 0)) {
  317             suprintf("Could not get Disk Extents: %s", WindowsErrorString());
  318             safe_closehandle(hDrive);
  319             continue;
  320         }
  321         safe_closehandle(hDrive);
  322         if (DiskExtents.NumberOfDiskExtents == 0) {
  323             suprintf("Ignoring volume '%s' because it has no extents...", volume_name);
  324             continue;
  325         }
  326         if (DiskExtents.NumberOfDiskExtents != 1) {
  327             // If we have more than one extent for a volume, it means that someone
  328             // is using RAID-1 or something => Stay well away from such a volume!
  329             suprintf("Ignoring volume '%s' because it has more than one extent (RAID?)...", volume_name);
  330             continue;
  331         }
  332         if (DiskExtents.Extents[0].DiskNumber != DriveIndex)
  333             // Not on our disk
  334             continue;
  335 
  336         if (found_name.Index == MAX_PARTITIONS) {
  337             uprintf("Error: Trying to process a disk with more than %d partitions!", MAX_PARTITIONS);
  338             goto out;
  339         }
  340 
  341         if (bKeepTrailingBackslash)
  342             volume_name[len - 1] = '\\';
  343         found_offset[found_name.Index] = DiskExtents.Extents[0].StartingOffset.QuadPart;
  344         StrArrayAdd(&found_name, volume_name, TRUE);
  345         if (!bSilent) {
  346             if (bPrintHeader) {
  347                 bPrintHeader = FALSE;
  348                 uuprintf("Windows volumes from this device:");
  349             }
  350             uuprintf("● %s @%lld", volume_name, DiskExtents.Extents[0].StartingOffset.QuadPart);
  351         }
  352     }
  353 
  354     if (found_name.Index == 0)
  355         goto out;
  356 
  357     // Now process all the volumes we found, and try to match one with our partition offset
  358     for (i = 0; (i < found_name.Index) && (PartitionOffset != 0) && (PartitionOffset != found_offset[i]); i++);
  359 
  360     if (i < found_name.Index) {
  361         ret = safe_strdup(found_name.String[i]);
  362     } else {
  363         // NB: We need to re-add DRIVE_INDEX_MIN for this call since CheckDriveIndex() substracted it
  364         ret = AltGetLogicalName(DriveIndex + DRIVE_INDEX_MIN, PartitionOffset, bKeepTrailingBackslash, bSilent);
  365         if ((ret != NULL) && (strchr(ret, ' ') != NULL))
  366             uprintf("Warning: Using physical device to access partition data");
  367     }
  368 
  369 out:
  370     if (hVolume != INVALID_HANDLE_VALUE)
  371         FindVolumeClose(hVolume);
  372     StrArrayDestroy(&found_name);
  373     return ret;
  374 }
  375 
  376 /*
  377  * Alternative version of the above, needed because some volumes, such as ESPs, are not listed
  378  * by Windows, be it with VDS or other APIs.
  379  * For these, we return the "\\?\GLOBALROOT\Device\HarddiskVolume#" identifier that matches
  380  * our "Harddisk#Partition#", as reported by QueryDosDevice().
  381  * The returned string is allocated and must be freed.
  382 */
  383 char* AltGetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent)
  384 {
  385     BOOL matching_drive = (DriveIndex == SelectedDrive.DeviceNumber);
  386     DWORD i;
  387     char *ret = NULL, volume_name[MAX_PATH], path[64];
  388 
  389     CheckDriveIndex(DriveIndex);
  390 
  391     // Match the offset to a partition index
  392     if (PartitionOffset == 0) {
  393         i = 0;
  394     } else if (matching_drive) {
  395         for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.PartitionOffset[i]); i++);
  396         if (i >= MAX_PARTITIONS) {
  397             suprintf("Error: Could not find a partition at offset %lld on this disk", PartitionOffset);
  398             goto out;
  399         }
  400     } else {
  401         suprintf("Error: Searching for a partition on a non matching disk");
  402         goto out;
  403     }
  404     static_sprintf(path, "Harddisk%luPartition%lu", DriveIndex, i + 1);
  405     static_strcpy(volume_name, groot_name);
  406     if (!QueryDosDeviceA(path, &volume_name[groot_len], (DWORD)(MAX_PATH - groot_len)) || (strlen(volume_name) < 20)) {
  407         suprintf("Could not find a DOS volume name for '%s': %s", path, WindowsErrorString());
  408         goto out;
  409     } else if (bKeepTrailingBackslash) {
  410         static_strcat(volume_name, "\\");
  411     }
  412     ret = safe_strdup(volume_name);
  413 
  414 out:
  415     return ret;
  416 }
  417 
  418 /*
  419  * Custom volume name for extfs formatting (that includes partition offset and partition size)
  420  * so that these can be created and accessed on pre 1703 versions of Windows.
  421  */
  422 char* GetExtPartitionName(DWORD DriveIndex, uint64_t PartitionOffset)
  423 {
  424     DWORD i;
  425     char* ret = NULL, volume_name[MAX_PATH];
  426 
  427     // Can't operate if we're not on the selected drive
  428     if (DriveIndex != SelectedDrive.DeviceNumber)
  429         goto out;
  430     CheckDriveIndex(DriveIndex);
  431     for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.PartitionOffset[i]); i++);
  432     if (i >= MAX_PARTITIONS)
  433         goto out;
  434     static_sprintf(volume_name, "\\\\.\\PhysicalDrive%lu %I64u %I64u", DriveIndex,
  435         SelectedDrive.PartitionOffset[i], SelectedDrive.PartitionSize[i]);
  436     ret = safe_strdup(volume_name);
  437 out:
  438     return ret;
  439 }
  440 
  441 static const char* VdsErrorString(HRESULT hr) {
  442     SetLastError(hr);
  443     return WindowsErrorString();
  444 }
  445 
  446 /*
  447  * Call on VDS to refresh the drive layout
  448  */
  449 BOOL RefreshLayout(DWORD DriveIndex)
  450 {
  451     HRESULT hr = S_FALSE;
  452     wchar_t wPhysicalName[24];
  453     IVdsServiceLoader* pLoader = NULL;
  454     IVdsService* pService = NULL;
  455     IEnumVdsObject *pEnum;
  456 
  457     CheckDriveIndex(DriveIndex);
  458     wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex);
  459 
  460     // Initialize COM
  461     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  462     IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
  463         RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
  464 
  465     // Create a VDS Loader Instance
  466     hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
  467         &IID_IVdsServiceLoader, (void **)&pLoader);
  468     if (hr != S_OK) {
  469         uprintf("Could not create VDS Loader Instance: %s", VdsErrorString(hr));
  470         goto out;
  471     }
  472 
  473     // Load the VDS Service
  474     hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
  475     if (hr != S_OK) {
  476         uprintf("Could not load VDS Service: %s", VdsErrorString(hr));
  477         goto out;
  478     }
  479 
  480     // Wait for the Service to become ready if needed
  481     hr = IVdsService_WaitForServiceReady(pService);
  482     if (hr != S_OK) {
  483         uprintf("VDS Service is not ready: %s", VdsErrorString(hr));
  484         goto out;
  485     }
  486 
  487     // Query the VDS Service Providers
  488     hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
  489     if (hr != S_OK) {
  490         uprintf("Could not query VDS Service Providers: %s", VdsErrorString(hr));
  491         goto out;
  492     }
  493 
  494     // Remove mountpoints
  495     hr = IVdsService_CleanupObsoleteMountPoints(pService);
  496     if (hr != S_OK) {
  497         uprintf("Could not clean up VDS mountpoints: %s", VdsErrorString(hr));
  498         goto out;
  499     }
  500 
  501     // Invoke layout refresh
  502     hr = IVdsService_Refresh(pService);
  503     if (hr != S_OK) {
  504         uprintf("Could not refresh VDS layout: %s", VdsErrorString(hr));
  505         goto out;
  506     }
  507 
  508     // Force re-enum
  509     hr = IVdsService_Reenumerate(pService);
  510     if (hr != S_OK) {
  511         uprintf("Could not refresh VDS layout: %s", VdsErrorString(hr));
  512         goto out;
  513     }
  514 
  515 out:
  516     if (pService != NULL)
  517         IVdsService_Release(pService);
  518     if (pLoader != NULL)
  519         IVdsServiceLoader_Release(pLoader);
  520     VDS_SET_ERROR(hr);
  521     return (hr == S_OK);
  522 }
  523 
  524 /*
  525  * Generic call to instantiate a VDS Disk Interface. Mostly copied from:
  526  * https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom?forum=vcgeneral
  527  * See also: https://docs.microsoft.com/en-us/windows/win32/vds/working-with-enumeration-objects
  528  */
  529 static BOOL GetVdsDiskInterface(DWORD DriveIndex, const IID* InterfaceIID, void** pInterfaceInstance, BOOL bSilent)
  530 {
  531     HRESULT hr = S_FALSE;
  532     ULONG ulFetched;
  533     wchar_t wPhysicalName[24];
  534     IVdsServiceLoader* pLoader;
  535     IVdsService* pService;
  536     IEnumVdsObject* pEnum;
  537     IUnknown* pUnk;
  538 
  539     *pInterfaceInstance = NULL;
  540     CheckDriveIndex(DriveIndex);
  541     wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex);
  542 
  543     // Initialize COM
  544     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  545     IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
  546         RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
  547 
  548     // Create a VDS Loader Instance
  549     hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
  550         &IID_IVdsServiceLoader, (void**)&pLoader);
  551     if (hr != S_OK) {
  552         suprintf("Could not create VDS Loader Instance: %s", VdsErrorString(hr));
  553         goto out;
  554     }
  555 
  556     // Load the VDS Service
  557     hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
  558     IVdsServiceLoader_Release(pLoader);
  559     if (hr != S_OK) {
  560         suprintf("Could not load VDS Service: %s", VdsErrorString(hr));
  561         goto out;
  562     }
  563 
  564     // Wait for the Service to become ready if needed
  565     hr = IVdsService_WaitForServiceReady(pService);
  566     if (hr != S_OK) {
  567         suprintf("VDS Service is not ready: %s", VdsErrorString(hr));
  568         goto out;
  569     }
  570 
  571     // Query the VDS Service Providers
  572     hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
  573     IVdsService_Release(pService);
  574     if (hr != S_OK) {
  575         suprintf("Could not query VDS Service Providers: %s", VdsErrorString(hr));
  576         goto out;
  577     }
  578 
  579     while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
  580         IVdsProvider* pProvider;
  581         IVdsSwProvider* pSwProvider;
  582         IEnumVdsObject* pEnumPack;
  583         IUnknown* pPackUnk;
  584 
  585         // Get VDS Provider
  586         hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void**)&pProvider);
  587         IUnknown_Release(pUnk);
  588         if (hr != S_OK) {
  589             suprintf("Could not get VDS Provider: %s", VdsErrorString(hr));
  590             break;
  591         }
  592 
  593         // Get VDS Software Provider
  594         hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void**)&pSwProvider);
  595         IVdsProvider_Release(pProvider);
  596         if (hr != S_OK) {
  597             suprintf("Could not get VDS Software Provider: %s", VdsErrorString(hr));
  598             break;
  599         }
  600 
  601         // Get VDS Software Provider Packs
  602         hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
  603         IVdsSwProvider_Release(pSwProvider);
  604         if (hr != S_OK) {
  605             suprintf("Could not get VDS Software Provider Packs: %s", VdsErrorString(hr));
  606             break;
  607         }
  608 
  609         // Enumerate Provider Packs
  610         while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
  611             IVdsPack* pPack;
  612             IEnumVdsObject* pEnumDisk;
  613             IUnknown* pDiskUnk;
  614 
  615             hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void**)&pPack);
  616             IUnknown_Release(pPackUnk);
  617             if (hr != S_OK) {
  618                 suprintf("Could not query VDS Software Provider Pack: %s", VdsErrorString(hr));
  619                 break;
  620             }
  621 
  622             // Use the pack interface to access the disks
  623             hr = IVdsPack_QueryDisks(pPack, &pEnumDisk);
  624             IVdsPack_Release(pPack);
  625             if (hr != S_OK) {
  626                 suprintf("Could not query VDS disks: %s", VdsErrorString(hr));
  627                 break;
  628             }
  629 
  630             // List disks
  631             while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFetched) == S_OK) {
  632                 VDS_DISK_PROP prop;
  633                 IVdsDisk* pDisk;
  634 
  635                 // Get the disk interface.
  636                 hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void**)&pDisk);
  637                 IUnknown_Release(pDiskUnk);
  638                 if (hr != S_OK) {
  639                     suprintf("Could not query VDS Disk Interface: %s", VdsErrorString(hr));
  640                     break;
  641                 }
  642 
  643                 // Get the disk properties
  644                 hr = IVdsDisk_GetProperties(pDisk, &prop);
  645                 if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) {
  646                     IVdsDisk_Release(pDisk);
  647                     suprintf("Could not query VDS Disk Properties: %s", VdsErrorString(hr));
  648                     break;
  649                 }
  650 
  651                 // Check if we are on the target disk
  652                 hr = (HRESULT)_wcsicmp(wPhysicalName, prop.pwszName);
  653                 CoTaskMemFree(prop.pwszName);
  654                 if (hr != S_OK) {
  655                     hr = S_OK;
  656                     continue;
  657                 }
  658 
  659                 // Instantiate the requested VDS disk interface
  660                 hr = IVdsDisk_QueryInterface(pDisk, InterfaceIID, pInterfaceInstance);
  661                 IVdsDisk_Release(pDisk);
  662                 if (hr != S_OK)
  663                     suprintf("Could not access the requested Disk interface: %s", VdsErrorString(hr));
  664 
  665                 // With the interface found, we should be able to return
  666                 break;
  667             }
  668             IEnumVdsObject_Release(pEnumDisk);
  669         }
  670         IEnumVdsObject_Release(pEnumPack);
  671     }
  672     IEnumVdsObject_Release(pEnum);
  673 
  674 out:
  675     VDS_SET_ERROR(hr);
  676     return (hr == S_OK);
  677 }
  678 
  679 /*
  680  * Delete one partition at offset PartitionOffset, or all partitions if the offset is 0.
  681  */
  682 BOOL DeletePartition(DWORD DriveIndex, ULONGLONG PartitionOffset, BOOL bSilent)
  683 {
  684     HRESULT hr = S_FALSE;
  685     VDS_PARTITION_PROP* prop_array;
  686     LONG i, prop_array_size;
  687     IVdsAdvancedDisk *pAdvancedDisk;
  688 
  689     if (!GetVdsDiskInterface(DriveIndex, &IID_IVdsAdvancedDisk, (void**)&pAdvancedDisk, bSilent))
  690         return FALSE;
  691     if (pAdvancedDisk == NULL) {
  692         suprintf("No partition to delete on disk");
  693         return TRUE;
  694     }
  695 
  696     // Query the partition data, so we can get the start offset, which we need for deletion
  697     hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size);
  698     if (hr == S_OK) {
  699         suprintf("Deleting partition%s:", (PartitionOffset == 0) ? "s" : "");
  700         // Now go through each partition
  701         for (i = 0; i < prop_array_size; i++) {
  702             if ((PartitionOffset != 0) && (prop_array[i].ullOffset != PartitionOffset))
  703                 continue;
  704             suprintf("● Partition %d (offset: %lld, size: %s)", prop_array[i].ulPartitionNumber,
  705                 prop_array[i].ullOffset, SizeToHumanReadable(prop_array[i].ullSize, FALSE, FALSE));
  706             hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE);
  707             if (hr != S_OK)
  708                 suprintf("Could not delete partition: %s", VdsErrorString(hr));
  709         }
  710     } else {
  711         suprintf("No partition to delete on disk");
  712         hr = S_OK;
  713     }
  714     CoTaskMemFree(prop_array);
  715     IVdsAdvancedDisk_Release(pAdvancedDisk);
  716     VDS_SET_ERROR(hr);
  717     return (hr == S_OK);
  718 }
  719 
  720 /*
  721  * Count on Microsoft for *COMPLETELY CRIPPLING* an API when alledgedly upgrading it...
  722  * As illustrated when you do so with diskpart (which uses VDS behind the scenes), VDS
  723  * simply *DOES NOT* list all the volumes that the system can see, especially compared
  724  * to what mountvol (which uses FindFirstVolume()/FindNextVolume()) and other APIs do.
  725  * Also for reference, if you want to list volumes through WMI in PowerShell:
  726  * Get-WmiObject win32_volume | Format-Table -Property DeviceID,Name,Label,Capacity
  727  */
  728 BOOL ListVdsVolumes(BOOL bSilent)
  729 {
  730     HRESULT hr = S_FALSE;
  731     ULONG ulFetched;
  732     IVdsServiceLoader* pLoader;
  733     IVdsService* pService;
  734     IEnumVdsObject* pEnum;
  735     IUnknown* pUnk;
  736 
  737     // Initialize COM
  738     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  739     IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
  740         RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
  741 
  742     // Create a VDS Loader Instance
  743     hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
  744         &IID_IVdsServiceLoader, (void**)&pLoader);
  745     if (hr != S_OK) {
  746         suprintf("Could not create VDS Loader Instance: %s", VdsErrorString(hr));
  747         goto out;
  748     }
  749 
  750     // Load the VDS Service
  751     hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
  752     IVdsServiceLoader_Release(pLoader);
  753     if (hr != S_OK) {
  754         suprintf("Could not load VDS Service: %s", VdsErrorString(hr));
  755         goto out;
  756     }
  757 
  758     // Wait for the Service to become ready if needed
  759     hr = IVdsService_WaitForServiceReady(pService);
  760     if (hr != S_OK) {
  761         suprintf("VDS Service is not ready: %s", VdsErrorString(hr));
  762         goto out;
  763     }
  764 
  765     // Query the VDS Service Providers
  766     hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
  767     IVdsService_Release(pService);
  768     if (hr != S_OK) {
  769         suprintf("Could not query VDS Service Providers: %s", VdsErrorString(hr));
  770         goto out;
  771     }
  772 
  773     while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
  774         IVdsProvider* pProvider;
  775         IVdsSwProvider* pSwProvider;
  776         IEnumVdsObject* pEnumPack;
  777         IUnknown* pPackUnk;
  778 
  779         // Get VDS Provider
  780         hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void**)&pProvider);
  781         IUnknown_Release(pUnk);
  782         if (hr != S_OK) {
  783             suprintf("Could not get VDS Provider: %s", VdsErrorString(hr));
  784             break;
  785         }
  786 
  787         // Get VDS Software Provider
  788         hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void**)&pSwProvider);
  789         IVdsProvider_Release(pProvider);
  790         if (hr != S_OK) {
  791             suprintf("Could not get VDS Software Provider: %s", VdsErrorString(hr));
  792             break;
  793         }
  794 
  795         // Get VDS Software Provider Packs
  796         hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
  797         IVdsSwProvider_Release(pSwProvider);
  798         if (hr != S_OK) {
  799             suprintf("Could not get VDS Software Provider Packs: %s", VdsErrorString(hr));
  800             break;
  801         }
  802 
  803         // Enumerate Provider Packs
  804         while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
  805             IVdsPack* pPack;
  806             IEnumVdsObject* pEnumVolume;
  807             IUnknown* pVolumeUnk;
  808 
  809             hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void**)&pPack);
  810             IUnknown_Release(pPackUnk);
  811             if (hr != S_OK) {
  812                 suprintf("Could not query VDS Software Provider Pack: %s", VdsErrorString(hr));
  813                 break;
  814             }
  815 
  816             // Use the pack interface to access the disks
  817             hr = IVdsPack_QueryVolumes(pPack, &pEnumVolume);
  818             if (hr != S_OK) {
  819                 suprintf("Could not query VDS volumes: %s", VdsErrorString(hr));
  820                 break;
  821             }
  822 
  823             // List volumes
  824             while (IEnumVdsObject_Next(pEnumVolume, 1, &pVolumeUnk, &ulFetched) == S_OK) {
  825                 IVdsVolume* pVolume;
  826                 IVdsVolumeMF3* pVolumeMF3;
  827                 VDS_VOLUME_PROP prop;
  828                 LPWSTR* wszPathArray;
  829                 ULONG i, ulNumberOfPaths;
  830 
  831                 // Get the volume interface.
  832                 hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVdsVolume, (void**)&pVolume);
  833                 if (hr != S_OK) {
  834                     suprintf("Could not query VDS Volume Interface: %s", VdsErrorString(hr));
  835                     break;
  836                 }
  837 
  838                 // Get the volume properties
  839                 hr = IVdsVolume_GetProperties(pVolume, &prop);
  840                 if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) {
  841                     suprintf("Could not query VDS Volume Properties: %s", VdsErrorString(hr));
  842                     break;
  843                 }
  844 
  845                 uprintf("FOUND VOLUME: '%S'", prop.pwszName);
  846                 CoTaskMemFree(prop.pwszName);
  847                 IVdsVolume_Release(pVolume);
  848 
  849                 // Get the volume MF3 interface.
  850                 hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVdsVolumeMF3, (void**)&pVolumeMF3);
  851                 if (hr != S_OK) {
  852                     suprintf("Could not query VDS VolumeMF3 Interface: %s", VdsErrorString(hr));
  853                     break;
  854                 }
  855 
  856                 // Get the volume properties
  857                 hr = IVdsVolumeMF3_QueryVolumeGuidPathnames(pVolumeMF3, &wszPathArray, &ulNumberOfPaths);
  858                 if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) {
  859                     suprintf("Could not query VDS VolumeMF3 GUID PathNames: %s", VdsErrorString(hr));
  860                     break;
  861                 }
  862                 hr = S_OK;
  863 
  864                 for (i = 0; i < ulNumberOfPaths; i++)
  865                     uprintf("  VOL GUID: '%S'", wszPathArray[i]);
  866                 CoTaskMemFree(wszPathArray);
  867                 IVdsVolume_Release(pVolumeMF3);
  868                 IUnknown_Release(pVolumeUnk);
  869             }
  870             IEnumVdsObject_Release(pEnumVolume);
  871         }
  872         IEnumVdsObject_Release(pEnumPack);
  873     }
  874     IEnumVdsObject_Release(pEnum);
  875 
  876 out:
  877     VDS_SET_ERROR(hr);
  878     return (hr == S_OK);
  879 }
  880 
  881 /* Wait for a logical drive to reappear - Used when a drive has just been repartitioned */
  882 BOOL WaitForLogical(DWORD DriveIndex, uint64_t PartitionOffset)
  883 {
  884     uint64_t EndTime;
  885     char* LogicalPath = NULL;
  886 
  887     // GetLogicalName() calls may be slow, so use the system time to
  888     // make sure we don't spend more than DRIVE_ACCESS_TIMEOUT in wait.
  889     EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT;
  890     do {
  891         LogicalPath = GetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE);
  892         // Need to filter out GlobalRoot devices as we don't want to wait on those
  893         if ((LogicalPath != NULL) && (strncmp(LogicalPath, groot_name, groot_len) != 0)) {
  894             free(LogicalPath);
  895             return TRUE;
  896         }
  897         free(LogicalPath);
  898         if (IS_ERROR(FormatStatus)) // User cancel
  899             return FALSE;
  900         Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
  901     } while (GetTickCount64() < EndTime);
  902     uprintf("Timeout while waiting for logical drive");
  903     return FALSE;
  904 }
  905 
  906 /*
  907  * Obtain a handle to the volume identified by DriveIndex + PartitionIndex
  908  * Returns INVALID_HANDLE_VALUE on error or NULL if no logical path exists (typical
  909  * of unpartitioned drives)
  910  */
  911 HANDLE GetLogicalHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
  912 {
  913     HANDLE hLogical = INVALID_HANDLE_VALUE;
  914     char* LogicalPath = GetLogicalName(DriveIndex, PartitionOffset, FALSE, FALSE);
  915 
  916     if (LogicalPath == NULL) {
  917         uprintf("No logical drive found (unpartitioned?)");
  918         return NULL;
  919     }
  920 
  921     hLogical = GetHandle(LogicalPath, bLockDrive, bWriteAccess, bWriteShare);
  922     free(LogicalPath);
  923     return hLogical;
  924 }
  925 
  926 /*
  927  * Who would have thought that Microsoft would make it so unbelievably hard to
  928  * get the frickin' device number for a drive? You have to use TWO different
  929  * methods to have a chance to get it!
  930  */
  931 int GetDriveNumber(HANDLE hDrive, char* path)
  932 {
  933     STORAGE_DEVICE_NUMBER_REDEF DeviceNumber;
  934     VOLUME_DISK_EXTENTS_REDEF DiskExtents;
  935     DWORD size;
  936     int r = -1;
  937 
  938     if (!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
  939         &DiskExtents, sizeof(DiskExtents), &size, NULL) || (size <= 0) || (DiskExtents.NumberOfDiskExtents < 1) ) {
  940         // DiskExtents are NO_GO (which is the case for external USB HDDs...)
  941         if(!DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
  942             &DeviceNumber, sizeof(DeviceNumber), &size, NULL ) || (size <= 0)) {
  943             uprintf("Could not get device number for device %s: %s", path, WindowsErrorString());
  944             return -1;
  945         }
  946         r = (int)DeviceNumber.DeviceNumber;
  947     } else if (DiskExtents.NumberOfDiskExtents >= 2) {
  948         uprintf("Ignoring drive '%s' as it spans multiple disks (RAID?)", path);
  949         return -1;
  950     } else {
  951         r = (int)DiskExtents.Extents[0].DiskNumber;
  952     }
  953     if (r >= MAX_DRIVES) {
  954         uprintf("Device Number for device %s is too big (%d) - ignoring device", path, r);
  955         return -1;
  956     }
  957     return r;
  958 }
  959 
  960 /*
  961  * Returns the drive letters for all volumes located on the drive identified by DriveIndex,
  962  * as well as the drive type. This is used as base for the 2 function calls that follow.
  963  */
  964 static BOOL _GetDriveLettersAndType(DWORD DriveIndex, char* drive_letters, UINT* drive_type)
  965 {
  966     DWORD size;
  967     BOOL r = FALSE;
  968     HANDLE hDrive = INVALID_HANDLE_VALUE, hPhysical = INVALID_HANDLE_VALUE;
  969     UINT _drive_type;
  970     IO_STATUS_BLOCK io_status_block;
  971     FILE_FS_DEVICE_INFORMATION file_fs_device_info;
  972     BYTE geometry[256] = { 0 };
  973     PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
  974     int i = 0, drives_found = 0, drive_number;
  975     char *drive, drives[26*4 + 1];  /* "D:\", "E:\", etc., plus one NUL */
  976     char logical_drive[] = "\\\\.\\#:";
  977 
  978     PF_INIT(NtQueryVolumeInformationFile, Ntdll);
  979 
  980     if (drive_letters != NULL)
  981         drive_letters[0] = 0;
  982     if (drive_type != NULL)
  983         *drive_type = DRIVE_UNKNOWN;
  984     CheckDriveIndex(DriveIndex);
  985 
  986     // This call is weird... The buffer needs to have an extra NUL, but you're
  987     // supposed to provide the size without the extra NUL. And the returned size
  988     // does not include the NUL either *EXCEPT* if your buffer is too small...
  989     // But then again, this doesn't hold true if you have a 105 byte buffer and
  990     // pass a 4*26=104 size, as the the call will return 105 (i.e. *FAILURE*)
  991     // instead of 104 as it should => screw Microsoft: We'll include the NUL
  992     // always, as each drive string is at least 4 chars long anyway.
  993     size = GetLogicalDriveStringsA(sizeof(drives), drives);
  994     if (size == 0) {
  995         uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
  996         goto out;
  997     }
  998     if (size > sizeof(drives)) {
  999         uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
 1000         goto out;
 1001     }
 1002 
 1003     r = TRUE;   // Required to detect drives that don't have volumes assigned
 1004     for (drive = drives ;*drive; drive += safe_strlen(drive) + 1) {
 1005         if (!isalpha(*drive))
 1006             continue;
 1007         *drive = (char)toupper((int)*drive);
 1008 
 1009         // IOCTL_STORAGE_GET_DEVICE_NUMBER's STORAGE_DEVICE_NUMBER.DeviceNumber is
 1010         // not unique! An HDD, a DVD and probably other drives can have the same
 1011         // value there => Use GetDriveType() to filter out unwanted devices.
 1012         // See https://github.com/pbatard/rufus/issues/32#issuecomment-3785956
 1013         _drive_type = GetDriveTypeA(drive);
 1014 
 1015         if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED))
 1016             continue;
 1017 
 1018         static_sprintf(logical_drive, "\\\\.\\%c:", drive[0]);
 1019         hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
 1020             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 1021         if (hDrive == INVALID_HANDLE_VALUE) {
 1022 //          uprintf("Warning: could not open drive %c: %s", drive[0], WindowsErrorString());
 1023             continue;
 1024         }
 1025 
 1026         // Eliminate floppy drives
 1027         if ((pfNtQueryVolumeInformationFile != NULL) &&
 1028             (pfNtQueryVolumeInformationFile(hDrive, &io_status_block, &file_fs_device_info,
 1029                 sizeof(file_fs_device_info), FileFsDeviceInformation) == NO_ERROR) &&
 1030             (file_fs_device_info.Characteristics & FILE_FLOPPY_DISKETTE) ) {
 1031                 continue;
 1032         }
 1033 
 1034         drive_number = GetDriveNumber(hDrive, logical_drive);
 1035         safe_closehandle(hDrive);
 1036         if (drive_number == DriveIndex) {
 1037             r = TRUE;
 1038             drives_found++;
 1039             if (drive_letters != NULL)
 1040                 drive_letters[i++] = *drive;
 1041             // The drive type should be the same for all volumes, so we can overwrite
 1042             if (drive_type != NULL)
 1043                 *drive_type = _drive_type;
 1044         }
 1045     }
 1046 
 1047     // Devices that don't have mounted partitions require special
 1048     // handling to determine if they are fixed or removable.
 1049     if ((drives_found == 0) && (drive_type != NULL)) {
 1050         hPhysical = GetPhysicalHandle(DriveIndex + DRIVE_INDEX_MIN, FALSE, FALSE, FALSE);
 1051         r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
 1052             NULL, 0, geometry, sizeof(geometry), &size, NULL);
 1053         safe_closehandle(hPhysical);
 1054         if (r && size > 0) {
 1055             if (DiskGeometry->Geometry.MediaType == FixedMedia)
 1056                 *drive_type = DRIVE_FIXED;
 1057             else if (DiskGeometry->Geometry.MediaType == RemovableMedia)
 1058                 *drive_type = DRIVE_REMOVABLE;
 1059         }
 1060     }
 1061 
 1062 out:
 1063     if (drive_letters != NULL)
 1064         drive_letters[i] = 0;
 1065     return r;
 1066 }
 1067 
 1068 // Could have used a #define, but this is clearer
 1069 BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters)
 1070 {
 1071     return _GetDriveLettersAndType(DriveIndex, drive_letters, NULL);
 1072 }
 1073 
 1074 // There's already a GetDriveType in the Windows API
 1075 UINT GetDriveTypeFromIndex(DWORD DriveIndex)
 1076 {
 1077     UINT drive_type;
 1078     _GetDriveLettersAndType(DriveIndex, NULL, &drive_type);
 1079     return drive_type;
 1080 }
 1081 
 1082 // Removes all drive letters associated with the specific drive, and return
 1083 // either the first or last letter that was removed, according to bReturnLast.
 1084 char RemoveDriveLetters(DWORD DriveIndex, BOOL bReturnLast, BOOL bSilent)
 1085 {
 1086     int i, len;
 1087     char drive_letters[27] = { 0 }, drive_name[4] = "#:\\";
 1088 
 1089     if (!GetDriveLetters(DriveIndex, drive_letters)) {
 1090         suprintf("Failed to get a drive letter");
 1091         return 0;
 1092     }
 1093     if (drive_letters[0] == 0) {
 1094         suprintf("No drive letter was assigned...");
 1095         return GetUnusedDriveLetter();
 1096     }
 1097     len = (int)strlen(drive_letters);
 1098     if (len == 0)
 1099         return 0;
 1100 
 1101     // Unmount all mounted volumes that belong to this drive
 1102     for (i = 0; i < len; i++) {
 1103         // Check that the current image isn't located on a drive we are trying to dismount
 1104         if ((boot_type == BT_IMAGE) && (drive_letters[i] == (PathGetDriveNumberU(image_path) + 'A'))) {
 1105             if ((PathGetDriveNumberU(image_path) + 'A') == drive_letters[i]) {
 1106                 suprintf("ABORTED: Cannot use an image that is located on the target drive!");
 1107                 return 0;
 1108             }
 1109         }
 1110         drive_name[0] = drive_letters[i];
 1111         // DefineDosDevice() cannot have a trailing backslash...
 1112         drive_name[2] = 0;
 1113         DefineDosDeviceA(DDD_REMOVE_DEFINITION, drive_name, NULL);
 1114         // ... but DeleteVolumeMountPoint() requires one. Go figure...
 1115         drive_name[2] = '\\';
 1116         if (!DeleteVolumeMountPointA(drive_name))
 1117             suprintf("Failed to delete mountpoint %s: %s", drive_name, WindowsErrorString());
 1118     }
 1119     return drive_letters[bReturnLast ? (len - 1) : 0];
 1120 }
 1121 
 1122 /*
 1123  * Return the next unused drive letter from the system or NUL on error.
 1124  */
 1125 char GetUnusedDriveLetter(void)
 1126 {
 1127     DWORD size;
 1128     char drive_letter, *drive, drives[26*4 + 1];    /* "D:\", "E:\", etc., plus one NUL */
 1129 
 1130     size = GetLogicalDriveStringsA(sizeof(drives), drives);
 1131     if (size == 0) {
 1132         uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
 1133         return 0;
 1134     }
 1135     if (size > sizeof(drives)) {
 1136         uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
 1137         return 0;
 1138     }
 1139 
 1140     for (drive_letter = 'C'; drive_letter <= 'Z'; drive_letter++) {
 1141         for (drive = drives ; *drive; drive += safe_strlen(drive) + 1) {
 1142             if (!isalpha(*drive))
 1143                 continue;
 1144             if (drive_letter == (char)toupper((int)*drive))
 1145                 break;
 1146         }
 1147         if (!*drive)
 1148             break;
 1149     }
 1150 
 1151     return (drive_letter > 'Z') ? 0 : drive_letter;
 1152 }
 1153 
 1154 BOOL IsDriveLetterInUse(const char drive_letter)
 1155 {
 1156     DWORD size;
 1157     char *drive, drives[26 * 4 + 1];
 1158 
 1159     size = GetLogicalDriveStringsA(sizeof(drives), drives);
 1160     if (size == 0) {
 1161         uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
 1162         return TRUE;
 1163     }
 1164     if (size > sizeof(drives)) {
 1165         uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
 1166         return TRUE;
 1167     }
 1168 
 1169     for (drive = drives; *drive; drive += safe_strlen(drive) + 1) {
 1170         if (drive_letter == (char)toupper((int)*drive))
 1171             return TRUE;
 1172     }
 1173 
 1174     return FALSE;
 1175 }
 1176 
 1177 /*
 1178  * Return the drive letter and volume label
 1179  * If the drive doesn't have a volume assigned, space is returned for the letter
 1180  */
 1181 BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label)
 1182 {
 1183     HANDLE hPhysical;
 1184     DWORD size, error;
 1185     static char VolumeLabel[MAX_PATH + 1] = { 0 };
 1186     char DrivePath[] = "#:\\", AutorunPath[] = "#:\\autorun.inf", *AutorunLabel = NULL;
 1187     WCHAR VolumeName[MAX_PATH + 1] = { 0 }, FileSystemName[64];
 1188     DWORD VolumeSerialNumber, MaximumComponentLength, FileSystemFlags;
 1189 
 1190     *label = STR_NO_LABEL;
 1191 
 1192     if (!GetDriveLetters(DriveIndex, letters))
 1193         return FALSE;
 1194     if (letters[0] == 0) {
 1195         // Even if we don't have a letter, try to obtain the label of the first partition
 1196         HANDLE h = GetLogicalHandle(DriveIndex, 0, FALSE, FALSE, FALSE);
 1197         if (GetVolumeInformationByHandleW(h, VolumeName, 64, &VolumeSerialNumber,
 1198             &MaximumComponentLength, &FileSystemFlags, FileSystemName, 64)) {
 1199             wchar_to_utf8_no_alloc(VolumeName, VolumeLabel, sizeof(VolumeLabel));
 1200             *label = (VolumeLabel[0] != 0) ? VolumeLabel : STR_NO_LABEL;
 1201         }
 1202         safe_closehandle(h);
 1203         // Drive without volume assigned - always enabled
 1204         return TRUE;
 1205     }
 1206     // We only care about an autorun.inf if we have a single volume
 1207     AutorunPath[0] = letters[0];
 1208     DrivePath[0] = letters[0];
 1209 
 1210     // Try to read an extended label from autorun first. Fallback to regular label if not found.
 1211     // In the case of card readers with no card, users can get an annoying popup asking them
 1212     // to insert media. Use IOCTL_STORAGE_CHECK_VERIFY to prevent this
 1213     hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
 1214     if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL))
 1215         AutorunLabel = get_token_data_file("label", AutorunPath);
 1216     else if (GetLastError() == ERROR_NOT_READY)
 1217         uprintf("Ignoring autorun.inf label for drive %c: %s", letters[0],
 1218         (HRESULT_CODE(GetLastError()) == ERROR_NOT_READY)?"No media":WindowsErrorString());
 1219     safe_closehandle(hPhysical);
 1220     if (AutorunLabel != NULL) {
 1221         uprintf("Using autorun.inf label for drive %c: '%s'", letters[0], AutorunLabel);
 1222         static_strcpy(VolumeLabel, AutorunLabel);
 1223         safe_free(AutorunLabel);
 1224         *label = VolumeLabel;
 1225     } else if (GetVolumeInformationU(DrivePath, VolumeLabel, ARRAYSIZE(VolumeLabel),
 1226         NULL, NULL, NULL, NULL, 0) && (VolumeLabel[0] != 0)) {
 1227         *label = VolumeLabel;
 1228     } else {
 1229         // Might be an extfs label
 1230         error = GetLastError();
 1231         *label = (char*)GetExtFsLabel(DriveIndex, 0);
 1232         if (*label == NULL) {
 1233             SetLastError(error);
 1234             if (error != ERROR_UNRECOGNIZED_VOLUME)
 1235                 duprintf("Failed to read label: %s", WindowsErrorString());
 1236             *label = STR_NO_LABEL;
 1237         }
 1238     }
 1239     return TRUE;
 1240 }
 1241 
 1242 /*
 1243  * Return the drive size
 1244  */
 1245 uint64_t GetDriveSize(DWORD DriveIndex)
 1246 {
 1247     BOOL r;
 1248     HANDLE hPhysical;
 1249     DWORD size;
 1250     BYTE geometry[256];
 1251     PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
 1252 
 1253     hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
 1254     if (hPhysical == INVALID_HANDLE_VALUE)
 1255         return FALSE;
 1256 
 1257     r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
 1258         NULL, 0, geometry, sizeof(geometry), &size, NULL);
 1259     safe_closehandle(hPhysical);
 1260     if (!r || size <= 0)
 1261         return 0;
 1262     return DiskGeometry->DiskSize.QuadPart;
 1263 }
 1264 
 1265 /*
 1266  * GET_DRIVE_GEOMETRY is used to tell if there is an actual media
 1267  */
 1268 BOOL IsMediaPresent(DWORD DriveIndex)
 1269 {
 1270     BOOL r;
 1271     HANDLE hPhysical;
 1272     DWORD size;
 1273     BYTE geometry[128];
 1274 
 1275     hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
 1276     r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
 1277             NULL, 0, geometry, sizeof(geometry), &size, NULL) && (size > 0);
 1278     safe_closehandle(hPhysical);
 1279     return r;
 1280 }
 1281 
 1282 const struct {int (*fn)(FILE *fp); char* str;} known_mbr[] = {
 1283     { is_dos_mbr, "DOS/NT/95A" },
 1284     { is_dos_f2_mbr, "DOS/NT/95A (F2)" },
 1285     { is_95b_mbr, "Windows 95B/98/98SE/ME" },
 1286     { is_2000_mbr, "Windows 2000/XP/2003" },
 1287     { is_vista_mbr, "Windows Vista" },
 1288     { is_win7_mbr, "Windows 7" },
 1289     { is_rufus_mbr, "Rufus" },
 1290     { is_syslinux_mbr, "Syslinux" },
 1291     { is_reactos_mbr, "ReactOS" },
 1292     { is_kolibrios_mbr, "KolibriOS" },
 1293     { is_grub4dos_mbr, "Grub4DOS" },
 1294     { is_grub2_mbr, "Grub 2.0" },
 1295     { is_zero_mbr_not_including_disk_signature_or_copy_protect, "Zeroed" },
 1296 };
 1297 
 1298 // Returns TRUE if the drive seems bootable, FALSE otherwise
 1299 BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent)
 1300 {
 1301     FAKE_FD fake_fd = { 0 };
 1302     FILE* fp = (FILE*)&fake_fd;
 1303     int i;
 1304 
 1305     fake_fd._handle = (char*)hPhysicalDrive;
 1306     set_bytes_per_sector(SelectedDrive.SectorSize);
 1307 
 1308     if (!is_br(fp)) {
 1309         suprintf("%s does not have a Boot Marker", TargetName);
 1310         return FALSE;
 1311     }
 1312     for (i=0; i<ARRAYSIZE(known_mbr); i++) {
 1313         if (known_mbr[i].fn(fp)) {
 1314             suprintf("%s has a %s Master Boot Record", TargetName, known_mbr[i].str);
 1315             return TRUE;
 1316         }
 1317     }
 1318 
 1319     suprintf("%s has an unknown Master Boot Record", TargetName);
 1320     return TRUE;
 1321 }
 1322 
 1323 const struct {int (*fn)(FILE *fp); char* str;} known_pbr[] = {
 1324     { entire_fat_16_br_matches, "FAT16 DOS" },
 1325     { entire_fat_16_fd_br_matches, "FAT16 FreeDOS" },
 1326     { entire_fat_16_ros_br_matches, "FAT16 ReactOS" },
 1327     { entire_fat_32_br_matches, "FAT32 DOS" },
 1328     { entire_fat_32_nt_br_matches, "FAT32 NT" },
 1329     { entire_fat_32_fd_br_matches, "FAT32 FreeDOS" },
 1330     { entire_fat_32_ros_br_matches, "FAT32 ReactOS" },
 1331     { entire_fat_32_kos_br_matches, "FAT32 KolibriOS" },
 1332 };
 1333 
 1334 BOOL AnalyzePBR(HANDLE hLogicalVolume)
 1335 {
 1336     const char* pbr_name = "Partition Boot Record";
 1337     FAKE_FD fake_fd = { 0 };
 1338     FILE* fp = (FILE*)&fake_fd;
 1339     int i;
 1340 
 1341     fake_fd._handle = (char*)hLogicalVolume;
 1342     set_bytes_per_sector(SelectedDrive.SectorSize);
 1343 
 1344     if (!is_br(fp)) {
 1345         uprintf("Volume does not have an x86 %s", pbr_name);
 1346         return FALSE;
 1347     }
 1348 
 1349     if (is_fat_16_br(fp) || is_fat_32_br(fp)) {
 1350         for (i=0; i<ARRAYSIZE(known_pbr); i++) {
 1351             if (known_pbr[i].fn(fp)) {
 1352                 uprintf("Drive has a %s %s", known_pbr[i].str, pbr_name);
 1353                 return TRUE;
 1354             }
 1355         }
 1356         uprintf("Volume has an unknown FAT16 or FAT32 %s", pbr_name);
 1357     } else {
 1358         uprintf("Volume has an unknown %s", pbr_name);
 1359     }
 1360     return TRUE;
 1361 }
 1362 
 1363 static BOOL StoreEspInfo(GUID* guid)
 1364 {
 1365     uint8_t j;
 1366     char key_name[2][16], *str;
 1367     // Look for an empty slot and use that if available
 1368     for (j = 1; j <= MAX_ESP_TOGGLE; j++) {
 1369         static_sprintf(key_name[0], "ToggleEsp%02u", j);
 1370         str = ReadSettingStr(key_name[0]);
 1371         if ((str == NULL) || (str[0] == 0))
 1372             return WriteSettingStr(key_name[0], GuidToString(guid));
 1373     }
 1374     // All slots are used => Move every key down and add to last slot
 1375     // NB: No, we don't care that the slot we remove may not be the oldest.
 1376     for (j = 1; j < MAX_ESP_TOGGLE; j++) {
 1377         static_sprintf(key_name[0], "ToggleEsp%02u", j);
 1378         static_sprintf(key_name[1], "ToggleEsp%02u", j + 1);
 1379         WriteSettingStr(key_name[0], ReadSettingStr(key_name[1]));
 1380     }
 1381     return WriteSettingStr(key_name[1], GuidToString(guid));
 1382 }
 1383 
 1384 static GUID* GetEspGuid(uint8_t index)
 1385 {
 1386     char key_name[16];
 1387 
 1388     static_sprintf(key_name, "ToggleEsp%02u", index);
 1389     return StringToGuid(ReadSettingStr(key_name));
 1390 }
 1391 
 1392 static BOOL ClearEspInfo(uint8_t index)
 1393 {
 1394     char key_name[16];
 1395     static_sprintf(key_name, "ToggleEsp%02u", index);
 1396     return WriteSettingStr(key_name, "");
 1397 }
 1398 
 1399 /*
 1400  * This calls changes the type of a GPT ESP back and forth to Basic Data.
 1401  * Needed because Windows 10 doesn't mount ESPs by default, and also
 1402  * doesn't let usermode apps (such as File Explorer) access mounted ESPs.
 1403  */
 1404 BOOL ToggleEsp(DWORD DriveIndex, uint64_t PartitionOffset)
 1405 {
 1406     char *volume_name, mount_point[] = DEFAULT_ESP_MOUNT_POINT;
 1407     BOOL r, ret = FALSE, found = FALSE;
 1408     HANDLE hPhysical;
 1409     DWORD size, i, j, esp_index = 0;
 1410     BYTE layout[4096] = { 0 };
 1411     GUID* guid;
 1412     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout;
 1413 
 1414     if ((PartitionOffset == 0) && (nWindowsVersion < WINDOWS_10)) {
 1415         uprintf("ESP toggling is only available for Windows 10 or later");
 1416         return FALSE;
 1417     }
 1418 
 1419     hPhysical = GetPhysicalHandle(DriveIndex, FALSE, TRUE, TRUE);
 1420     if (hPhysical == INVALID_HANDLE_VALUE)
 1421         return FALSE;
 1422 
 1423     r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
 1424         NULL, 0, layout, sizeof(layout), &size, NULL);
 1425     if (!r || size <= 0) {
 1426         uprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
 1427         goto out;
 1428     }
 1429     // TODO: Handle MBR
 1430     if (DriveLayout->PartitionStyle != PARTITION_STYLE_GPT) {
 1431         uprintf("ESP toggling is only available for GPT drives");
 1432         goto out;
 1433     }
 1434 
 1435     if (PartitionOffset == 0) {
 1436         // See if the current drive contains an ESP
 1437         for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) {
 1438             if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) {
 1439                 esp_index = i;
 1440                 j++;
 1441             }
 1442         }
 1443 
 1444         if (j > 1) {
 1445             uprintf("ESP toggling is not available for drives with more than one ESP");
 1446             goto out;
 1447         }
 1448         if (j == 1) {
 1449             // ESP -> Basic Data
 1450             i = esp_index;
 1451             uprintf("ESP name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name);
 1452             if (!StoreEspInfo(&DriveLayout->PartitionEntry[i].Gpt.PartitionId)) {
 1453                 uprintf("ESP toggling data could not be stored");
 1454                 goto out;
 1455             }
 1456             DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_MICROSOFT_DATA;
 1457         } else {
 1458             // Basic Data -> ESP
 1459             for (j = 1; j <= MAX_ESP_TOGGLE; j++) {
 1460                 guid = GetEspGuid((uint8_t)j);
 1461                 if (guid != NULL) {
 1462                     for (i = 0; i < DriveLayout->PartitionCount; i++) {
 1463                         if (CompareGUID(guid, &DriveLayout->PartitionEntry[i].Gpt.PartitionId)) {
 1464                             found = TRUE;
 1465                             break;
 1466                         }
 1467                     }
 1468                     if (found)
 1469                         break;
 1470                 }
 1471             }
 1472             if (j > MAX_ESP_TOGGLE)
 1473                 goto out;
 1474             DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP;
 1475         }
 1476     } else {
 1477         for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) {
 1478             if (DriveLayout->PartitionEntry[i].StartingOffset.QuadPart == PartitionOffset) {
 1479                 DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP;
 1480                 break;
 1481             }
 1482         }
 1483     }
 1484     if (i >= DriveLayout->PartitionCount) {
 1485         uprintf("No partition to toggle");
 1486         goto out;
 1487     }
 1488 
 1489     DriveLayout->PartitionEntry[i].RewritePartition = TRUE; // Just in case
 1490     r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)DriveLayout, size, NULL, 0, &size, NULL);
 1491     if (!r) {
 1492         uprintf("Could not set drive layout: %s", WindowsErrorString());
 1493         goto out;
 1494     }
 1495     RefreshDriveLayout(hPhysical);
 1496     if (PartitionOffset == 0) {
 1497         if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) {
 1498             // We successfully reverted ESP from Basic Data -> Delete stored ESP info
 1499             ClearEspInfo((uint8_t)j);
 1500         } else if (!IsDriveLetterInUse(*mount_point)) {
 1501             // We succesfully switched ESP to Basic Data -> Try to mount it
 1502             volume_name = GetLogicalName(DriveIndex, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart, TRUE, FALSE);
 1503             IGNORE_RETVAL(MountVolume(mount_point, volume_name));
 1504             free(volume_name);
 1505         }
 1506     }
 1507     ret = TRUE;
 1508 
 1509 out:
 1510     safe_closehandle(hPhysical);
 1511     return ret;
 1512 }
 1513 
 1514 /*
 1515  * Fill the drive properties (size, FS, etc)
 1516  * Returns TRUE if the drive has a partition that can be mounted in Windows, FALSE otherwise
 1517  */
 1518 BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSystemNameSize, BOOL bSilent)
 1519 {
 1520     // MBR partition types that can be mounted in Windows
 1521     const uint8_t mbr_mountable[] = { 0x01, 0x04, 0x06, 0x07, 0x0b, 0x0c, 0x0e, 0xef };
 1522     BOOL r, ret = FALSE, isUefiNtfs;
 1523     HANDLE hPhysical;
 1524     DWORD size, i, j, super_floppy_disk = FALSE;
 1525     BYTE geometry[256] = {0}, layout[4096] = {0}, part_type;
 1526     PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
 1527     PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout;
 1528     char *volume_name, *buf;
 1529 
 1530     if (FileSystemName == NULL)
 1531         return FALSE;
 1532 
 1533     SelectedDrive.nPartitions = 0;
 1534     memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
 1535     memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));
 1536     // Populate the filesystem data
 1537     FileSystemName[0] = 0;
 1538     volume_name = GetLogicalName(DriveIndex, 0, TRUE, FALSE);
 1539     if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) {
 1540         suprintf("No volume information for drive 0x%02x", DriveIndex);
 1541     }
 1542     safe_free(volume_name);
 1543 
 1544     hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
 1545     if (hPhysical == INVALID_HANDLE_VALUE)
 1546         return FALSE;
 1547 
 1548     r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
 1549             NULL, 0, geometry, sizeof(geometry), &size, NULL);
 1550     if (!r || size <= 0) {
 1551         suprintf("Could not get geometry for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
 1552         safe_closehandle(hPhysical);
 1553         return FALSE;
 1554     }
 1555     SelectedDrive.DiskSize = DiskGeometry->DiskSize.QuadPart;
 1556     SelectedDrive.SectorSize = DiskGeometry->Geometry.BytesPerSector;
 1557     SelectedDrive.FirstDataSector = MAXDWORD;
 1558     if (SelectedDrive.SectorSize < 512) {
 1559         suprintf("Warning: Drive 0x%02x reports a sector size of %d - Correcting to 512 bytes.",
 1560             DriveIndex, SelectedDrive.SectorSize);
 1561         SelectedDrive.SectorSize = 512;
 1562     }
 1563     SelectedDrive.SectorsPerTrack = DiskGeometry->Geometry.SectorsPerTrack;
 1564     SelectedDrive.MediaType = DiskGeometry->Geometry.MediaType;
 1565 
 1566     suprintf("Disk type: %s, Disk size: %s, Sector size: %d bytes", (SelectedDrive.MediaType == FixedMedia)?"FIXED":"Removable",
 1567         SizeToHumanReadable(SelectedDrive.DiskSize, FALSE, TRUE), SelectedDrive.SectorSize);
 1568     suprintf("Cylinders: %" PRIi64 ", Tracks per cylinder: %d, Sectors per track: %d",
 1569         DiskGeometry->Geometry.Cylinders, DiskGeometry->Geometry.TracksPerCylinder, DiskGeometry->Geometry.SectorsPerTrack);
 1570 
 1571     r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
 1572             NULL, 0, layout, sizeof(layout), &size, NULL );
 1573     if (!r || size <= 0) {
 1574         suprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
 1575         safe_closehandle(hPhysical);
 1576         return FALSE;
 1577     }
 1578 
 1579     switch (DriveLayout->PartitionStyle) {
 1580     case PARTITION_STYLE_MBR:
 1581         SelectedDrive.PartitionStyle = PARTITION_STYLE_MBR;
 1582         for (i = 0; i < DriveLayout->PartitionCount; i++) {
 1583             if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) {
 1584                 SelectedDrive.nPartitions++;
 1585             }
 1586         }
 1587         // Detect drives that are using the whole disk as a single partition
 1588         if ((DriveLayout->PartitionEntry[0].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) &&
 1589             (DriveLayout->PartitionEntry[0].StartingOffset.QuadPart == 0LL)) {
 1590             suprintf("Partition type: SFD (%s) or unpartitioned", sfd_name);
 1591             super_floppy_disk = TRUE;
 1592         } else {
 1593             suprintf("Partition type: MBR, NB Partitions: %d", SelectedDrive.nPartitions);
 1594             SelectedDrive.has_mbr_uefi_marker = (DriveLayout->Mbr.Signature == MBR_UEFI_MARKER);
 1595             suprintf("Disk ID: 0x%08X %s", DriveLayout->Mbr.Signature, SelectedDrive.has_mbr_uefi_marker?"(UEFI target)":"");
 1596             AnalyzeMBR(hPhysical, "Drive", bSilent);
 1597         }
 1598         for (i = 0; i < DriveLayout->PartitionCount; i++) {
 1599             isUefiNtfs = FALSE;
 1600             if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) {
 1601                 part_type = DriveLayout->PartitionEntry[i].Mbr.PartitionType;
 1602                 // Microsoft will have to explain why they completely ignore the actual MBR partition
 1603                 // type for zeroed drive (which *IS* 0x00) and fill in Small FAT16 instead (0x04).
 1604                 // This means that if we detect a Small FAT16 "partition", that "starts" at offset 0
 1605                 // and that is larger than 16 MB, our drive is actually unpartitioned. 
 1606                 if (part_type == 0x04 && super_floppy_disk && SelectedDrive.DiskSize > 16 * MB)
 1607                     break;
 1608                 if (part_type == 0xef) {
 1609                     // Check the FAT label to see if we're dealing with an UEFI_NTFS partition
 1610                     buf = calloc(SelectedDrive.SectorSize, 1);
 1611                     if (buf != NULL) {
 1612                         if (SetFilePointerEx(hPhysical, DriveLayout->PartitionEntry[i].StartingOffset, NULL, FILE_BEGIN) &&
 1613                             ReadFile(hPhysical, buf, SelectedDrive.SectorSize, &size, NULL)) {
 1614                             isUefiNtfs = (strncmp(&buf[0x2B], "UEFI_NTFS", 9) == 0);
 1615                         }
 1616                         free(buf);
 1617                     }
 1618                 }
 1619                 suprintf("Partition %d%s:", i + (super_floppy_disk ? 0 : 1), isUefiNtfs ? " (UEFI:NTFS)" : "");
 1620                 for (j = 0; j < ARRAYSIZE(mbr_mountable); j++) {
 1621                     if (part_type == mbr_mountable[j]) {
 1622                         ret = TRUE;
 1623                         break;
 1624                     }
 1625                 }
 1626                 if (i < MAX_PARTITIONS) {
 1627                     SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
 1628                     SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
 1629                 }
 1630                 // NB: MinGW's gcc 4.9.2 broke "%lld" printout on XP so we use the inttypes.h "PRI##" qualifiers
 1631                 suprintf("  Type: %s (0x%02x)\r\n  Size: %s (%" PRIi64 " bytes)\r\n  Start Sector: %" PRIi64 ", Boot: %s",
 1632                     ((part_type == 0x07 || super_floppy_disk) && (FileSystemName[0] != 0)) ?
 1633                     FileSystemName : GetMBRPartitionType(part_type), super_floppy_disk ? 0: part_type,
 1634                     SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE),
 1635                     DriveLayout->PartitionEntry[i].PartitionLength.QuadPart,
 1636                     DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize,
 1637                     DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No");
 1638                 // suprintf("  GUID: %s", GuidToString(&DriveLayout->PartitionEntry[i].Mbr.PartitionId));
 1639                 SelectedDrive.FirstDataSector = min(SelectedDrive.FirstDataSector,
 1640                     (DWORD)(DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize));
 1641                 if ((part_type == RUFUS_EXTRA_PARTITION_TYPE) || (isUefiNtfs))
 1642                     // This is a partition Rufus created => we can safely ignore it
 1643                     --SelectedDrive.nPartitions;
 1644                 if (part_type == 0xee)  // Flag a protective MBR for non GPT platforms (XP)
 1645                     SelectedDrive.has_protective_mbr = TRUE;
 1646             }
 1647         }
 1648         break;
 1649     case PARTITION_STYLE_GPT:
 1650         SelectedDrive.PartitionStyle = PARTITION_STYLE_GPT;
 1651         suprintf("Partition type: GPT, NB Partitions: %d", DriveLayout->PartitionCount);
 1652         suprintf("Disk GUID: %s", GuidToString(&DriveLayout->Gpt.DiskId));
 1653         suprintf("Max parts: %d, Start Offset: %" PRIi64 ", Usable = %" PRIi64 " bytes",
 1654             DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart);
 1655         for (i = 0; i < DriveLayout->PartitionCount; i++) {
 1656             if (i < MAX_PARTITIONS) {
 1657                 SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
 1658                 SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
 1659             }
 1660             SelectedDrive.nPartitions++;
 1661             isUefiNtfs = (wcscmp(DriveLayout->PartitionEntry[i].Gpt.Name, L"UEFI:NTFS") == 0);
 1662             suprintf("Partition %d%s:\r\n  Type: %s", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "",
 1663                 GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType));
 1664             if (DriveLayout->PartitionEntry[i].Gpt.Name[0] != 0)
 1665                 suprintf("  Name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name);
 1666             suprintf("  ID: %s\r\n  Size: %s (%" PRIi64 " bytes)\r\n  Start Sector: %" PRIi64 ", Attributes: 0x%016" PRIX64,
 1667                 GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId),
 1668                 SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE),
 1669                 DriveLayout->PartitionEntry[i].PartitionLength,
 1670                 DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize,
 1671                 DriveLayout->PartitionEntry[i].Gpt.Attributes);
 1672             SelectedDrive.FirstDataSector = min(SelectedDrive.FirstDataSector,
 1673                 (DWORD)(DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize));
 1674             // Don't register the partitions that we don't care about destroying
 1675             if ( isUefiNtfs ||
 1676                  (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MICROSOFT_RESERVED)) ||
 1677                  (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) )
 1678                 --SelectedDrive.nPartitions;
 1679             if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MICROSOFT_DATA))
 1680                 ret = TRUE;
 1681         }
 1682         break;
 1683     default:
 1684         SelectedDrive.PartitionStyle = PARTITION_STYLE_MBR;
 1685         suprintf("Partition type: RAW");
 1686         break;
 1687     }
 1688 #if defined(__GNUC__)
 1689 #pragma GCC diagnostic warning "-Warray-bounds"
 1690 #endif
 1691     safe_closehandle(hPhysical);
 1692 
 1693     return ret;
 1694 }
 1695 
 1696 /*
 1697  * Flush file data
 1698  */
 1699 static BOOL FlushDrive(char drive_letter)
 1700 {
 1701     HANDLE hDrive = INVALID_HANDLE_VALUE;
 1702     BOOL r = FALSE;
 1703     char logical_drive[] = "\\\\.\\#:";
 1704 
 1705     logical_drive[4] = drive_letter;
 1706     hDrive = CreateFileA(logical_drive, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
 1707         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 1708     if (hDrive == INVALID_HANDLE_VALUE) {
 1709         uprintf("Failed to open %c: for flushing: %s", drive_letter, WindowsErrorString());
 1710         goto out;
 1711     }
 1712     r = FlushFileBuffers(hDrive);
 1713     if (r == FALSE)
 1714         uprintf("Failed to flush %c: %s", drive_letter, WindowsErrorString());
 1715 
 1716 out:
 1717     safe_closehandle(hDrive);
 1718     return r;
 1719 }
 1720 
 1721 /*
 1722  * Unmount of volume using the DISMOUNT_VOLUME ioctl
 1723  */
 1724 BOOL UnmountVolume(HANDLE hDrive)
 1725 {
 1726     DWORD size;
 1727 
 1728     if (!DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &size, NULL)) {
 1729         uprintf("Could not unmount drive: %s", WindowsErrorString());
 1730         return FALSE;
 1731     }
 1732     return TRUE;
 1733 }
 1734 
 1735 /*
 1736  * Mount the volume identified by drive_guid to mountpoint drive_name.
 1737  * If volume_name is already mounted, but with a different letter than the
 1738  * one requested then drive_name is updated to use that letter.
 1739  */
 1740 BOOL MountVolume(char* drive_name, char *volume_name)
 1741 {
 1742     char mounted_guid[52], dos_name[] = "?:";
 1743 #if defined(WINDOWS_IS_NOT_BUGGY)
 1744     char mounted_letter[27] = { 0 };
 1745     DWORD size;
 1746 #endif
 1747 
 1748     if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '?')) {
 1749         SetLastError(ERROR_INVALID_PARAMETER);
 1750         return FALSE;
 1751     }
 1752 
 1753     // If we are working with a "\\?\GLOBALROOT" device, SetVolumeMountPoint()
 1754     // is useless, so try with DefineDosDevice() instead.
 1755     if (_strnicmp(volume_name, groot_name, groot_len) == 0) {
 1756         dos_name[0] = drive_name[0];
 1757         // Microsoft will also have to explain why "In no case is a trailing backslash allowed" [1] in
 1758         // DefineDosDevice(), instead of just checking if the driver parameter is "X:\" and remove the
 1759         // backslash from a copy of the parameter in the bloody API call. *THIS* really tells a lot
 1760         // about the level of thought and foresight that actually goes into the Windows APIs...
 1761         // [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-definedosdevicew
 1762         if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, dos_name, &volume_name[14])) {
 1763             uprintf("Could not mount %s as %C:", volume_name, drive_name[0]);
 1764             return FALSE;
 1765         }
 1766         uprintf("%s was successfully mounted as %C:", volume_name, drive_name[0]);
 1767         return TRUE;
 1768     }
 1769 
 1770     // Great: Windows has a *MAJOR BUG* whereas, in some circumstances, GetVolumePathNamesForVolumeName()
 1771     // can return the *WRONG* drive letter. And yes, we validated that this is *NOT* an issue like stack
 1772     // or buffer corruption and whatnot. It *IS* a Windows bug. So just drop the idea of updating the
 1773     // drive letter if already mounted and use the passed target always.
 1774 #if defined(WINDOWS_IS_NOT_BUGGY)
 1775     // Windows may already have the volume mounted but under a different letter.
 1776     // If that is the case, update drive_name to that letter.
 1777     if ( (GetVolumePathNamesForVolumeNameA(volume_name, mounted_letter, sizeof(mounted_letter), &size))
 1778       && (size > 1) && (mounted_letter[0] != drive_name[0]) ) {
 1779         uprintf("%s is already mounted as %C: instead of %C: - Will now use this target instead...",
 1780             volume_name, mounted_letter[0], drive_name[0]);
 1781         drive_name[0] = mounted_letter[0];
 1782         return TRUE;
 1783     }
 1784 #endif
 1785 
 1786     if (!SetVolumeMountPointA(drive_name, volume_name)) {
 1787         if (GetLastError() == ERROR_DIR_NOT_EMPTY) {
 1788             if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) {
 1789                 uprintf("%s is already mounted, but volume GUID could not be checked: %s",
 1790                     drive_name, WindowsErrorString());
 1791             } else if (safe_strcmp(volume_name, mounted_guid) != 0) {
 1792                 uprintf("%s is mounted, but volume GUID doesn't match:\r\n  expected %s, got %s",
 1793                     drive_name, volume_name, mounted_guid);
 1794             } else {
 1795                 duprintf("%s is already mounted as %C:", volume_name, drive_name[0]);
 1796                 return TRUE;
 1797             }
 1798             uprintf("Retrying after dismount...");
 1799             if (!DeleteVolumeMountPointA(drive_name))
 1800                 uprintf("Warning: Could not delete volume mountpoint '%s': %s", drive_name, WindowsErrorString());
 1801             if (SetVolumeMountPointA(drive_name, volume_name))
 1802                 return TRUE;
 1803             if ((GetLastError() == ERROR_DIR_NOT_EMPTY) &&
 1804                 GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid)) &&
 1805                 (safe_strcmp(volume_name, mounted_guid) == 0)) {
 1806                 uprintf("%s was remounted as %C: (second time lucky!)", volume_name, drive_name[0]);
 1807                 return TRUE;
 1808             }
 1809         }
 1810         return FALSE;
 1811     }
 1812     return TRUE;
 1813 }
 1814 
 1815 /*
 1816  * Alternate version of MountVolume required for ESP's, since Windows (including VDS) does
 1817  * *NOT* provide any means of mounting these volume but through DefineDosDevice(). Also
 1818  * note that bcdboot is very finicky about what it may or may not handle, even if the
 1819  * mount was successful (e.g. '\Device\HarddiskVolume###' vs 'Device\HarddiskVolume###').
 1820  * Returned string is static (no concurrency) and must not be freed.
 1821  */
 1822 char* AltMountVolume(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bSilent)
 1823 {
 1824     char* ret = NULL, *volume_name = NULL;
 1825     static char mounted_drive[] = "?:";
 1826 
 1827     mounted_drive[0] = GetUnusedDriveLetter();
 1828     if (mounted_drive[0] == 0) {
 1829         suprintf("Could not find an unused drive letter");
 1830         goto out;
 1831     }
 1832     // Can't use a regular volume GUID for ESPs...
 1833     volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, FALSE);
 1834     if ((volume_name == NULL) || (strncmp(volume_name, groot_name, groot_len) != 0)) {
 1835         suprintf("Unexpected volume name: '%s'", volume_name);
 1836         goto out;
 1837     }
 1838 
 1839     suprintf("Mounting '%s' as '%s'", &volume_name[14], mounted_drive);
 1840     // bcdboot doesn't like it if you forget the starting '\'
 1841     if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, mounted_drive, &volume_name[14])) {
 1842         suprintf("Mount operation failed: %s", WindowsErrorString());
 1843         goto out;
 1844     }
 1845     ret = mounted_drive;
 1846 
 1847 out:
 1848     free(volume_name);
 1849     return ret;
 1850 }
 1851 
 1852 /*
 1853  * Unmount a volume that was mounted by AltmountVolume()
 1854  */
 1855 BOOL AltUnmountVolume(const char* drive_name, BOOL bSilent)
 1856 {
 1857     if (drive_name == NULL)
 1858         return FALSE;
 1859     if (!DefineDosDeviceA(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, drive_name, NULL)) {
 1860         suprintf("Could not unmount '%s': %s", drive_name, WindowsErrorString());
 1861         return FALSE;
 1862     }
 1863     suprintf("Successfully unmounted '%s'", drive_name);
 1864     return TRUE;
 1865 }
 1866 
 1867 /*
 1868  * Issue a complete remount of the volume.
 1869  * Note that drive_name *may* be altered when the volume gets remounted.
 1870  */
 1871 BOOL RemountVolume(char* drive_name, BOOL bSilent)
 1872 {
 1873     char volume_name[51];
 1874 
 1875     // UDF requires a sync/flush, and it's also a good idea for other FS's
 1876     FlushDrive(drive_name[0]);
 1877     if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(volume_name))) {
 1878         if (MountVolume(drive_name, volume_name)) {
 1879             suprintf("Successfully remounted %s as %C:", volume_name, drive_name[0]);
 1880         } else {
 1881             suprintf("Could not remount %s as %C: %s", volume_name, drive_name[0], WindowsErrorString());
 1882             // This will leave the drive inaccessible and must be flagged as an error
 1883             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME);
 1884             return FALSE;
 1885         }
 1886     }
 1887     return TRUE;
 1888 }
 1889 
 1890 /*
 1891  * Zero the first 'size' bytes of a partition. This is needed because we haven't found a way to
 1892  * properly reset Windows's cached view of a drive partitioning short of cycling the USB port
 1893  * (especially IOCTL_DISK_UPDATE_PROPERTIES is *USELESS*), and therefore the OS will try to
 1894  * read the file system data at an old location, even if the partition has just been deleted.
 1895  */
 1896 static BOOL ClearPartition(HANDLE hDrive, LARGE_INTEGER offset, DWORD size)
 1897 {
 1898     BOOL r = FALSE;
 1899     uint8_t* buffer = calloc(size, 1);
 1900 
 1901     if (buffer == NULL)
 1902         return FALSE;
 1903 
 1904     if (!SetFilePointerEx(hDrive, offset, NULL, FILE_BEGIN)) {
 1905         free(buffer);
 1906         return FALSE;
 1907     }
 1908 
 1909     r = WriteFileWithRetry(hDrive, buffer, size, &size, WRITE_RETRIES);
 1910     free(buffer);
 1911     return r;
 1912 }
 1913 
 1914 /*
 1915  * Create a partition table
 1916  * See http://technet.microsoft.com/en-us/library/cc739412.aspx for some background info
 1917  * NB: if you modify the MBR outside of using the Windows API, Windows still uses the cached
 1918  * copy it got from the last IOCTL, and ignores your changes until you replug the drive
 1919  * or issue an IOCTL_DISK_UPDATE_PROPERTIES.
 1920  */
 1921 BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL mbr_uefi_marker, uint8_t extra_partitions)
 1922 {
 1923     const char* PartitionTypeName[] = { "MBR", "GPT", "SFD" };
 1924     const wchar_t *extra_part_name = L"", *main_part_name = write_as_esp ? L"EFI System Partition" : L"Main Data Partition";
 1925     const LONGLONG main_part_size = write_as_esp ? MAX_ISO_TO_ESP_SIZE * MB : SelectedDrive.DiskSize;
 1926     const LONGLONG bytes_per_track = ((LONGLONG)SelectedDrive.SectorsPerTrack) * SelectedDrive.SectorSize;
 1927     const DWORD size_to_clear = MAX_SECTORS_TO_CLEAR * SelectedDrive.SectorSize;
 1928     uint8_t* buffer;
 1929     size_t uefi_ntfs_size = 0;
 1930     CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}};
 1931     DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0};
 1932     BOOL r;
 1933     DWORD i, size, bufsize, pn = 0;
 1934     LONGLONG main_part_size_in_sectors, extra_part_size_in_tracks = 0;
 1935     // Go for a 260 MB sized ESP by default to keep everyone happy, including 4K sector users:
 1936     // https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions
 1937     // and folks using MacOS: https://github.com/pbatard/rufus/issues/979
 1938     LONGLONG esp_size = 260 * MB;
 1939 
 1940     PrintInfoDebug(0, MSG_238, PartitionTypeName[partition_style]);
 1941 
 1942     if (partition_style == PARTITION_STYLE_SFD)
 1943         // Nothing to do
 1944         return TRUE;
 1945 
 1946     if (extra_partitions & XP_UEFI_NTFS) {
 1947         uefi_ntfs_size = GetResourceSize(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img");
 1948         if (uefi_ntfs_size == 0)
 1949             return FALSE;
 1950     }
 1951     memset(partition_offset, 0, sizeof(partition_offset));
 1952     memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
 1953     memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));
 1954 
 1955     // Compute the start offset of our first partition
 1956     if ((partition_style == PARTITION_STYLE_GPT) || (!IsChecked(IDC_OLD_BIOS_FIXES))) {
 1957         // Go with the MS 1 MB wastage at the beginning...
 1958         DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = MB;
 1959     } else {
 1960         // Some folks appear to think that 'Fixes for old BIOSes' is some kind of magic
 1961         // wand and are adamant to try to apply them when creating *MODERN* VHD drives.
 1962         // This, however, wrecks havok on MS' internal format calls because, as opposed
 1963         // to what is the case for regular drives, VHDs require each cluster block to
 1964         // be aligned to the cluster size, and that may not be the case with the stupid
 1965         // CHS sizes that IBM imparted upon us. Long story short, we now align to a
 1966         // cylinder size that is itself aligned to the cluster size.
 1967         // If this actually breaks old systems, please send your complaints to IBM.
 1968         LONGLONG ClusterSize = (LONGLONG)ComboBox_GetCurItemData(hClusterSize);
 1969         if (ClusterSize == 0)
 1970             ClusterSize = 0x200;
 1971         DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart =
 1972             ((bytes_per_track + (ClusterSize - 1)) / ClusterSize) * ClusterSize;
 1973     }
 1974 
 1975     // Having the ESP up front may help (and is the Microsoft recommended way) but this
 1976     // is only achievable if we can mount more than one partition at once, which means
 1977     // either fixed drive or Windows 10 1703 or later.
 1978     if (((SelectedDrive.MediaType == FixedMedia) || (nWindowsBuildNumber > 15000)) &&
 1979         (extra_partitions & XP_ESP)) {
 1980         assert(partition_style == PARTITION_STYLE_GPT);
 1981         extra_part_name = L"EFI System Partition";
 1982         DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = esp_size;
 1983         DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_GENERIC_ESP;
 1984         uprintf("● Creating %S (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
 1985             SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
 1986         IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
 1987         wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, extra_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
 1988         // Zero the first sectors from this partition to avoid file system caching issues
 1989         if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
 1990             uprintf("Could not zero %S: %s", extra_part_name, WindowsErrorString());
 1991         SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
 1992         SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
 1993         partition_offset[PI_ESP] = SelectedDrive.PartitionOffset[pn];
 1994         pn++;
 1995         DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn - 1].StartingOffset.QuadPart +
 1996             DriveLayoutEx.PartitionEntry[pn - 1].PartitionLength.QuadPart;
 1997         // Clear the extra partition we processed
 1998         extra_partitions &= ~(XP_ESP);
 1999     }
 2000 
 2001     // If required, set the MSR partition (GPT only - must be created before the data part)
 2002     if (extra_partitions & XP_MSR) {
 2003         assert(partition_style == PARTITION_STYLE_GPT);
 2004         extra_part_name = L"Microsoft Reserved Partition";
 2005         DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = 128*MB;
 2006         DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_MICROSOFT_RESERVED;
 2007         uprintf("● Creating %S (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
 2008             SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
 2009         IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
 2010         wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, extra_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
 2011         // Zero the first sectors from this partition to avoid file system caching issues
 2012         if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
 2013             uprintf("Could not zero %S: %s", extra_part_name, WindowsErrorString());
 2014         SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
 2015         SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
 2016         pn++;
 2017         DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart +
 2018                 DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart;
 2019         // Clear the extra partition we processed
 2020         extra_partitions &= ~(XP_MSR);
 2021     }
 2022 
 2023     // Set our main data partition
 2024     if (write_as_esp) {
 2025         // Align ESP to 64 MB while leaving at least 32 MB free space
 2026         esp_size = max(esp_size, ((((LONGLONG)img_report.projected_size / MB) + 96) / 64) * 64 * MB);
 2027         main_part_size_in_sectors = (esp_size - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) /
 2028             SelectedDrive.SectorSize;
 2029     } else {
 2030         main_part_size_in_sectors = (main_part_size - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) /
 2031             // Need 33 sectors at the end for secondary GPT
 2032             SelectedDrive.SectorSize - ((partition_style == PARTITION_STYLE_GPT) ? 33 : 0);
 2033     }
 2034     if (extra_partitions) {
 2035         // Adjust the size according to extra partitions (which we always align to a track)
 2036         if (extra_partitions & XP_ESP) {
 2037             extra_part_name = L"EFI System";
 2038             extra_part_size_in_tracks = (esp_size + bytes_per_track - 1) / bytes_per_track;
 2039         } else if (extra_partitions & XP_UEFI_NTFS) {
 2040             extra_part_name = L"UEFI:NTFS";
 2041             extra_part_size_in_tracks = (max(MIN_EXTRA_PART_SIZE, uefi_ntfs_size) + bytes_per_track - 1) / bytes_per_track;
 2042         } else if ((extra_partitions & XP_CASPER)) {
 2043             assert(persistence_size != 0);
 2044             extra_part_name = L"Linux Persistence";
 2045             extra_part_size_in_tracks = persistence_size / bytes_per_track;
 2046         } else if (extra_partitions & XP_COMPAT) {
 2047             extra_part_name = L"BIOS Compatibility";
 2048             extra_part_size_in_tracks = 1;  // One track for the extra partition
 2049         } else {
 2050             assert(FALSE);
 2051         }
 2052         // NB: Because we already subtracted the backup GPT size from the main partition size and
 2053         // this extra partition is indexed on main size, it does not overflow into the backup GPT.
 2054         main_part_size_in_sectors = ((main_part_size_in_sectors / SelectedDrive.SectorsPerTrack) -
 2055             extra_part_size_in_tracks) * SelectedDrive.SectorsPerTrack;
 2056     }
 2057     if (main_part_size_in_sectors <= 0) {
 2058         uprintf("Error: Invalid %S size", main_part_name);
 2059         return FALSE;
 2060     }
 2061     uprintf("● Creating %S (offset: %lld, size: %s)", main_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
 2062         SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.SectorSize, TRUE, FALSE));
 2063     // Zero the beginning of this partition to avoid conflicting leftovers
 2064     if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
 2065         uprintf("Could not zero %S: %s", main_part_name, WindowsErrorString());
 2066 
 2067     DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = main_part_size_in_sectors * SelectedDrive.SectorSize;
 2068     if (partition_style == PARTITION_STYLE_MBR) {
 2069         DriveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = (boot_type != BT_NON_BOOTABLE);
 2070         switch (file_system) {
 2071         case FS_FAT16:
 2072             DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0e;  // FAT16 LBA
 2073             break;
 2074         case FS_NTFS:
 2075         case FS_EXFAT:
 2076         case FS_UDF:
 2077         case FS_REFS:
 2078             DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
 2079             break;
 2080         case FS_EXT2:
 2081         case FS_EXT3:
 2082         case FS_EXT4:
 2083             DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83;
 2084             break;
 2085         case FS_FAT32:
 2086             DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0c;  // FAT32 LBA
 2087             break;
 2088         default:
 2089             uprintf("Unsupported file system");
 2090             return FALSE;
 2091         }
 2092     } else {
 2093         DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = write_as_esp ? PARTITION_GENERIC_ESP : PARTITION_MICROSOFT_DATA;
 2094         IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
 2095         wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, main_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
 2096     }
 2097     SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
 2098     SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
 2099     partition_offset[PI_MAIN] = SelectedDrive.PartitionOffset[pn];
 2100     pn++;
 2101 
 2102     // Set the optional extra partition
 2103     if (extra_partitions) {
 2104         // Should end on a track boundary
 2105         DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart +
 2106             DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart;
 2107         DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = (extra_partitions & XP_UEFI_NTFS)?uefi_ntfs_size:
 2108             extra_part_size_in_tracks * bytes_per_track;
 2109         uprintf("● Creating %S Partition (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
 2110             SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
 2111         SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
 2112         SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
 2113         if (extra_partitions & XP_CASPER)
 2114             partition_offset[PI_CASPER] = SelectedDrive.PartitionOffset[pn];
 2115         else if (extra_partitions & XP_ESP)
 2116             partition_offset[PI_ESP] = SelectedDrive.PartitionOffset[pn];
 2117 
 2118         if (partition_style == PARTITION_STYLE_GPT) {
 2119             DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = (extra_partitions & XP_ESP) ? PARTITION_GENERIC_ESP : PARTITION_MICROSOFT_DATA;
 2120             IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
 2121             wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, (extra_partitions & XP_ESP) ? L"EFI System Partition" : extra_part_name,
 2122                 ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
 2123         } else {
 2124             if (extra_partitions & (XP_UEFI_NTFS | XP_ESP)) {
 2125                 DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;
 2126             } else if (extra_partitions & XP_CASPER) {
 2127                 DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83;
 2128             } else if (extra_partitions & XP_COMPAT) {
 2129                 DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = RUFUS_EXTRA_PARTITION_TYPE;
 2130                 // Set the one track compatibility partition to be all hidden sectors
 2131                 DriveLayoutEx.PartitionEntry[pn].Mbr.HiddenSectors = SelectedDrive.SectorsPerTrack;
 2132             } else {
 2133                 assert(FALSE);
 2134             }
 2135         }
 2136 
 2137         // We need to write the UEFI:NTFS partition before we refresh the disk
 2138         if (extra_partitions & XP_UEFI_NTFS) {
 2139             uprintf("Writing %S data...", extra_part_name);
 2140             if (!SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) {
 2141                 uprintf("Could not set position");
 2142                 return FALSE;
 2143             }
 2144             buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img", &bufsize, FALSE);
 2145             if (buffer == NULL) {
 2146                 uprintf("Could not access source image");
 2147                 return FALSE;
 2148             }
 2149             if(!WriteFileWithRetry(hDrive, buffer, bufsize, &size, WRITE_RETRIES)) {
 2150                 uprintf("Write error: %s", WindowsErrorString());
 2151                 return FALSE;
 2152             }
 2153             installed_uefi_ntfs = TRUE;
 2154         }
 2155         pn++;
 2156     }
 2157 
 2158     // Initialize the remaining partition data
 2159     for (i = 0; i < pn; i++) {
 2160         DriveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
 2161         DriveLayoutEx.PartitionEntry[i].PartitionStyle = partition_style;
 2162         DriveLayoutEx.PartitionEntry[i].RewritePartition = TRUE;
 2163     }
 2164 
 2165     switch (partition_style) {
 2166     case PARTITION_STYLE_MBR:
 2167         CreateDisk.PartitionStyle = PARTITION_STYLE_MBR;
 2168         // If MBR+UEFI is selected, write an UEFI marker in lieu of the regular MBR signature.
 2169         // This helps us reselect the partition scheme option that was used when creating the
 2170         // drive in Rufus. As far as I can tell, Windows doesn't care much if this signature
 2171         // isn't unique for USB drives.
 2172         CreateDisk.Mbr.Signature = mbr_uefi_marker?MBR_UEFI_MARKER:(DWORD)GetTickCount64();
 2173 
 2174         DriveLayoutEx.PartitionStyle = PARTITION_STYLE_MBR;
 2175         DriveLayoutEx.PartitionCount = 4;   // Must be multiple of 4 for MBR
 2176         DriveLayoutEx.Type.Mbr.Signature = CreateDisk.Mbr.Signature;
 2177         // TODO: CHS fixup (32 sectors/track) through a cheat mode, if requested
 2178         // NB: disk geometry is computed by BIOS & co. by finding a match between LBA and CHS value of first partition
 2179         //     ms-sys's write_partition_number_of_heads() and write_partition_start_sector_number() can be used if needed
 2180         break;
 2181     case PARTITION_STYLE_GPT:
 2182         // TODO: (HOW?!?!?) As per MSDN: "When specifying a GUID partition table (GPT) as the PARTITION_STYLE of the CREATE_DISK
 2183         // structure, an application should wait for the MSR partition arrival before sending the IOCTL_DISK_SET_DRIVE_LAYOUT_EX
 2184         // control code. For more information about device notification, see RegisterDeviceNotification."
 2185 
 2186         CreateDisk.PartitionStyle = PARTITION_STYLE_GPT;
 2187         IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId));
 2188         CreateDisk.Gpt.MaxPartitionCount = MAX_PARTITIONS;
 2189 
 2190         DriveLayoutEx.PartitionStyle = PARTITION_STYLE_GPT;
 2191         DriveLayoutEx.PartitionCount = pn;
 2192         // At the very least, a GPT disk has 34 reserved sectors at the beginning and 33 at the end.
 2193         DriveLayoutEx.Type.Gpt.StartingUsableOffset.QuadPart = 34 * SelectedDrive.SectorSize;
 2194         DriveLayoutEx.Type.Gpt.UsableLength.QuadPart = SelectedDrive.DiskSize - (34+33) * SelectedDrive.SectorSize;
 2195         DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_PARTITIONS;
 2196         DriveLayoutEx.Type.Gpt.DiskId = CreateDisk.Gpt.DiskId;
 2197         break;
 2198     }
 2199 
 2200     // If you don't call IOCTL_DISK_CREATE_DISK, the IOCTL_DISK_SET_DRIVE_LAYOUT_EX call will fail
 2201     size = sizeof(CreateDisk);
 2202     r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL);
 2203     if (!r) {
 2204         uprintf("Could not reset disk: %s", WindowsErrorString());
 2205         return FALSE;
 2206     }
 2207 
 2208     // "The goggles, they do nothing!"
 2209     RefreshDriveLayout(hDrive);
 2210 
 2211     size = sizeof(DriveLayoutEx) - ((partition_style == PARTITION_STYLE_GPT)?((4-pn)*sizeof(PARTITION_INFORMATION_EX)):0);
 2212     r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL);
 2213     if (!r) {
 2214         uprintf("Could not set drive layout: %s", WindowsErrorString());
 2215         return FALSE;
 2216     }
 2217 
 2218     if (!RefreshDriveLayout(hDrive))
 2219         return FALSE;
 2220 
 2221     return TRUE;
 2222 }
 2223 
 2224 BOOL RefreshDriveLayout(HANDLE hDrive)
 2225 {
 2226     BOOL r;
 2227     DWORD size;
 2228 
 2229     // Diskpart does call the following IOCTL this after updating the partition table, so we do too
 2230     r = DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL );
 2231     if (!r)
 2232         uprintf("Could not refresh drive layout: %s", WindowsErrorString());
 2233     return r;
 2234 }
 2235 
 2236 /* Initialize disk for partitioning */
 2237 BOOL InitializeDisk(HANDLE hDrive)
 2238 {
 2239     BOOL r;
 2240     DWORD size;
 2241     CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}};
 2242 
 2243     uprintf("Initializing disk...");
 2244 
 2245     size = sizeof(CreateDisk);
 2246     r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK,
 2247             (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL );
 2248     if (!r) {
 2249         uprintf("Could not delete drive layout: %s", WindowsErrorString());
 2250         safe_closehandle(hDrive);
 2251         return FALSE;
 2252     }
 2253 
 2254     r = DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL );
 2255     if (!r) {
 2256         uprintf("Could not refresh drive layout: %s", WindowsErrorString());
 2257         safe_closehandle(hDrive);
 2258         return FALSE;
 2259     }
 2260 
 2261     return TRUE;
 2262 }
 2263 
 2264 /*
 2265  * Convert MBR or GPT partition types to their human readable forms
 2266  */
 2267 const char* GetMBRPartitionType(const uint8_t type)
 2268 {
 2269     int i;
 2270     for (i = 0; (i < ARRAYSIZE(mbr_type)) && (mbr_type[i].type != type); i++);
 2271     return (i < ARRAYSIZE(mbr_type)) ? mbr_type[i].name : "Unknown";
 2272 }
 2273 
 2274 const char* GetGPTPartitionType(const GUID* guid)
 2275 {
 2276     int i;
 2277     for (i = 0; (i < ARRAYSIZE(gpt_type)) && !CompareGUID(guid, gpt_type[i].guid); i++);
 2278     return (i < ARRAYSIZE(gpt_type)) ? gpt_type[i].name : GuidToString(guid);
 2279 }