"Fossies" - the Fresh Open Source Software Archive

Member "rufus-3.13/src/format.c" (20 Nov 2020, 85463 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 "format.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  * Formatting 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 <stdlib.h>
   29 #include <process.h>
   30 #include <stddef.h>
   31 #include <ctype.h>
   32 #include <locale.h>
   33 #include <assert.h>
   34 #include <vds.h>
   35 
   36 #include "rufus.h"
   37 #include "missing.h"
   38 #include "resource.h"
   39 #include "settings.h"
   40 #include "msapi_utf8.h"
   41 #include "localization.h"
   42 
   43 #include "br.h"
   44 #include "fat16.h"
   45 #include "fat32.h"
   46 #include "ntfs.h"
   47 #include "partition_info.h"
   48 #include "file.h"
   49 #include "drive.h"
   50 #include "format.h"
   51 #include "badblocks.h"
   52 #include "bled/bled.h"
   53 #include "../res/grub/grub_version.h"
   54 
   55 /*
   56  * Globals
   57  */
   58 const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", "ReFS", "ext2", "ext3", "ext4" };
   59 DWORD FormatStatus = 0, LastWriteError = 0;
   60 badblocks_report report = { 0 };
   61 static float format_percent = 0.0f;
   62 static int task_number = 0;
   63 static unsigned int sec_buf_pos = 0;
   64 extern const int nb_steps[FS_MAX];
   65 extern uint32_t dur_mins, dur_secs;
   66 extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
   67 static int actual_fs_type, wintogo_index = -1, wininst_index = 0;
   68 extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing, write_as_image;
   69 extern BOOL use_vds, write_as_esp;
   70 uint8_t *grub2_buf = NULL, *sec_buf = NULL;
   71 long grub2_len;
   72 
   73 /*
   74  * Convert the fmifs outputs messages (that use an OEM code page) to UTF-8
   75  */
   76 static void OutputUTF8Message(const char* src)
   77 {
   78     int len;
   79     wchar_t* wdst = NULL;
   80 
   81     if (src == NULL)
   82         goto out;
   83     len = (int)safe_strlen(src);
   84     while ((len > 0) && ((src[len-1] == 0x0A) || (src[len-1] == 0x0D) || (src[len-1] == ' ')))
   85         len--;
   86     if (len == 0)
   87         goto out;
   88 
   89     len = MultiByteToWideChar(CP_OEMCP, 0, src, len, NULL, 0);
   90     if (len == 0)
   91         goto out;
   92     wdst = (wchar_t*)calloc(len+1, sizeof(wchar_t));
   93     if ((wdst == NULL) || (MultiByteToWideChar(CP_OEMCP, 0, src, len, wdst, len+1) == 0))
   94         goto out;
   95     uprintf("%S", wdst);
   96 
   97 out:
   98     safe_free(wdst);
   99 }
  100 
  101 /*
  102  * FormatEx callback. Return FALSE to halt operations
  103  */
  104 static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
  105 {
  106     char percent_str[8];
  107     if (IS_ERROR(FormatStatus))
  108         return FALSE;
  109 
  110     assert((actual_fs_type >= 0) && (actual_fs_type < FS_MAX));
  111     if ((actual_fs_type < 0) || (actual_fs_type >= FS_MAX))
  112         return FALSE;
  113 
  114     switch(Command) {
  115     case FCC_PROGRESS:
  116         static_sprintf(percent_str, "%lu%%", *((DWORD*)pData));
  117         PrintInfo(0, MSG_217, percent_str);
  118         UpdateProgress(OP_FORMAT, 1.0f * (*((DWORD*)pData)));
  119         break;
  120     case FCC_STRUCTURE_PROGRESS:    // No progress on quick format
  121         if (task_number < nb_steps[actual_fs_type] - 1) {
  122             if (task_number == 0)
  123                 uprintf("Creating file system...");
  124             PrintInfo(0, MSG_218, ++task_number, nb_steps[actual_fs_type]);
  125             format_percent += 100.0f / (1.0f * nb_steps[actual_fs_type]);
  126             UpdateProgress(OP_CREATE_FS, format_percent);
  127         }
  128         break;
  129     case FCC_DONE:
  130         PrintInfo(0, MSG_218, nb_steps[actual_fs_type], nb_steps[actual_fs_type]);
  131         UpdateProgress(OP_CREATE_FS, 100.0f);
  132         if(*(BOOLEAN*)pData == FALSE) {
  133             uprintf("Error while formatting");
  134             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE;
  135         }
  136         break;
  137     case FCC_DONE_WITH_STRUCTURE:
  138         break;
  139     case FCC_INCOMPATIBLE_FILE_SYSTEM:
  140         uprintf("Incompatible File System");
  141         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS);
  142         break;
  143     case FCC_ACCESS_DENIED:
  144         uprintf("Access denied");
  145         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
  146         break;
  147     case FCC_MEDIA_WRITE_PROTECTED:
  148         uprintf("Media is write protected");
  149         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT;
  150         break;
  151     case FCC_VOLUME_IN_USE:
  152         uprintf("Volume is in use");
  153         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE;
  154         break;
  155     case FCC_DEVICE_NOT_READY:
  156         uprintf("The device is not ready");
  157         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_READY;
  158         break;
  159     case FCC_CANT_QUICK_FORMAT:
  160         uprintf("Cannot quick format this volume");
  161         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_QUICK_FORMAT);
  162         break;
  163     case FCC_BAD_LABEL:
  164         uprintf("Bad label");
  165         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_LABEL_TOO_LONG;
  166         break;
  167     case FCC_OUTPUT:
  168         OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
  169         break;
  170     case FCC_CLUSTER_SIZE_TOO_BIG:
  171     case FCC_CLUSTER_SIZE_TOO_SMALL:
  172         uprintf("Unsupported cluster size");
  173         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_CLUSTER_SIZE);
  174         break;
  175     case FCC_VOLUME_TOO_BIG:
  176     case FCC_VOLUME_TOO_SMALL:
  177         uprintf("Volume is too %s", (Command == FCC_VOLUME_TOO_BIG)?"big":"small");
  178         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_VOLUME_SIZE);
  179         break;
  180     case FCC_NO_MEDIA_IN_DRIVE:
  181         uprintf("No media in drive");
  182         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE;
  183         break;
  184     case FCC_ALIGNMENT_VIOLATION:
  185         uprintf("Partition start offset is not aligned to the cluster size");
  186         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OFFSET_ALIGNMENT_VIOLATION;
  187         break;
  188     default:
  189         uprintf("FormatExCallback: Received unhandled command 0x%02X - aborting", Command);
  190         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
  191         break;
  192     }
  193     return (!IS_ERROR(FormatStatus));
  194 }
  195 
  196 /*
  197  * Chkdsk callback. Return FALSE to halt operations
  198  */
  199 static BOOLEAN __stdcall ChkdskCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
  200 {
  201     DWORD* percent;
  202     if (IS_ERROR(FormatStatus))
  203         return FALSE;
  204 
  205     switch (Command) {
  206     case FCC_PROGRESS:
  207     case FCC_CHECKDISK_PROGRESS:
  208         percent = (DWORD*)pData;
  209         PrintInfo(0, MSG_219, *percent);
  210         break;
  211     case FCC_DONE:
  212         if (*(BOOLEAN*)pData == FALSE) {
  213             uprintf("Error while checking disk");
  214             return FALSE;
  215         }
  216         break;
  217     case FCC_UNKNOWN1A:
  218     case FCC_DONE_WITH_STRUCTURE:
  219         // Silence these specific calls
  220         break;
  221     case FCC_INCOMPATIBLE_FILE_SYSTEM:
  222         uprintf("Incompatible File System");
  223         return FALSE;
  224     case FCC_ACCESS_DENIED:
  225         uprintf("Access denied");
  226         return FALSE;
  227     case FCC_MEDIA_WRITE_PROTECTED:
  228         uprintf("Media is write protected");
  229         return FALSE;
  230     case FCC_VOLUME_IN_USE:
  231         uprintf("Volume is in use");
  232         return FALSE;
  233     case FCC_OUTPUT:
  234         OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
  235         break;
  236     case FCC_NO_MEDIA_IN_DRIVE:
  237         uprintf("No media in drive");
  238         return FALSE;
  239     case FCC_READ_ONLY_MODE:
  240         uprintf("Media has been switched to read-only - Leaving checkdisk");
  241         break;
  242     default:
  243         uprintf("ChkdskExCallback: received unhandled command %X", Command);
  244         // Assume the command isn't an error
  245         break;
  246     }
  247     return TRUE;
  248 }
  249 
  250 /*
  251  * Converts an UTF-8 label to a valid FAT/NTFS one
  252  * TODO: Use IVdsService::QueryFileSystemTypes -> VDS_FILE_SYSTEM_TYPE_PROP
  253  * to get the list of unauthorised and max length for each FS.
  254  */
  255 static void ToValidLabel(char* Label, BOOL bFAT)
  256 {
  257     size_t i, j, k;
  258     BOOL found;
  259     const WCHAR unauthorized[] = L"*?,;:/\\|+=<>[]\"";
  260     const WCHAR to_underscore[] = L"\t.";
  261     char label[16] = { 0 };
  262     WCHAR *wLabel = utf8_to_wchar(Label);
  263 
  264     if (wLabel == NULL)
  265         return;
  266 
  267     for (i = 0, k = 0; i < wcslen(wLabel); i++) {
  268         if (bFAT) { // NTFS does allows all the FAT unauthorized above
  269             found = FALSE;
  270             for (j = 0; j < wcslen(unauthorized); j++) {
  271                 if (wLabel[i] == unauthorized[j]) {
  272                     found = TRUE;
  273                     break;
  274                 }
  275             }
  276             // A FAT label that contains extended chars will be rejected
  277             if (wLabel[i] >= 0x80) {
  278                 wLabel[k++] = L'_';
  279                 found = TRUE;
  280             }
  281             if (found)
  282                 continue;
  283         }
  284         found = FALSE;
  285         for (j = 0; j < wcslen(to_underscore); j++) {
  286             if (wLabel[i] == to_underscore[j]) {
  287                 wLabel[k++] = '_';
  288                 found = TRUE;
  289                 break;
  290             }
  291         }
  292         if (found)
  293             continue;
  294         wLabel[k++] = bFAT ? toupper(wLabel[i]) : wLabel[i];
  295     }
  296     wLabel[k] = 0;
  297 
  298     if (bFAT) {
  299         if (wcslen(wLabel) > 11)
  300             wLabel[11] = 0;
  301         for (i = 0, j = 0; wLabel[i] != 0 ; i++)
  302             if (wLabel[i] == '_')
  303                 j++;
  304         if (i < 2*j) {
  305             // If the final label is mostly underscore, use an alternate label according to the
  306             // size (eg: "256 MB", "7.9 GB"). Note that we can't use SelectedDrive.proposed_label
  307             // here as it may contain localized character for GB or MB, so make sure that the
  308             // effective label we use is an English one, and also make sure we convert the dot.
  309             static_sprintf(label, "%s", SizeToHumanReadable(SelectedDrive.DiskSize, TRUE, FALSE));
  310             for (i = 0; label[i] != 0; i++)
  311                 wLabel[i] = (label[i] == '.') ? '_' : label[i];
  312             wLabel[i] = 0;
  313             uprintf("FAT label is mostly underscores. Using '%S' label instead.", wLabel);
  314         }
  315     } else if (wcslen(wLabel) > 32) {
  316         wLabel[32] = 0;
  317     }
  318 
  319     // Needed for disk by label isolinux.cfg workaround
  320     wchar_to_utf8_no_alloc(wLabel, img_report.usb_label, sizeof(img_report.usb_label));
  321     safe_strcpy(Label, strlen(Label) + 1, img_report.usb_label);
  322     free(wLabel);
  323 }
  324 
  325 /*
  326  * Call on VDS to format a partition
  327  */
  328 static BOOL FormatNativeVds(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags)
  329 {
  330     BOOL r = FALSE, bFoundVolume = FALSE;
  331     HRESULT hr;
  332     ULONG ulFetched;
  333     IVdsServiceLoader *pLoader;
  334     IVdsService *pService;
  335     IEnumVdsObject *pEnum;
  336     IUnknown *pUnk;
  337     char* VolumeName = NULL;
  338     WCHAR *wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName);
  339 
  340     if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) {
  341         PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs);
  342     } else {
  343         PrintInfoDebug(0, MSG_222, FSName);
  344     }
  345     UpdateProgressWithInfoInit(NULL, TRUE);
  346     VolumeName = GetLogicalName(DriveIndex, PartitionOffset, TRUE, TRUE);
  347     wVolumeName = utf8_to_wchar(VolumeName);
  348     if (wVolumeName == NULL) {
  349         uprintf("Could not read volume name (%s)", VolumeName);
  350         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_GEN_FAILURE;
  351         goto out;
  352     }
  353 
  354     // Initialize COM
  355     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  356     IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
  357         RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
  358 
  359     // Create a VDS Loader Instance
  360     hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
  361         &IID_IVdsServiceLoader, (void **)&pLoader);
  362     if (hr != S_OK) {
  363         VDS_SET_ERROR(hr);
  364         uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString());
  365         goto out;
  366     }
  367 
  368     // Load the VDS Service
  369     hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
  370     IVdsServiceLoader_Release(pLoader);
  371     if (hr != S_OK) {
  372         VDS_SET_ERROR(hr);
  373         uprintf("Could not load VDS Service: %s", WindowsErrorString());
  374         goto out;
  375     }
  376 
  377     // Wait for the Service to become ready if needed
  378     hr = IVdsService_WaitForServiceReady(pService);
  379     if (hr != S_OK) {
  380         VDS_SET_ERROR(hr);
  381         uprintf("VDS Service is not ready: %s", WindowsErrorString());
  382         goto out;
  383     }
  384 
  385     // Query the VDS Service Providers
  386     hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
  387     if (hr != S_OK) {
  388         VDS_SET_ERROR(hr);
  389         uprintf("Could not query VDS Service Providers: %s", WindowsErrorString());
  390         goto out;
  391     }
  392 
  393     while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
  394         IVdsProvider *pProvider;
  395         IVdsSwProvider *pSwProvider;
  396         IEnumVdsObject *pEnumPack;
  397         IUnknown *pPackUnk;
  398         CHECK_FOR_USER_CANCEL;
  399 
  400         // Get VDS Provider
  401         hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider);
  402         IUnknown_Release(pUnk);
  403         if (hr != S_OK) {
  404             VDS_SET_ERROR(hr);
  405             uprintf("Could not get VDS Provider: %s", WindowsErrorString());
  406             goto out;
  407         }
  408 
  409         // Get VDS Software Provider
  410         hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider);
  411         IVdsProvider_Release(pProvider);
  412         if (hr != S_OK) {
  413             VDS_SET_ERROR(hr);
  414             uprintf("Could not get VDS Software Provider: %s", WindowsErrorString());
  415             goto out;
  416         }
  417 
  418         // Get VDS Software Provider Packs
  419         hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
  420         IVdsSwProvider_Release(pSwProvider);
  421         if (hr != S_OK) {
  422             VDS_SET_ERROR(hr);
  423             uprintf("Could not get VDS Software Provider Packs: %s", WindowsErrorString());
  424             goto out;
  425         }
  426 
  427         // Enumerate Provider Packs
  428         while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
  429             IVdsPack *pPack;
  430             IEnumVdsObject *pEnumVolume;
  431             IUnknown *pVolumeUnk;
  432             CHECK_FOR_USER_CANCEL;
  433 
  434             hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack);
  435             IUnknown_Release(pPackUnk);
  436             if (hr != S_OK) {
  437                 VDS_SET_ERROR(hr);
  438                 uprintf("Could not query VDS Software Provider Pack: %s", WindowsErrorString());
  439                 goto out;
  440             }
  441 
  442             // Use the pack interface to access the volumes
  443             hr = IVdsPack_QueryVolumes(pPack, &pEnumVolume);
  444             if (hr != S_OK) {
  445                 VDS_SET_ERROR(hr);
  446                 uprintf("Could not query VDS volumes: %s", WindowsErrorString());
  447                 goto out;
  448             }
  449 
  450             // List volumes
  451             while (IEnumVdsObject_Next(pEnumVolume, 1, &pVolumeUnk, &ulFetched) == S_OK) {
  452                 BOOL match;
  453                 HRESULT hr2 = E_FAIL;
  454                 VDS_VOLUME_PROP VolumeProps;
  455                 LPWSTR *wszPathArray;
  456                 ULONG ulPercentCompleted, ulNumberOfPaths;
  457                 USHORT usFsVersion = 0;
  458                 IVdsVolume *pVolume;
  459                 IVdsAsync* pAsync;
  460                 IVdsVolumeMF3 *pVolumeMF3;
  461                 CHECK_FOR_USER_CANCEL;
  462 
  463                 // Get the volume interface.
  464                 hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVdsVolume, (void **)&pVolume);
  465                 if (hr != S_OK) {
  466                     VDS_SET_ERROR(hr);
  467                     uprintf("Could not query VDS Volume Interface: %s", WindowsErrorString());
  468                     goto out;
  469                 }
  470 
  471                 hr = IVdsVolume_GetProperties(pVolume, &VolumeProps);
  472                 if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) {
  473                     VDS_SET_ERROR(hr);
  474                     IVdsVolume_Release(pVolume);
  475                     uprintf("Could not query VDS Volume Properties: %s", WindowsErrorString());
  476                     continue;
  477                 }
  478                 CoTaskMemFree(VolumeProps.pwszName);
  479 
  480                 // Instantiate the IVdsVolumeMF3 interface for our volume.
  481                 hr = IVdsVolume_QueryInterface(pVolume, &IID_IVdsVolumeMF3, (void **)&pVolumeMF3);
  482                 IVdsVolume_Release(pVolume);
  483                 if (hr != S_OK) {
  484                     VDS_SET_ERROR(hr);
  485                     uprintf("Could not access VDS VolumeMF3 interface: %s", WindowsErrorString());
  486                     continue;
  487                 }
  488 
  489                 // Query the volume GUID
  490                 hr = IVdsVolumeMF3_QueryVolumeGuidPathnames(pVolumeMF3, &wszPathArray, &ulNumberOfPaths);
  491                 if (hr != S_OK) {
  492                     VDS_SET_ERROR(hr);
  493                     uprintf("Could not query VDS VolumeGuidPathnames: %s", WindowsErrorString());
  494                     continue;
  495                 }
  496 
  497                 if (ulNumberOfPaths > 1)
  498                     uprintf("Notice: Volume %S has more than one GUID...", wszPathArray[0]);
  499 
  500                 match = (wcscmp(wVolumeName, wszPathArray[0]) == 0);
  501                 CoTaskMemFree(wszPathArray);
  502                 if (!match)
  503                     continue;
  504 
  505                 bFoundVolume = TRUE;
  506                 if (strcmp(Label, FileSystemLabel[FS_UDF]) == 0)
  507                     usFsVersion = ReadSetting32(SETTING_USE_UDF_VERSION);
  508                 if (ClusterSize < 0x200) {
  509                     ClusterSize = 0;
  510                     uprintf("Using default cluster size");
  511                 } else {
  512                     uprintf("Using cluster size: %d bytes", ClusterSize);
  513                 }
  514                 format_percent = 0.0f;
  515                 uprintf("%s format was selected", (Flags & FP_QUICK) ? "Quick" : "Slow");
  516                 if (Flags & FP_COMPRESSION)
  517                     uprintf("NTFS compression is enabled");
  518 
  519                 hr = IVdsVolumeMF3_FormatEx2(pVolumeMF3, wFSName, usFsVersion, ClusterSize, wLabel, Flags, &pAsync);
  520                 while (SUCCEEDED(hr)) {
  521                     if (IS_ERROR(FormatStatus)) {
  522                         IVdsAsync_Cancel(pAsync);
  523                         break;
  524                     }
  525                     hr = IVdsAsync_QueryStatus(pAsync, &hr2, &ulPercentCompleted);
  526                     if (SUCCEEDED(hr)) {
  527                         if (Flags & FP_QUICK) {
  528                             // Progress report on quick format is useless, so we'll just pretend we have 2 tasks
  529                             PrintInfo(0, MSG_218, (ulPercentCompleted < 100) ? 1 : 2, 2);
  530                             UpdateProgress(OP_CREATE_FS, (float)ulPercentCompleted);
  531                         } else {
  532                             UpdateProgressWithInfo(OP_FORMAT, MSG_217, ulPercentCompleted, 100);
  533                         }
  534                         hr = hr2;
  535                         if (hr == S_OK)
  536                             break;
  537                         if (hr == VDS_E_OPERATION_PENDING)
  538                             hr = S_OK;
  539                     }
  540                     Sleep(500);
  541                 }
  542                 if (!SUCCEEDED(hr)) {
  543                     VDS_SET_ERROR(hr);
  544                     uprintf("Could not format drive: %s", WindowsErrorString());
  545                     goto out;
  546                 }
  547 
  548                 IVdsAsync_Release(pAsync);
  549                 IVdsVolumeMF3_Release(pVolumeMF3);
  550 
  551                 if (!IS_ERROR(FormatStatus)) {
  552                     uprintf("Format completed.");
  553                     r = TRUE;
  554                 }
  555                 goto out;
  556             }
  557         }
  558     }
  559 
  560 out:
  561     if ((!bFoundVolume) && (FormatStatus == 0))
  562         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_PATH_NOT_FOUND;
  563     safe_free(VolumeName);
  564     safe_free(wVolumeName);
  565     safe_free(wLabel);
  566     safe_free(wFSName);
  567     return r;
  568 }
  569 
  570 /*
  571  * Call on fmifs.dll's FormatEx() to format the drive
  572  */
  573 static BOOL FormatNative(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags)
  574 {
  575     BOOL r = FALSE;
  576     PF_DECL(FormatEx);
  577     PF_DECL(EnableVolumeCompression);
  578     char *locale, *VolumeName = NULL;
  579     WCHAR* wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName);
  580     size_t i;
  581 
  582     if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) {
  583         PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs);
  584     } else {
  585         PrintInfoDebug(0, MSG_222, FSName);
  586     }
  587     VolumeName = GetLogicalName(DriveIndex, PartitionOffset, TRUE, TRUE);
  588     wVolumeName = utf8_to_wchar(VolumeName);
  589     if (wVolumeName == NULL) {
  590         uprintf("Could not read volume name (%s)", VolumeName);
  591         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE;
  592         goto out;
  593     }
  594     // Hey, nice consistency here, Microsoft! -  FormatEx() fails if wVolumeName has
  595     // a trailing backslash, but EnableCompression() fails without...
  596     wVolumeName[wcslen(wVolumeName)-1] = 0;     // Remove trailing backslash
  597 
  598     // LoadLibrary("fmifs.dll") appears to changes the locale, which can lead to
  599     // problems with tolower(). Make sure we restore the locale. For more details,
  600     // see https://sourceforge.net/p/mingw/mailman/message/29269040/
  601     locale = setlocale(LC_ALL, NULL);
  602     PF_INIT_OR_OUT(FormatEx, fmifs);
  603     PF_INIT(EnableVolumeCompression, fmifs);
  604     setlocale(LC_ALL, locale);
  605 
  606     if (ClusterSize < 0x200) {
  607         // 0 is FormatEx's value for default, which we need to use for UDF
  608         ClusterSize = 0;
  609         uprintf("Using default cluster size");
  610     } else {
  611         uprintf("Using cluster size: %d bytes", ClusterSize);
  612     }
  613     format_percent = 0.0f;
  614     task_number = 0;
  615 
  616     uprintf("%s format was selected", (Flags & FP_QUICK) ? "Quick" : "Slow");
  617     for (i = 0; i < WRITE_RETRIES; i++) {
  618         pfFormatEx(wVolumeName, SelectedDrive.MediaType, wFSName, wLabel,
  619             (Flags & FP_QUICK), ClusterSize, FormatExCallback);
  620         if (!IS_ERROR(FormatStatus) || (HRESULT_CODE(FormatStatus) == ERROR_CANCELLED))
  621             break;
  622         uprintf("%s - Retrying...", WindowsErrorString());
  623         Sleep(WRITE_TIMEOUT);
  624     }
  625     if (IS_ERROR(FormatStatus))
  626         goto out;
  627 
  628     if (Flags & FP_COMPRESSION) {
  629         wVolumeName[wcslen(wVolumeName)] = '\\';    // Add trailing backslash back again
  630         if (pfEnableVolumeCompression(wVolumeName, FPF_COMPRESSED)) {
  631             uprintf("Enabled NTFS compression");
  632         } else {
  633             uprintf("Could not enable NTFS compression: %s", WindowsErrorString());
  634         }
  635     }
  636 
  637     if (!IS_ERROR(FormatStatus)) {
  638         uprintf("Format completed.");
  639         r = TRUE;
  640     }
  641 
  642 out:
  643     if (!r && !IS_ERROR(FormatStatus))
  644         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|SCODE_CODE(GetLastError());
  645     safe_free(VolumeName);
  646     safe_free(wVolumeName);
  647     safe_free(wLabel);
  648     safe_free(wFSName);
  649     return r;
  650 }
  651 
  652 static BOOL FormatPartition(DWORD DriveIndex, uint64_t PartitionOffset, DWORD UnitAllocationSize, USHORT FSType, LPCSTR Label, DWORD Flags)
  653 {
  654     if ((DriveIndex < 0x80) || (DriveIndex > 0x100) || (FSType >= FS_MAX) ||
  655         ((UnitAllocationSize != 0) && (!IS_POWER_OF_2(UnitAllocationSize)))) {
  656         ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER;
  657         return FALSE;
  658     }
  659     actual_fs_type = FSType;
  660     if ((FSType == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32) || (Flags & FP_LARGE_FAT32)))
  661         return FormatLargeFAT32(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
  662     else if (FSType >= FS_EXT2)
  663         return FormatExtFs(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
  664     else if (use_vds)
  665         return FormatNativeVds(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
  666     else
  667         return FormatNative(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
  668 }
  669 
  670 /*
  671  * Call on fmifs.dll's Chkdsk() to fixup the filesystem
  672  */
  673 static BOOL CheckDisk(char DriveLetter)
  674 {
  675     BOOL r = FALSE;
  676     PF_DECL(Chkdsk);
  677     WCHAR wDriveRoot[] = L"?:\\";
  678     WCHAR wFSType[32];
  679     size_t i;
  680 
  681     wDriveRoot[0] = (WCHAR)DriveLetter;
  682     PrintInfoDebug(0, MSG_223);
  683 
  684     PF_INIT_OR_OUT(Chkdsk, Fmifs);
  685 
  686     GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType));
  687     // We may have a " (Default)" trail
  688     for (i=0; i<wcslen(wFSType); i++) {
  689         if (wFSType[i] == ' ') {
  690             wFSType[i] = 0;
  691             break;
  692         }
  693     }
  694 
  695     pfChkdsk(wDriveRoot, wFSType, FALSE, FALSE, FALSE, FALSE, NULL, NULL, ChkdskCallback);
  696     if (!IS_ERROR(FormatStatus)) {
  697         uprintf("NTFS Fixup completed.\n");
  698         r = TRUE;
  699     }
  700 
  701 out:
  702     return r;
  703 }
  704 
  705 static BOOL ClearMBRGPT(HANDLE hPhysicalDrive, LONGLONG DiskSize, DWORD SectorSize, BOOL add1MB)
  706 {
  707     BOOL r = FALSE;
  708     uint64_t i, last_sector = DiskSize/SectorSize, num_sectors_to_clear;
  709     unsigned char* pBuf = (unsigned char*) calloc(SectorSize, 1);
  710 
  711     PrintInfoDebug(0, MSG_224);
  712     if (pBuf == NULL) {
  713         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
  714         goto out;
  715     }
  716     // http://en.wikipedia.org/wiki/GUID_Partition_Table tells us we should clear 34 sectors at the
  717     // beginning and 33 at the end. We bump these values to MAX_SECTORS_TO_CLEAR each end to help
  718     // with reluctant access to large drive.
  719 
  720     // We try to clear at least 1MB + the PBR when Large FAT32 is selected (add1MB), but
  721     // don't do it otherwise, as it seems unnecessary and may take time for slow drives.
  722     // Also, for various reasons (one of which being that Windows seems to have issues
  723     // with GPT drives that contain a lot of small partitions) we try not not to clear
  724     // sectors further than the lowest partition already residing on the disk.
  725     num_sectors_to_clear = min(SelectedDrive.FirstDataSector, (DWORD)((add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR));
  726     // Special case for big floppy disks (FirstDataSector = 0)
  727     if (num_sectors_to_clear < 4)
  728         num_sectors_to_clear = (DWORD)((add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR);
  729 
  730     uprintf("Erasing %d sectors", num_sectors_to_clear);
  731     for (i = 0; i < num_sectors_to_clear; i++) {
  732         CHECK_FOR_USER_CANCEL;
  733         if (write_sectors(hPhysicalDrive, SectorSize, i, 1, pBuf) != SectorSize)
  734             goto out;
  735     }
  736     for (i = last_sector - MAX_SECTORS_TO_CLEAR; i < last_sector; i++) {
  737         CHECK_FOR_USER_CANCEL;
  738         // Windows seems to be an ass about keeping a lock on a backup GPT,
  739         // so we try to be lenient about not being able to clear it.
  740         if (write_sectors(hPhysicalDrive, SectorSize, i, 1, pBuf) != SectorSize)
  741             break;
  742     }
  743     r = TRUE;
  744 
  745 out:
  746     safe_free(pBuf);
  747     return r;
  748 }
  749 
  750 /*
  751  * Process the Master Boot Record
  752  */
  753 static BOOL WriteMBR(HANDLE hPhysicalDrive)
  754 {
  755     BOOL r = FALSE;
  756     DWORD size;
  757     unsigned char* buffer = NULL;
  758     FAKE_FD fake_fd = { 0 };
  759     FILE* fp = (FILE*)&fake_fd;
  760     const char* using_msg = "Using %s MBR";
  761 
  762     if (SelectedDrive.SectorSize < 512)
  763         goto out;
  764 
  765     if (partition_type == PARTITION_STYLE_GPT) {
  766         // Add a notice with a protective MBR
  767         fake_fd._handle = (char*)hPhysicalDrive;
  768         set_bytes_per_sector(SelectedDrive.SectorSize);
  769         uprintf(using_msg, "Rufus protective");
  770         r = write_rufus_msg_mbr(fp);
  771         goto notify;
  772     }
  773 
  774     // FormatEx rewrites the MBR and removes the LBA attribute of FAT16
  775     // and FAT32 partitions - we need to correct this in the MBR
  776     buffer = (unsigned char*)_mm_malloc(SelectedDrive.SectorSize, 16);
  777     if (buffer == NULL) {
  778         uprintf("Could not allocate memory for MBR");
  779         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
  780         goto out;
  781     }
  782 
  783     if (!read_sectors(hPhysicalDrive, SelectedDrive.SectorSize, 0, 1, buffer)) {
  784         uprintf("Could not read MBR\n");
  785         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
  786         goto out;
  787     }
  788 
  789     switch (ComboBox_GetCurItemData(hFileSystem)) {
  790     case FS_FAT16:
  791         if (buffer[0x1c2] == 0x0e) {
  792             uprintf("Partition is already FAT16 LBA...\n");
  793         } else if ((buffer[0x1c2] != 0x04) && (buffer[0x1c2] != 0x06)) {
  794             uprintf("Warning: converting a non FAT16 partition to FAT16 LBA: FS type=0x%02x\n", buffer[0x1c2]);
  795         }
  796         buffer[0x1c2] = 0x0e;
  797         break;
  798     case FS_FAT32:
  799         if (buffer[0x1c2] == 0x0c) {
  800             uprintf("Partition is already FAT32 LBA...\n");
  801         } else if (buffer[0x1c2] != 0x0b) {
  802             uprintf("Warning: converting a non FAT32 partition to FAT32 LBA: FS type=0x%02x\n", buffer[0x1c2]);
  803         }
  804         buffer[0x1c2] = 0x0c;
  805         break;
  806     }
  807     if ((boot_type != BT_NON_BOOTABLE) && (target_type == TT_BIOS)) {
  808         // Set first partition bootable - masquerade as per the DiskID selected
  809         buffer[0x1be] = IsChecked(IDC_RUFUS_MBR) ?
  810             (BYTE)ComboBox_GetCurItemData(hDiskID):0x80;
  811         uprintf("Set bootable USB partition as 0x%02X\n", buffer[0x1be]);
  812     }
  813 
  814     if (!write_sectors(hPhysicalDrive, SelectedDrive.SectorSize, 0, 1, buffer)) {
  815         uprintf("Could not write MBR\n");
  816         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
  817         goto out;
  818     }
  819 
  820     fake_fd._handle = (char*)hPhysicalDrive;
  821     set_bytes_per_sector(SelectedDrive.SectorSize);
  822 
  823     // What follows is really a case statement with complex conditions listed
  824     // by order of preference
  825     if ((boot_type == BT_IMAGE) && HAS_WINDOWS(img_report) && (allow_dual_uefi_bios) && (target_type == TT_BIOS))
  826         goto windows_mbr;
  827 
  828     // Non bootable or forced UEFI (zeroed MBR)
  829     if ((boot_type == BT_NON_BOOTABLE) || (target_type == TT_UEFI)) {
  830         uprintf(using_msg, "Zeroed");
  831         r = write_zero_mbr(fp);
  832         goto notify;
  833     }
  834 
  835     // Syslinux
  836     if ( (boot_type == BT_SYSLINUX_V4) || (boot_type == BT_SYSLINUX_V6) ||
  837          ((boot_type == BT_IMAGE) && HAS_SYSLINUX(img_report)) ) {
  838         uprintf(using_msg, "Syslinux");
  839         r = write_syslinux_mbr(fp);
  840         goto notify;
  841     }
  842 
  843     // Grub 2.0
  844     if ( ((boot_type == BT_IMAGE) && (img_report.has_grub2)) || (boot_type == BT_GRUB2) ) {
  845         uprintf(using_msg, "Grub 2.0");
  846         r = write_grub2_mbr(fp);
  847         goto notify;
  848     }
  849 
  850     // Grub4DOS
  851     if ( ((boot_type == BT_IMAGE) && (img_report.has_grub4dos)) || (boot_type == BT_GRUB4DOS) ) {
  852         uprintf(using_msg, "Grub4DOS");
  853         r = write_grub4dos_mbr(fp);
  854         goto notify;
  855     }
  856 
  857     // ReactOS
  858     if (boot_type == BT_REACTOS) {
  859         uprintf(using_msg, "ReactOS");
  860         r = write_reactos_mbr(fp);
  861         goto notify;
  862     }
  863 
  864     // KolibriOS
  865     if ( (boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report) && (IS_FAT(fs_type))) {
  866         uprintf(using_msg, "KolibriOS");
  867         r = write_kolibrios_mbr(fp);
  868         goto notify;
  869     }
  870 
  871     // If everything else failed, fall back to a conventional Windows/Rufus MBR
  872 windows_mbr:
  873     if ((HAS_WINPE(img_report) && !img_report.uses_minint) || (IsChecked(IDC_RUFUS_MBR))) {
  874         uprintf(using_msg, APPLICATION_NAME);
  875         r = write_rufus_mbr(fp);
  876     } else {
  877         uprintf(using_msg, "Windows 7");
  878         r = write_win7_mbr(fp);
  879     }
  880 
  881 notify:
  882     // Tell the system we've updated the disk properties
  883     if (!DeviceIoControl(hPhysicalDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL))
  884         uprintf("Failed to notify system about disk properties update: %s\n", WindowsErrorString());
  885 
  886 out:
  887     safe_mm_free(buffer);
  888     return r;
  889 }
  890 
  891 /*
  892  * Write Secondary Boot Record (usually right after the MBR)
  893  */
  894 static BOOL WriteSBR(HANDLE hPhysicalDrive)
  895 {
  896     // TODO: Do we need anything special for 4K sectors?
  897     DWORD size, max_size, br_size = 0x200;
  898     int r, sub_type = boot_type;
  899     unsigned char* buf = NULL;
  900     FAKE_FD fake_fd = { 0 };
  901     FILE* fp = (FILE*)&fake_fd;
  902 
  903     fake_fd._handle = (char*)hPhysicalDrive;
  904     set_bytes_per_sector(SelectedDrive.SectorSize);
  905     // Syslinux has precedence over Grub
  906     if ((boot_type == BT_IMAGE) && (!HAS_SYSLINUX(img_report))) {
  907         if (img_report.has_grub4dos)
  908             sub_type = BT_GRUB4DOS;
  909         if (img_report.has_grub2)
  910             sub_type = BT_GRUB2;
  911     }
  912 
  913     // Use BT_MAX for the protective message
  914     if ((boot_type != BT_NON_BOOTABLE) && (partition_type == PARTITION_STYLE_GPT))
  915         sub_type = BT_MAX;
  916 
  917     switch (sub_type) {
  918     case BT_GRUB4DOS:
  919         uprintf("Writing Grub4Dos SBR");
  920         buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_GR_GRUB_GRLDR_MBR), _RT_RCDATA, "grldr.mbr", &size, FALSE);
  921         if ((buf == NULL) || (size <= br_size)) {
  922             uprintf("grldr.mbr is either not present or too small");
  923             return FALSE;
  924         }
  925         buf = &buf[br_size];
  926         size -= br_size;
  927         break;
  928     case BT_GRUB2:
  929         if (grub2_buf != NULL) {
  930             uprintf("Writing Grub 2.0 SBR (from download) %s",
  931                 IsBufferInDB(grub2_buf, grub2_len)?"✓":"✗");
  932             buf = grub2_buf;
  933             size = (DWORD)grub2_len;
  934         } else {
  935             uprintf("Writing Grub 2.0 SBR (from embedded)");
  936             buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_GR_GRUB2_CORE_IMG), _RT_RCDATA, "core.img", &size, FALSE);
  937             if (buf == NULL) {
  938                 uprintf("Could not access core.img");
  939                 return FALSE;
  940             }
  941         }
  942         break;
  943     case BT_MAX:
  944         uprintf("Writing protective message SBR");
  945         size = 4 * KB;
  946         br_size = 17 * KB;  // 34 sectors are reserved for protective MBR + primary GPT
  947         buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_SBR_MSG), _RT_RCDATA, "msg.txt", &size, TRUE);
  948         if (buf == NULL) {
  949             uprintf("Could not access message");
  950             return FALSE;
  951         }
  952         break;
  953     default:
  954         // No need to write secondary block
  955         return TRUE;
  956     }
  957 
  958     // Ensure that we have sufficient space for the SBR
  959     max_size = IsChecked(IDC_OLD_BIOS_FIXES) ?
  960         (DWORD)(SelectedDrive.SectorsPerTrack * SelectedDrive.SectorSize) : 1 * MB;
  961     if (br_size + size > max_size) {
  962         uprintf("  SBR size is too large - You may need to uncheck 'Add fixes for old BIOSes'.");
  963         if (sub_type == BT_MAX)
  964             safe_free(buf);
  965         return FALSE;
  966     }
  967 
  968     r = write_data(fp, br_size, buf, (uint64_t)size);
  969     safe_free(grub2_buf);
  970     if (sub_type == BT_MAX)
  971         safe_free(buf);
  972     return (r != 0);
  973 }
  974 
  975 /*
  976  * Process the Partition Boot Record
  977  */
  978 static __inline const char* bt_to_name(void) {
  979     switch (boot_type) {
  980     case BT_FREEDOS: return "FreeDOS";
  981     case BT_REACTOS: return "ReactOS";
  982     default:
  983         return ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) ? "KolibriOS" : "Standard";
  984     }
  985 }
  986 
  987 BOOL WritePBR(HANDLE hLogicalVolume)
  988 {
  989     int i;
  990     FAKE_FD fake_fd = { 0 };
  991     FILE* fp = (FILE*)&fake_fd;
  992     const char* using_msg = "Using %s %s partition boot record";
  993 
  994     fake_fd._handle = (char*)hLogicalVolume;
  995     set_bytes_per_sector(SelectedDrive.SectorSize);
  996 
  997     switch (actual_fs_type) {
  998     case FS_FAT16:
  999         uprintf(using_msg, bt_to_name(), "FAT16");
 1000         if (!is_fat_16_fs(fp)) {
 1001             uprintf("New volume does not have a FAT16 boot sector - aborting");
 1002             break;
 1003         }
 1004         uprintf("Confirmed new volume has a FAT16 boot sector");
 1005         if (boot_type == BT_FREEDOS) {
 1006             if (!write_fat_16_fd_br(fp, 0)) break;
 1007         } else if (boot_type == BT_REACTOS) {
 1008             if (!write_fat_16_ros_br(fp, 0)) break;
 1009         } else if ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) {
 1010             uprintf("FAT16 is not supported for KolibriOS\n"); break;
 1011         } else {
 1012             if (!write_fat_16_br(fp, 0)) break;
 1013         }
 1014         // Disk Drive ID needs to be corrected on XP
 1015         if (!write_partition_physical_disk_drive_id_fat16(fp))
 1016             break;
 1017         return TRUE;
 1018     case FS_FAT32:
 1019         uprintf(using_msg, bt_to_name(), "FAT32");
 1020         for (i = 0; i < 2; i++) {
 1021             if (!is_fat_32_fs(fp)) {
 1022                 uprintf("New volume does not have a %s FAT32 boot sector - aborting\n", i?"secondary":"primary");
 1023                 break;
 1024             }
 1025             uprintf("Confirmed new volume has a %s FAT32 boot sector\n", i?"secondary":"primary");
 1026             uprintf("Setting %s FAT32 boot sector for boot...\n", i?"secondary":"primary");
 1027             if (boot_type == BT_FREEDOS) {
 1028                 if (!write_fat_32_fd_br(fp, 0)) break;
 1029             } else if (boot_type == BT_REACTOS) {
 1030                 if (!write_fat_32_ros_br(fp, 0)) break;
 1031             } else if ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) {
 1032                 if (!write_fat_32_kos_br(fp, 0)) break;
 1033             } else if ((boot_type == BT_IMAGE) && HAS_BOOTMGR(img_report)) {
 1034                 if (!write_fat_32_pe_br(fp, 0)) break;
 1035             } else if ((boot_type == BT_IMAGE) && HAS_WINPE(img_report)) {
 1036                 if (!write_fat_32_nt_br(fp, 0)) break;
 1037             } else {
 1038                 if (!write_fat_32_br(fp, 0)) break;
 1039             }
 1040             // Disk Drive ID needs to be corrected on XP
 1041             if (!write_partition_physical_disk_drive_id_fat32(fp))
 1042                 break;
 1043             fake_fd._offset += 6 * SelectedDrive.SectorSize;
 1044         }
 1045         return TRUE;
 1046     case FS_NTFS:
 1047         uprintf(using_msg, bt_to_name(), "NTFS");
 1048         if (!is_ntfs_fs(fp)) {
 1049             uprintf("New volume does not have an NTFS boot sector - aborting\n");
 1050             break;
 1051         }
 1052         uprintf("Confirmed new volume has an NTFS boot sector\n");
 1053         if (!write_ntfs_br(fp)) break;
 1054         // Note: NTFS requires a full remount after writing the PBR. We dismount when we lock
 1055         // and also go through a forced remount, so that shouldn't be an issue.
 1056         // But with NTFS, if you don't remount, you don't boot!
 1057         return TRUE;
 1058     default:
 1059         uprintf("Unsupported FS for FS BR processing - aborting\n");
 1060         break;
 1061     }
 1062     FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 1063     return FALSE;
 1064 }
 1065 
 1066 /*
 1067  * Setup WinPE for bootable USB
 1068  */
 1069 static BOOL SetupWinPE(char drive_letter)
 1070 {
 1071     char src[64], dst[32];
 1072     const char* basedir[3] = { "i386", "amd64", "minint" };
 1073     const char* patch_str_org[2] = { "\\minint\\txtsetup.sif", "\\minint\\system32\\" };
 1074     const char* patch_str_rep[2][2] = { { "\\i386\\txtsetup.sif", "\\i386\\system32\\" } ,
 1075                                         { "\\amd64\\txtsetup.sif", "\\amd64\\system32\\" } };
 1076     const char *win_nt_bt_org = "$win_nt$.~bt";
 1077     const char *rdisk_zero = "rdisk(0)";
 1078     const LARGE_INTEGER liZero = { {0, 0} };
 1079     char setupsrcdev[64];
 1080     HANDLE handle = INVALID_HANDLE_VALUE;
 1081     DWORD i, j, size, rw_size, index = 0;
 1082     BOOL r = FALSE;
 1083     char* buffer = NULL;
 1084 
 1085     if ((img_report.winpe & WINPE_AMD64) == WINPE_AMD64)
 1086         index = 1;
 1087     else if ((img_report.winpe & WINPE_MININT) == WINPE_MININT)
 1088         index = 2;
 1089     // Allow other values than harddisk 1, as per user choice for disk ID
 1090     static_sprintf(setupsrcdev, "SetupSourceDevice = \"\\device\\harddisk%d\\partition1\"",
 1091         ComboBox_GetCurSel(hDiskID));
 1092     // Copy of ntdetect.com in root
 1093     static_sprintf(src, "%c:\\%s\\ntdetect.com", drive_letter, basedir[2*(index/2)]);
 1094     static_sprintf(dst, "%c:\\ntdetect.com", drive_letter);
 1095     CopyFileA(src, dst, TRUE);
 1096     if (!img_report.uses_minint) {
 1097         // Create a copy of txtsetup.sif, as we want to keep the i386/amd64 files unmodified
 1098         static_sprintf(src, "%c:\\%s\\txtsetup.sif", drive_letter, basedir[index]);
 1099         static_sprintf(dst, "%c:\\txtsetup.sif", drive_letter);
 1100         if (!CopyFileA(src, dst, TRUE)) {
 1101             uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
 1102         }
 1103         if (insert_section_data(dst, "[SetupData]", setupsrcdev, FALSE) == NULL) {
 1104             uprintf("Failed to add SetupSourceDevice in %s\n", dst);
 1105             goto out;
 1106         }
 1107         uprintf("Successfully added '%s' to %s\n", setupsrcdev, dst);
 1108     }
 1109 
 1110     static_sprintf(src, "%c:\\%s\\setupldr.bin", drive_letter,  basedir[2*(index/2)]);
 1111     static_sprintf(dst, "%c:\\BOOTMGR", drive_letter);
 1112     if (!CopyFileA(src, dst, TRUE)) {
 1113         uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
 1114     }
 1115 
 1116     // \minint with /minint option doesn't require further processing => return true
 1117     // \minint and no \i386 without /minint is unclear => return error
 1118     if (img_report.winpe&WINPE_MININT) {
 1119         if (img_report.uses_minint) {
 1120             uprintf("Detected \\minint directory with /minint option: nothing to patch\n");
 1121             r = TRUE;
 1122         } else if (!(img_report.winpe&(WINPE_I386|WINPE_AMD64))) {
 1123             uprintf("Detected \\minint directory only but no /minint option: not sure what to do\n");
 1124         }
 1125         goto out;
 1126     }
 1127 
 1128     // At this stage we only handle \i386
 1129     handle = CreateFileA(dst, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
 1130         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 1131     if (handle == INVALID_HANDLE_VALUE) {
 1132         uprintf("Could not open %s for patching: %s\n", dst, WindowsErrorString());
 1133         goto out;
 1134     }
 1135     size = GetFileSize(handle, NULL);
 1136     if (size == INVALID_FILE_SIZE) {
 1137         uprintf("Could not get size for file %s: %s\n", dst, WindowsErrorString());
 1138         goto out;
 1139     }
 1140     buffer = (char*)malloc(size);
 1141     if (buffer == NULL)
 1142         goto out;
 1143     if ((!ReadFile(handle, buffer, size, &rw_size, NULL)) || (size != rw_size)) {
 1144         uprintf("Could not read file %s: %s\n", dst, WindowsErrorString());
 1145         goto out;
 1146     }
 1147     if (!SetFilePointerEx(handle, liZero, NULL, FILE_BEGIN)) {
 1148         uprintf("Could not rewind file %s: %s\n", dst, WindowsErrorString());
 1149         goto out;
 1150     }
 1151 
 1152     // Patch setupldr.bin
 1153     uprintf("Patching file %s\n", dst);
 1154     // Remove CRC check for 32 bit part of setupldr.bin from Win2k3
 1155     if ((size > 0x2061) && (buffer[0x2060] == 0x74) && (buffer[0x2061] == 0x03)) {
 1156         buffer[0x2060] = 0xeb;
 1157         buffer[0x2061] = 0x1a;
 1158         uprintf("  0x00002060: 0x74 0x03 -> 0xEB 0x1A (disable Win2k3 CRC check)\n");
 1159     }
 1160     for (i=1; i<size-32; i++) {
 1161         for (j=0; j<ARRAYSIZE(patch_str_org); j++) {
 1162             if (safe_strnicmp(&buffer[i], patch_str_org[j], strlen(patch_str_org[j])-1) == 0) {
 1163                 uprintf("  0x%08X: '%s' -> '%s'\n", i, &buffer[i], patch_str_rep[index][j]);
 1164                 strcpy(&buffer[i], patch_str_rep[index][j]);
 1165                 i += (DWORD)max(strlen(patch_str_org[j]), strlen(patch_str_rep[index][j])); // in case org is a substring of rep
 1166             }
 1167         }
 1168     }
 1169 
 1170     if (!img_report.uses_minint) {
 1171         // Additional setupldr.bin/bootmgr patching
 1172         for (i=0; i<size-32; i++) {
 1173             // rdisk(0) -> rdisk(#) disk masquerading
 1174             // NB: only the first one seems to be needed
 1175             if (safe_strnicmp(&buffer[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) {
 1176                 buffer[i+6] = 0x30 + ComboBox_GetCurSel(hDiskID);
 1177                 uprintf("  0x%08X: '%s' -> 'rdisk(%c)'\n", i, rdisk_zero, buffer[i+6]);
 1178             }
 1179             // $WIN_NT$_~BT -> i386/amd64
 1180             if (safe_strnicmp(&buffer[i], win_nt_bt_org, strlen(win_nt_bt_org)-1) == 0) {
 1181                 uprintf("  0x%08X: '%s' -> '%s%s'\n", i, &buffer[i], basedir[index], &buffer[i+strlen(win_nt_bt_org)]);
 1182                 strcpy(&buffer[i], basedir[index]);
 1183                 // This ensures that we keep the terminator backslash
 1184                 buffer[i+strlen(basedir[index])] = buffer[i+strlen(win_nt_bt_org)];
 1185                 buffer[i+strlen(basedir[index])+1] = 0;
 1186             }
 1187         }
 1188     }
 1189 
 1190     if (!WriteFileWithRetry(handle, buffer, size, &rw_size, WRITE_RETRIES)) {
 1191         uprintf("Could not write patched file: %s\n", WindowsErrorString());
 1192         goto out;
 1193     }
 1194     r = TRUE;
 1195 
 1196 out:
 1197     safe_closehandle(handle);
 1198     safe_free(buffer);
 1199     return r;
 1200 }
 1201 
 1202 // Checks which versions of Windows are available in an install image
 1203 // to set our extraction index. Asks the user to select one if needed.
 1204 // Returns -2 on user cancel, -1 on other error, >=0 on success.
 1205 int SetWinToGoIndex(void)
 1206 {
 1207     char *mounted_iso, *build, mounted_image_path[128];
 1208     char xml_file[MAX_PATH] = "";
 1209     char *install_names[MAX_WININST];
 1210     StrArray version_name, version_index;
 1211     int i, build_nr = 0;
 1212     BOOL bNonStandard = FALSE;
 1213 
 1214     // Sanity checks
 1215     wintogo_index = -1;
 1216     wininst_index = 0;
 1217     if ((nWindowsVersion < WINDOWS_8) || ((WimExtractCheck(FALSE) & 4) == 0) ||
 1218         (ComboBox_GetCurItemData(hFileSystem) != FS_NTFS)) {
 1219         return -1;
 1220     }
 1221 
 1222     // If we have multiple windows install images, ask the user the one to use
 1223     if (img_report.wininst_index > 1) {
 1224         for (i = 0; i < img_report.wininst_index; i++)
 1225             install_names[i] = &img_report.wininst_path[i][2];
 1226         wininst_index = SelectionDialog(lmprintf(MSG_130), lmprintf(MSG_131), install_names, img_report.wininst_index);
 1227         if (wininst_index < 0)
 1228             return -2;
 1229         wininst_index--;
 1230         if ((wininst_index < 0) || (wininst_index >= MAX_WININST))
 1231             wininst_index = 0;
 1232     }
 1233 
 1234     // If we're not using a straigth install.wim, we need to mount the ISO to access it
 1235     if (!img_report.is_windows_img) {
 1236         mounted_iso = MountISO(image_path);
 1237         if (mounted_iso == NULL) {
 1238             uprintf("Could not mount ISO for Windows To Go selection");
 1239             return FALSE;
 1240         }
 1241         static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
 1242     }
 1243 
 1244     // Now take a look at the XML file in install.wim to list our versions
 1245     if ((GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, xml_file) == 0) || (xml_file[0] == 0)) {
 1246         // Last ditch effort to get a tmp file - just extract it to the current directory
 1247         static_strcpy(xml_file, ".\\RufVXml.tmp");
 1248     }
 1249     // GetTempFileName() may leave a file behind
 1250     DeleteFileU(xml_file);
 1251 
 1252     // Must use the Windows WIM API as 7z messes up the XML
 1253     if (!WimExtractFile_API(img_report.is_windows_img ? image_path : mounted_image_path,
 1254         0, "[1].xml", xml_file, FALSE)) {
 1255         uprintf("Could not acquire WIM index");
 1256         goto out;
 1257     }
 1258 
 1259     StrArrayCreate(&version_name, 16);
 1260     StrArrayCreate(&version_index, 16);
 1261     for (i = 0; StrArrayAdd(&version_index, get_token_data_file_indexed("IMAGE INDEX", xml_file, i + 1), FALSE) >= 0; i++) {
 1262         // Some people are apparently creating *unofficial* Windows ISOs that don't have DISPLAYNAME elements.
 1263         // If we are parsing such an ISO, try to fall back to using DESCRIPTION. Of course, since we don't use
 1264         // a formal XML parser, if an ISO mixes entries with both DISPLAYNAME and DESCRIPTION and others with
 1265         // only DESCRIPTION, the version names we report will be wrong.
 1266         // But hey, there's only so far I'm willing to go to help people who, not content to have demonstrated
 1267         // their utter ignorance on development matters, are also trying to lecture experienced developers
 1268         // about specific "noob mistakes"... that don't exist in the code they are trying to criticize.
 1269         if (StrArrayAdd(&version_name, get_token_data_file_indexed("DISPLAYNAME", xml_file, i + 1), FALSE) < 0) {
 1270             bNonStandard = TRUE;
 1271             if (StrArrayAdd(&version_name, get_token_data_file_indexed("DESCRIPTION", xml_file, i + 1), FALSE) < 0) {
 1272                 uprintf("Warning: Could not find a description for image index %d", i + 1);
 1273                 StrArrayAdd(&version_name, "Unknown Windows Version", TRUE);
 1274             }
 1275         }
 1276     }
 1277     if (bNonStandard)
 1278         uprintf("Warning: Nonstandard Windows image (missing <DISPLAYNAME> entries)");
 1279 
 1280     if (i > 1)
 1281         i = SelectionDialog(lmprintf(MSG_291), lmprintf(MSG_292), version_name.String, i);
 1282     if (i < 0) {
 1283         wintogo_index = -2; // Cancelled by the user
 1284     } else if (i == 0) {
 1285         wintogo_index = 1;
 1286     } else {
 1287         wintogo_index = atoi(version_index.String[i - 1]);
 1288     }
 1289     if (i > 0) {
 1290         // Get the build version
 1291         build = get_token_data_file_indexed("BUILD", xml_file, i);
 1292         if (build != NULL)
 1293             build_nr = atoi(build);
 1294         free(build);
 1295         uprintf("Will use '%s' (Build: %d, Index %s) for Windows To Go",
 1296             version_name.String[i - 1], build_nr, version_index.String[i - 1]);
 1297         // Need Windows 10 Creator Update or later for boot on REMOVABLE to work
 1298         if ((build_nr < 15000) && (SelectedDrive.MediaType != FixedMedia)) {
 1299             if (MessageBoxExU(hMainDialog, lmprintf(MSG_098), lmprintf(MSG_190),
 1300                 MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES)
 1301                 wintogo_index = -2;
 1302         }
 1303         // Display a notice about WppRecorder.sys for 1809 ISOs
 1304         if (build_nr == 17763) {
 1305             notification_info more_info;
 1306             more_info.id = MORE_INFO_URL;
 1307             more_info.url = WPPRECORDER_MORE_INFO_URL;
 1308             Notification(MSG_INFO, NULL, &more_info, lmprintf(MSG_128, "Windows To Go"), lmprintf(MSG_133));
 1309         }
 1310     }
 1311     StrArrayDestroy(&version_name);
 1312     StrArrayDestroy(&version_index);
 1313 
 1314 out:
 1315     DeleteFileU(xml_file);
 1316     if (!img_report.is_windows_img)
 1317         UnMountISO();
 1318     return wintogo_index;
 1319 }
 1320 
 1321 // http://technet.microsoft.com/en-ie/library/jj721578.aspx
 1322 // As opposed to the technet guide above, we don't set internal drives offline,
 1323 // due to people wondering why they can't see them by default and we also use
 1324 // bcdedit rather than 'unattend.xml' to disable the recovery environment.
 1325 static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
 1326 {
 1327     char *mounted_iso, *ms_efi = NULL, mounted_image_path[128], cmd[MAX_PATH];
 1328     ULONG cluster_size;
 1329 
 1330     uprintf("Windows To Go mode selected");
 1331     // Additional sanity checks
 1332     if ((use_esp) && (SelectedDrive.MediaType != FixedMedia) && (nWindowsBuildNumber < 15000)) {
 1333         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
 1334         return FALSE;
 1335     }
 1336 
 1337     if (!img_report.is_windows_img) {
 1338         mounted_iso = MountISO(image_path);
 1339         if (mounted_iso == NULL) {
 1340             uprintf("Could not mount ISO for Windows To Go installation");
 1341             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
 1342             return FALSE;
 1343         }
 1344         static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
 1345         uprintf("Mounted ISO as '%s'", mounted_iso);
 1346     }
 1347 
 1348     // Now we use the WIM API to apply that image
 1349     if (!WimApplyImage(img_report.is_windows_img ? image_path : mounted_image_path, wintogo_index, drive_name)) {
 1350         uprintf("Failed to apply Windows To Go image");
 1351         if (!IS_ERROR(FormatStatus))
 1352             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
 1353         if (!img_report.is_windows_img)
 1354             UnMountISO();
 1355         return FALSE;
 1356     }
 1357     if (!img_report.is_windows_img)
 1358         UnMountISO();
 1359 
 1360     if (use_esp) {
 1361         uprintf("Setting up EFI System Partition");
 1362         // According to Ubuntu (https://bugs.launchpad.net/ubuntu/+source/partman-efi/+bug/811485) you want to use FAT32.
 1363         // However, you have to be careful that the cluster size needs to be greater or equal to the sector size, which
 1364         // in turn has an impact on the minimum EFI partition size we can create (see ms_efi_size_MB in drive.c)
 1365         if (SelectedDrive.SectorSize <= 1024)
 1366             cluster_size = 1024;
 1367         else if (SelectedDrive.SectorSize <= 4096)
 1368             cluster_size = 4096;
 1369         else    // Go for broke
 1370             cluster_size = (ULONG)SelectedDrive.SectorSize;
 1371         // Boy do you *NOT* want to specify a label here, and spend HOURS figuring out why your EFI partition cannot boot...
 1372         // Also, we use the Large FAT32 facility Microsoft APIs are *UTTERLY USELESS* for achieving what we want:
 1373         // VDS cannot list ESP volumes (talk about allegedly improving on the old disk and volume APIs, only to
 1374         // completely neuter it) and IVdsDiskPartitionMF::FormatPartitionEx(), which is what you are supposed to
 1375         // use for ESPs, explicitly states: "This method cannot be used to format removable media."
 1376         if (!FormatPartition(DriveIndex, partition_offset[PI_ESP], cluster_size, FS_FAT32, "",
 1377             FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT)) {
 1378             uprintf("Could not format EFI System Partition");
 1379             return FALSE;
 1380         }
 1381         Sleep(200);
 1382         // Need to have the ESP mounted to invoke bcdboot
 1383         ms_efi = AltMountVolume(DriveIndex, partition_offset[PI_ESP], FALSE);
 1384         if (ms_efi == NULL) {
 1385             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER);
 1386             return FALSE;
 1387         }
 1388     }
 1389 
 1390     // We invoke the 'bcdboot' command from the host, as the one from the drive produces problems (#558)
 1391     // and of course, we couldn't invoke an ARM64 'bcdboot' binary on an x86 host anyway...
 1392     // Also, since Rufus should (usually) be running as a 32 bit app, on 64 bit systems, we need to use
 1393     // 'C:\Windows\Sysnative' and not 'C:\Windows\System32' to invoke bcdboot, as 'C:\Windows\System32'
 1394     // will get converted to 'C:\Windows\SysWOW64' behind the scenes, and there is no bcdboot.exe there.
 1395     uprintf("Enabling boot using command:");
 1396     static_sprintf(cmd, "%s\\bcdboot.exe %s\\Windows /v /f %s /s %s", sysnative_dir, drive_name,
 1397         HAS_BOOTMGR_BIOS(img_report) ? (HAS_BOOTMGR_EFI(img_report) ? "ALL" : "BIOS") : "UEFI",
 1398         (use_esp)?ms_efi:drive_name);
 1399     uprintf(cmd);
 1400     if (RunCommand(cmd, sysnative_dir, usb_debug) != 0) {
 1401         // Try to continue... but report a failure
 1402         uprintf("Failed to enable boot");
 1403         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
 1404     }
 1405 
 1406     UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files + 2 * wim_extra_files, wim_nb_files);
 1407 
 1408     uprintf("Disable the use of the Windows Recovery Environment using command:");
 1409     static_sprintf(cmd, "%s\\bcdedit.exe /store %s\\EFI\\Microsoft\\Boot\\BCD /set {default} recoveryenabled no",
 1410         sysnative_dir, (use_esp) ? ms_efi : drive_name);
 1411     uprintf(cmd);
 1412     RunCommand(cmd, sysnative_dir, usb_debug);
 1413 
 1414     UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_nb_files, wim_nb_files);
 1415 
 1416     if (use_esp) {
 1417         Sleep(200);
 1418         AltUnmountVolume(ms_efi, FALSE);
 1419     }
 1420 
 1421     return TRUE;
 1422 }
 1423 
 1424 static void update_progress(const uint64_t processed_bytes)
 1425 {
 1426     // NB: We don't really care about resetting this value to UINT64_MAX for a new pass.
 1427     static uint64_t last_value = UINT64_MAX;
 1428     uint64_t cur_value;
 1429 
 1430     UpdateProgressWithInfo(OP_FORMAT, MSG_261, processed_bytes, img_report.image_size);
 1431     cur_value = (processed_bytes * min(80, img_report.image_size)) / img_report.image_size;
 1432     if (cur_value != last_value) {
 1433         last_value = cur_value;
 1434         uprintfs("+");
 1435     }
 1436 }
 1437 
 1438 // Some compressed images use streams that aren't multiple of the sector
 1439 // size and cause write failures => Use a write override that alleviates
 1440 // the problem. See GitHub issue #1422 for details.
 1441 static int sector_write(int fd, const void* _buf, unsigned int count)
 1442 {
 1443     const uint8_t* buf = (const uint8_t*)_buf;
 1444     unsigned int sec_size = (unsigned int)SelectedDrive.SectorSize;
 1445     int written, fill_size = 0;
 1446 
 1447     if (sec_size == 0)
 1448         sec_size = 512;
 1449 
 1450     // If we are on a sector boundary and count is multiple of the
 1451     // sector size, just issue a regular write
 1452     if ((sec_buf_pos == 0) && (count % sec_size == 0))
 1453         return _write(fd, buf, count);
 1454 
 1455     // If we have an existing partial sector, fill and write it
 1456     if (sec_buf_pos > 0) {
 1457         fill_size = min(sec_size - sec_buf_pos, count);
 1458         memcpy(&sec_buf[sec_buf_pos], buf, fill_size);
 1459         sec_buf_pos += fill_size;
 1460         // If we don't have a full sector just buffer it for next call
 1461         if (sec_buf_pos < sec_size)
 1462             return (int)count;
 1463         sec_buf_pos = 0;
 1464         written = _write(fd, sec_buf, sec_size);
 1465         if (written != sec_size)
 1466             return written;
 1467     }
 1468 
 1469     // Now write as many full sectors as we can
 1470     uint32_t sec_num = (count - fill_size) / sec_size;
 1471     written = _write(fd, &buf[fill_size], sec_num * sec_size);
 1472     if (written < 0)
 1473         return written;
 1474     else if (written != sec_num * sec_size)
 1475         return fill_size + written;
 1476     sec_buf_pos = count - fill_size - written;
 1477     assert(sec_buf_pos < sec_size);
 1478 
 1479     // Keep leftover bytes, if any, in the sector buffer
 1480     if (sec_buf_pos != 0)
 1481         memcpy(sec_buf, &buf[fill_size + written], sec_buf_pos);
 1482     return (int)count;
 1483 }
 1484 
 1485 /* Write an image file or zero a drive */
 1486 static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage)
 1487 {
 1488     BOOL s, ret = FALSE;
 1489     LARGE_INTEGER li;
 1490     DWORD i, rSize, wSize, xSize, BufSize;
 1491     uint64_t wb, target_size = hSourceImage?img_report.image_size:SelectedDrive.DiskSize;
 1492     uint64_t cur_value, last_value = UINT64_MAX;
 1493     int64_t bled_ret;
 1494     uint8_t* buffer = NULL;
 1495     uint32_t zero_data, *cmp_buffer = NULL;
 1496     int throttle_fast_zeroing = 0;
 1497 
 1498     if (SelectedDrive.SectorSize < 512) {
 1499         uprintf("Unexpected sector size (%d) - Aborting", SelectedDrive.SectorSize);
 1500         return FALSE;
 1501     }
 1502 
 1503     // We poked the MBR and other stuff, so we need to rewind
 1504     li.QuadPart = 0;
 1505     if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
 1506         uprintf("Warning: Unable to rewind image position - wrong data might be copied!");
 1507     UpdateProgressWithInfoInit(NULL, FALSE);
 1508 
 1509     if (img_report.compression_type != BLED_COMPRESSION_NONE) {
 1510         uprintf("Writing compressed image:");
 1511         sec_buf = (uint8_t*)_mm_malloc(SelectedDrive.SectorSize, SelectedDrive.SectorSize);
 1512         if (sec_buf == NULL) {
 1513             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
 1514             uprintf("Could not allocate disk write buffer");
 1515             goto out;
 1516         }
 1517         assert((uintptr_t)sec_buf % SelectedDrive.SectorSize == 0);
 1518         sec_buf_pos = 0;
 1519         bled_init(_uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus);
 1520         bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type);
 1521         bled_exit();
 1522         if ((bled_ret >= 0) && (sec_buf_pos != 0)) {
 1523             // A disk image that doesn't end up on disk boundary should be a rare
 1524             // enough case, so we dont bother checking the write operation and
 1525             // just issue a notice about it in the log.
 1526             uprintf("Notice: Compressed image data didn't end on block boundary.");
 1527             // Gonna assert that WriteFile() and _write() share the same file offset
 1528             WriteFile(hPhysicalDrive, sec_buf, SelectedDrive.SectorSize, &wSize, NULL);
 1529         }
 1530         safe_mm_free(sec_buf);
 1531         if ((bled_ret < 0) && (SCODE_CODE(FormatStatus) != ERROR_CANCELLED)) {
 1532             // Unfortunately, different compression backends return different negative error codes
 1533             uprintf("Could not write compressed image: %lld", bled_ret);
 1534             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT;
 1535             goto out;
 1536         }
 1537     } else {
 1538         uprintf(hSourceImage?"Writing Image:":fast_zeroing?"Fast-zeroing drive:":"Zeroing drive:");
 1539         // Our buffer size must be a multiple of the sector size and *ALIGNED* to the sector size
 1540         BufSize = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize;
 1541         buffer = (uint8_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize);
 1542         if (buffer == NULL) {
 1543             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
 1544             uprintf("Could not allocate disk write buffer");
 1545             goto out;
 1546         }
 1547         assert((uintptr_t)buffer % SelectedDrive.SectorSize == 0);
 1548 
 1549         // Clear buffer
 1550         memset(buffer, fast_zeroing ? 0xff : 0x00, BufSize);
 1551 
 1552         if (fast_zeroing) {
 1553             cmp_buffer = (uint32_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize);
 1554             if (cmp_buffer == NULL) {
 1555                 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
 1556                 uprintf("Could not allocate disk comparison buffer");
 1557                 goto out;
 1558             }
 1559             assert((uintptr_t)cmp_buffer % SelectedDrive.SectorSize == 0);
 1560         }
 1561 
 1562         // Don't bother trying for something clever, using double buffering overlapped and whatnot:
 1563         // With Windows' default optimizations, sync read + sync write for sequential operations
 1564         // will be as fast, if not faster, than whatever async scheme you can come up with.
 1565         rSize = BufSize;
 1566         for (wb = 0, wSize = 0; wb < target_size; wb += wSize) {
 1567             UpdateProgressWithInfo(OP_FORMAT, hSourceImage ? MSG_261 : fast_zeroing ? MSG_306 : MSG_286, wb, target_size);
 1568             cur_value = (wb * min(80, target_size)) / target_size;
 1569             if (cur_value != last_value) {
 1570                 last_value = cur_value;
 1571                 uprintfs("+");
 1572             }
 1573             if (hSourceImage != NULL) {
 1574                 s = ReadFile(hSourceImage, buffer, BufSize, &rSize, NULL);
 1575                 if (!s) {
 1576                     FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT;
 1577                     uprintf("Read error: %s", WindowsErrorString());
 1578                     goto out;
 1579                 }
 1580                 if (rSize == 0)
 1581                     break;
 1582             }
 1583             // Don't overflow our projected size (mostly for VHDs)
 1584             if (wb + rSize > target_size) {
 1585                 rSize = (DWORD)(target_size - wb);
 1586             }
 1587 
 1588             // WriteFile fails unless the size is a multiple of sector size
 1589             if (rSize % SelectedDrive.SectorSize != 0)
 1590                 rSize = ((rSize + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize;
 1591 
 1592             // Fast-zeroing: Depending on your hardware, reading from flash may be much faster than writing, so
 1593             // we might speed things up by skipping empty blocks, or skipping the write if the data is the same.
 1594             // Notes: A block is declared empty when all bits are either 0 (zeros) or 1 (flash block erased).
 1595             // Also, a back-off strategy is used to limit reading.
 1596             if (throttle_fast_zeroing) {
 1597                 throttle_fast_zeroing--;
 1598             } else if (fast_zeroing) {
 1599                 assert(hSourceImage == NULL);   // Only enabled for zeroing
 1600                 CHECK_FOR_USER_CANCEL;
 1601 
 1602                 // Read block and compare against the block that needs to be written
 1603                 s = ReadFile(hPhysicalDrive, cmp_buffer, rSize, &xSize, NULL);
 1604                 if ((!s) || (xSize != rSize) ) {
 1605                     uprintf("Read error: Could not read data for fast zeroing comparison - %s", WindowsErrorString());
 1606                     goto out;
 1607                 }
 1608 
 1609                 // Check for an empty block by comparing with the first element
 1610                 zero_data = cmp_buffer[0];
 1611                 // Check all bits are the same
 1612                 if ((zero_data == 0) || (zero_data == 0xffffffff)) {
 1613                     // Compare the rest of the block against the first element
 1614                     for (i = 1; (i < rSize / sizeof(uint32_t)) && (cmp_buffer[i] == zero_data); i++);
 1615                     if (i >= rSize / sizeof(uint32_t)) {
 1616                         // Block is empty, skip write
 1617                         wSize = rSize;
 1618                         continue;
 1619                     }
 1620                 }
 1621 
 1622                 // Move the file pointer position back for writing
 1623                 li.QuadPart = wb;
 1624                 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) {
 1625                     uprintf("Error: Could not reset position - %s", WindowsErrorString());
 1626                     goto out;
 1627                 }
 1628                 // Throttle read operations
 1629                 throttle_fast_zeroing = 15;
 1630             }
 1631 
 1632             for (i = 1; i <= WRITE_RETRIES; i++) {
 1633                 CHECK_FOR_USER_CANCEL;
 1634                 s = WriteFile(hPhysicalDrive, buffer, rSize, &wSize, NULL);
 1635                 if ((s) && (wSize == rSize))
 1636                     break;
 1637                 if (s)
 1638                     uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize);
 1639                 else
 1640                     uprintf("Write error at sector %lld: %s", wb / SelectedDrive.SectorSize, WindowsErrorString());
 1641                 if (i < WRITE_RETRIES) {
 1642                     li.QuadPart = wb;
 1643                     uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
 1644                     Sleep(WRITE_TIMEOUT);
 1645                     if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) {
 1646                         uprintf("Write error: Could not reset position - %s", WindowsErrorString());
 1647                         goto out;
 1648                     }
 1649                 } else {
 1650                     FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT;
 1651                     goto out;
 1652                 }
 1653                 Sleep(200);
 1654             }
 1655             if (i > WRITE_RETRIES)
 1656                 goto out;
 1657         }
 1658     }
 1659     RefreshDriveLayout(hPhysicalDrive);
 1660     ret = TRUE;
 1661 out:
 1662     safe_mm_free(buffer);
 1663     safe_mm_free(cmp_buffer);
 1664     return ret;
 1665 }
 1666 
 1667 /*
 1668  * Standalone thread for the formatting operation
 1669  * According to http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562.aspx
 1670  * To change a volume file system
 1671  *   Open a volume.
 1672  *   Lock the volume.
 1673  *   Format the volume.
 1674  *   Dismount the volume.
 1675  *   Unlock the volume.
 1676  *   Close the volume handle.
 1677  */
 1678 DWORD WINAPI FormatThread(void* param)
 1679 {
 1680     int r;
 1681     BOOL ret, use_large_fat32, windows_to_go, actual_lock_drive = lock_drive;
 1682     BOOL need_logical = FALSE;
 1683     DWORD cr, DriveIndex = (DWORD)(uintptr_t)param, ClusterSize, Flags;
 1684     HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE;
 1685     HANDLE hLogicalVolume = INVALID_HANDLE_VALUE;
 1686     HANDLE hSourceImage = INVALID_HANDLE_VALUE;
 1687     SYSTEMTIME lt;
 1688     FILE* log_fd;
 1689     uint8_t *buffer = NULL, extra_partitions = 0;
 1690     char *bb_msg, *volume_name = NULL;
 1691     char drive_name[] = "?:\\";
 1692     char drive_letters[27], fs_name[32], label[64];
 1693     char logfile[MAX_PATH], *userdir;
 1694     char efi_dst[] = "?:\\efi\\boot\\bootx64.efi";
 1695     char kolibri_dst[] = "?:\\MTLD_F32";
 1696     char grub4dos_dst[] = "?:\\grldr";
 1697 
 1698     use_large_fat32 = (fs_type == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32));
 1699     windows_to_go = (image_options & IMOP_WINTOGO) && (boot_type == BT_IMAGE) && HAS_WINTOGO(img_report) &&
 1700         ComboBox_GetCurItemData(hImageOption);
 1701     large_drive = (SelectedDrive.DiskSize > (1*TB));
 1702     if (large_drive)
 1703         uprintf("Notice: Large drive detected (may produce short writes)");
 1704     // Find out if we need to add any extra partitions
 1705     if ((windows_to_go) && (target_type == TT_UEFI) && (partition_type == PARTITION_STYLE_GPT))
 1706         // According to Microsoft, every GPT disk (we RUN Windows from) must have an MSR due to not having hidden sectors
 1707         // http://msdn.microsoft.com/en-us/library/windows/hardware/dn640535.aspx#gpt_faq_what_disk_require_msr
 1708         extra_partitions = XP_ESP | XP_MSR;
 1709     else if ( ((fs_type == FS_NTFS) || (fs_type == FS_EXFAT)) &&
 1710               ((boot_type == BT_UEFI_NTFS) || ((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report) &&
 1711                ((target_type == TT_UEFI) || (windows_to_go) || (allow_dual_uefi_bios)))) )
 1712         extra_partitions = XP_UEFI_NTFS;
 1713     else if ((boot_type == BT_IMAGE) && !write_as_image && HAS_PERSISTENCE(img_report) && persistence_size)
 1714         extra_partitions = XP_CASPER;
 1715     else if (IsChecked(IDC_OLD_BIOS_FIXES))
 1716         extra_partitions = XP_COMPAT;
 1717     // On pre 1703 platforms (and even on later ones), anything with ext2/ext3 doesn't sit
 1718     // too well with Windows. Same with ESPs. Relaxing our locking rules seems to help...
 1719     if ((extra_partitions & (XP_ESP | XP_CASPER)) || (fs_type >= FS_EXT2))
 1720         actual_lock_drive = FALSE;
 1721 
 1722     PrintInfoDebug(0, MSG_225);
 1723     hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, FALSE, !actual_lock_drive);
 1724     if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
 1725         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
 1726         goto out;
 1727     }
 1728 
 1729     // At this stage we have both a handle and a lock to the physical drive
 1730     if (!GetDriveLetters(DriveIndex, drive_letters)) {
 1731         uprintf("Failed to get a drive letter");
 1732         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER);
 1733         goto out;
 1734     }
 1735 
 1736     // Unassign all drives letters
 1737     drive_name[0] = RemoveDriveLetters(DriveIndex, TRUE, FALSE);
 1738     if (drive_name[0] == 0) {
 1739         uprintf("Unable to find a drive letter to use");
 1740         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER);
 1741         goto out;
 1742     }
 1743     uprintf("Will use '%C:' as volume mountpoint", drive_name[0]);
 1744 
 1745     // It kind of blows, but we have to relinquish access to the physical drive
 1746     // for VDS to be able to delete the partitions that reside on it...
 1747     safe_unlockclose(hPhysicalDrive);
 1748     PrintInfo(0, MSG_239, lmprintf(MSG_307));
 1749     if (!DeletePartition(DriveIndex, 0, FALSE)) {
 1750         SetLastError(FormatStatus);
 1751         FormatStatus = 0;
 1752         // If we couldn't delete partitions, Windows give us trouble unless we
 1753         // request access to the logical drive. Don't ask me why!
 1754         need_logical = TRUE;
 1755     }
 1756 
 1757     // An extra refresh of the (now empty) partition data here appears to be helpful
 1758     GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), TRUE);
 1759 
 1760     // Now get RW access to the physical drive
 1761     hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, TRUE, !actual_lock_drive);
 1762     if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
 1763         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
 1764         goto out;
 1765     }
 1766     RefreshDriveLayout(hPhysicalDrive);
 1767 
 1768     // If we write an image that contains an ESP, Windows forcibly reassigns/removes the target
 1769     // drive, which causes a write error. To work around this, we must lock the logical drive.
 1770     // Also need to lock logical drive if we couldn't delete partitions, to keep Windows happy...
 1771     if (((boot_type == BT_IMAGE) && write_as_image) || (need_logical)) {
 1772         uprintf("Requesting logical volume handle...");
 1773         hLogicalVolume = GetLogicalHandle(DriveIndex, 0, TRUE, FALSE, !actual_lock_drive);
 1774         if (hLogicalVolume == INVALID_HANDLE_VALUE) {
 1775             uprintf("Could not access logical volume");
 1776             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
 1777             goto out;
 1778         // If the call succeeds (and we don't get a NULL logical handle as returned for
 1779         // unpartitioned drives), try to unmount the volume.
 1780         } else if ((hLogicalVolume == NULL) && (!UnmountVolume(hLogicalVolume))) {
 1781             uprintf("Trying to continue regardless...");
 1782         }
 1783     }
 1784     CHECK_FOR_USER_CANCEL;
 1785 
 1786     if (!zero_drive && !write_as_image) {
 1787         PrintInfoDebug(0, MSG_226);
 1788         AnalyzeMBR(hPhysicalDrive, "Drive", FALSE);
 1789         UpdateProgress(OP_ANALYZE_MBR, -1.0f);
 1790     }
 1791 
 1792     if (zero_drive) {
 1793         WriteDrive(hPhysicalDrive, NULL);
 1794         goto out;
 1795     }
 1796 
 1797     // Zap partition records. This helps prevent access errors.
 1798     // Note, Microsoft's way of cleaning partitions (IOCTL_DISK_CREATE_DISK, which is what we apply
 1799     // in InitializeDisk) is *NOT ENOUGH* to reset a disk and can render it inoperable for partitioning
 1800     // or formatting under Windows. See https://github.com/pbatard/rufus/issues/759 for details.
 1801     if ((boot_type != BT_IMAGE) || (img_report.is_iso && !write_as_image)) {
 1802         if ((!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, use_large_fat32)) ||
 1803             (!InitializeDisk(hPhysicalDrive))) {
 1804             uprintf("Could not reset partitions");
 1805             FormatStatus = (LastWriteError != 0) ? LastWriteError : (ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE);
 1806             goto out;
 1807         }
 1808     }
 1809 
 1810     if (IsChecked(IDC_BAD_BLOCKS)) {
 1811         do {
 1812             int sel = ComboBox_GetCurSel(hNBPasses);
 1813             // create a log file for bad blocks report. Since %USERPROFILE% may
 1814             // have localized characters, we use the UTF-8 API.
 1815             userdir = getenvU("USERPROFILE");
 1816             static_strcpy(logfile, userdir);
 1817             safe_free(userdir);
 1818             GetLocalTime(&lt);
 1819             safe_sprintf(&logfile[strlen(logfile)], sizeof(logfile)-strlen(logfile)-1,
 1820                 "\\rufus_%04d%02d%02d_%02d%02d%02d.log",
 1821                 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
 1822             log_fd = fopenU(logfile, "w+");
 1823             if (log_fd == NULL) {
 1824                 uprintf("Could not create log file for bad blocks check");
 1825             } else {
 1826                 fprintf(log_fd, APPLICATION_NAME " bad blocks check started on: %04d.%02d.%02d %02d:%02d:%02d",
 1827                 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
 1828                 fflush(log_fd);
 1829             }
 1830 
 1831             if (!BadBlocks(hPhysicalDrive, SelectedDrive.DiskSize, (sel >= 2) ? 4 : sel +1, sel, &report, log_fd)) {
 1832                 uprintf("Bad blocks: Check failed.");
 1833                 if (!IS_ERROR(FormatStatus))
 1834                     FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_BADBLOCKS_FAILURE);
 1835                 ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, FALSE);
 1836                 fclose(log_fd);
 1837                 DeleteFileU(logfile);
 1838                 goto out;
 1839             }
 1840             uprintf("Bad Blocks: Check completed, %d bad block%s found. (%d/%d/%d errors)",
 1841                 report.bb_count, (report.bb_count==1)?"":"s",
 1842                 report.num_read_errors, report.num_write_errors, report.num_corruption_errors);
 1843             r = IDOK;
 1844             if (report.bb_count) {
 1845                 bb_msg = lmprintf(MSG_011, report.bb_count, report.num_read_errors, report.num_write_errors,
 1846                     report.num_corruption_errors);
 1847                 fprintf(log_fd, "%s", bb_msg);
 1848                 GetLocalTime(&lt);
 1849                 fprintf(log_fd, APPLICATION_NAME " bad blocks check ended on: %04d.%02d.%02d %02d:%02d:%02d",
 1850                 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
 1851                 fclose(log_fd);
 1852                 r = MessageBoxExU(hMainDialog, lmprintf(MSG_012, bb_msg, logfile),
 1853                     lmprintf(MSG_010), MB_ABORTRETRYIGNORE|MB_ICONWARNING|MB_IS_RTL, selected_langid);
 1854             } else {
 1855                 // We didn't get any errors => delete the log file
 1856                 fclose(log_fd);
 1857                 DeleteFileU(logfile);
 1858             }
 1859         } while (r == IDRETRY);
 1860         if (r == IDABORT) {
 1861             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED;
 1862             goto out;
 1863         }
 1864 
 1865         // Especially after destructive badblocks test, you must zero the MBR/GPT completely
 1866         // before repartitioning. Else, all kind of bad things happen.
 1867         if (!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, use_large_fat32)) {
 1868             uprintf("unable to zero MBR/GPT");
 1869             if (!IS_ERROR(FormatStatus))
 1870                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 1871             goto out;
 1872         }
 1873     }
 1874 
 1875     // Write an image file
 1876     if ((boot_type == BT_IMAGE) && write_as_image) {
 1877         hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL,
 1878             OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
 1879         if (hSourceImage == INVALID_HANDLE_VALUE) {
 1880             uprintf("Could not open image '%s': %s", image_path, WindowsErrorString());
 1881             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
 1882             goto out;
 1883         }
 1884 
 1885         WriteDrive(hPhysicalDrive, hSourceImage);
 1886 
 1887         // If the image contains a partition we might be able to access, try to re-mount it
 1888         safe_unlockclose(hPhysicalDrive);
 1889         safe_unlockclose(hLogicalVolume);
 1890         Sleep(200);
 1891         WaitForLogical(DriveIndex, 0);
 1892         if (GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), TRUE)) {
 1893             volume_name = GetLogicalName(DriveIndex, 0, TRUE, TRUE);
 1894             if ((volume_name != NULL) && (MountVolume(drive_name, volume_name)))
 1895                 uprintf("Remounted %s as %C:", volume_name, drive_name[0]);
 1896         }
 1897         goto out;
 1898     }
 1899 
 1900     UpdateProgress(OP_ZERO_MBR, -1.0f);
 1901     CHECK_FOR_USER_CANCEL;
 1902 
 1903     if (!CreatePartition(hPhysicalDrive, partition_type, fs_type, (partition_type == PARTITION_STYLE_MBR)
 1904         && (target_type == TT_UEFI), extra_partitions)) {
 1905         FormatStatus = (LastWriteError != 0) ? LastWriteError : (ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE);
 1906         goto out;
 1907     }
 1908     UpdateProgress(OP_PARTITION, -1.0f);
 1909 
 1910     // Close the (unmounted) volume before formatting
 1911     if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) {
 1912         PrintInfoDebug(0, MSG_227);
 1913         if (!CloseHandle(hLogicalVolume)) {
 1914             uprintf("Could not close volume: %s", WindowsErrorString());
 1915             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
 1916             goto out;
 1917         }
 1918     }
 1919     hLogicalVolume = INVALID_HANDLE_VALUE;
 1920 
 1921     // VDS wants us to unlock the phys
 1922     if (use_vds) {
 1923         safe_unlockclose(hPhysicalDrive);
 1924         uprintf("Refreshing drive layout...");
 1925         // Note: This may leave the device disabled on re-plug or reboot
 1926         // so only do this for the experimental VDS path for now...
 1927         cr = CycleDevice(ComboBox_GetCurSel(hDeviceList));
 1928         if (cr == ERROR_DEVICE_REINITIALIZATION_NEEDED) {
 1929             uprintf("Zombie device detected, trying again...");
 1930             Sleep(1000);
 1931             cr = CycleDevice(ComboBox_GetCurSel(hDeviceList));
 1932         }
 1933         if (cr == 0)
 1934             uprintf("Successfully cycled device");
 1935         else
 1936             uprintf("Cycling device failed!");
 1937         RefreshLayout(DriveIndex);
 1938     }
 1939 
 1940     // Wait for the logical drive we just created to appear
 1941     uprintf("Waiting for logical drive to reappear...");
 1942     Sleep(200);
 1943     if (!WaitForLogical(DriveIndex, partition_offset[PI_MAIN])) {
 1944         uprintf("Logical drive was not found - aborting");
 1945         if (!IS_ERROR(FormatStatus))
 1946             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_TIMEOUT;
 1947         goto out;
 1948     }
 1949     CHECK_FOR_USER_CANCEL;
 1950 
 1951     // Format Casper partition if required. Do it before we format anything with
 1952     // a file system that Windows will recognize, to avoid concurrent access.
 1953     if (extra_partitions & XP_CASPER) {
 1954         uint32_t ext_version = ReadSetting32(SETTING_USE_EXT_VERSION);
 1955         if ((ext_version < 2) || (ext_version > 4))
 1956             ext_version = 3;
 1957         uprintf("Using %s-like method to enable persistence", img_report.uses_casper ? "Ubuntu" : "Debian");
 1958         if (!FormatPartition(DriveIndex, partition_offset[PI_CASPER], 0, FS_EXT2 + (ext_version - 2),
 1959             img_report.uses_casper ? "casper-rw" : "persistence",
 1960             (img_report.uses_casper ? 0 : FP_CREATE_PERSISTENCE_CONF) |
 1961             (IsChecked(IDC_QUICK_FORMAT) ? FP_QUICK : 0))) {
 1962             if (!IS_ERROR(FormatStatus))
 1963                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 1964             goto out;
 1965         }
 1966     }
 1967 
 1968     GetWindowTextU(hLabel, label, sizeof(label));
 1969     if (fs_type < FS_EXT2)
 1970         ToValidLabel(label, (fs_type == FS_FAT16) || (fs_type == FS_FAT32) || (fs_type == FS_EXFAT));
 1971     ClusterSize = (DWORD)ComboBox_GetCurItemData(hClusterSize);
 1972     if ((ClusterSize < 0x200) || (write_as_esp))
 1973         ClusterSize = 0;    // 0 = default cluster size
 1974 
 1975     Flags = FP_FORCE;
 1976     if (IsChecked(IDC_QUICK_FORMAT))
 1977         Flags |= FP_QUICK;
 1978     if ((fs_type == FS_NTFS) && (enable_ntfs_compression))
 1979         Flags |= FP_COMPRESSION;
 1980 
 1981     ret = FormatPartition(DriveIndex, partition_offset[PI_MAIN], ClusterSize, fs_type, label, Flags);
 1982     if (!ret) {
 1983         // Error will be set by FormatPartition() in FormatStatus
 1984         uprintf("Format error: %s", StrError(FormatStatus, TRUE));
 1985         goto out;
 1986     }
 1987 
 1988     if (use_vds) {
 1989         // Get RW access back to the physical drive...
 1990         hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, TRUE, !actual_lock_drive);
 1991         if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
 1992             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
 1993             goto out;
 1994         }
 1995     }
 1996 
 1997     // Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted
 1998     if ((partition_type == PARTITION_STYLE_MBR) || ((boot_type != BT_NON_BOOTABLE) && (partition_type == PARTITION_STYLE_GPT))) {
 1999         PrintInfoDebug(0, MSG_228); // "Writing master boot record..."
 2000         if ((!WriteMBR(hPhysicalDrive)) || (!WriteSBR(hPhysicalDrive))) {
 2001             if (!IS_ERROR(FormatStatus))
 2002                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 2003             goto out;
 2004         }
 2005         UpdateProgress(OP_FIX_MBR, -1.0f);
 2006     }
 2007     Sleep(200);
 2008     WaitForLogical(DriveIndex, 0);
 2009     // Try to continue
 2010     CHECK_FOR_USER_CANCEL;
 2011 
 2012     volume_name = GetLogicalName(DriveIndex, partition_offset[PI_MAIN], TRUE, TRUE);
 2013     if (volume_name == NULL) {
 2014         uprintf("Could not get volume name");
 2015         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_VOLUME_ID;
 2016         goto out;
 2017     }
 2018     uprintf("Found volume %s", volume_name);
 2019 
 2020     // Windows is really finicky with regards to reassigning drive letters even after
 2021     // we forcibly removed them, so add yet another explicit call to RemoveDriveLetters()
 2022     RemoveDriveLetters(DriveIndex, FALSE, TRUE);
 2023     if (!MountVolume(drive_name, volume_name)) {
 2024         uprintf("Could not remount %s as %C: %s\n", volume_name, drive_name[0], WindowsErrorString());
 2025         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_MOUNT_VOLUME);
 2026         goto out;
 2027     }
 2028     CHECK_FOR_USER_CANCEL;
 2029 
 2030     // Disable file indexing, unless it was force-enabled by the user
 2031     if ((!enable_file_indexing) && ((fs_type == FS_NTFS) || (fs_type == FS_UDF) || (fs_type == FS_REFS))) {
 2032         uprintf("Disabling file indexing...");
 2033         if (!SetFileAttributesA(volume_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
 2034             uprintf("Could not disable file indexing: %s", WindowsErrorString());
 2035     }
 2036 
 2037     // Refresh the drive label - This is needed as Windows may have altered it from
 2038     // the name we proposed, and we require an exact label, to patch config files.
 2039     if ((fs_type < FS_EXT2) && !GetVolumeInformationU(drive_name, img_report.usb_label,
 2040         ARRAYSIZE(img_report.usb_label), NULL, NULL, NULL, NULL, 0)) {
 2041         uprintf("Warning: Failed to refresh label: %s", WindowsErrorString());
 2042     } else if ((fs_type >= FS_EXT2) && (fs_type <= FS_EXT4)) {
 2043         const char* ext_label = GetExtFsLabel(DriveIndex, 0);
 2044         if (ext_label != NULL)
 2045             static_strcpy(img_report.usb_label, label);
 2046     }
 2047 
 2048     if (boot_type != BT_NON_BOOTABLE) {
 2049         if (boot_type == BT_UEFI_NTFS) {
 2050             // All good
 2051         } else if (target_type == TT_UEFI) {
 2052             // For once, no need to do anything - just check our sanity
 2053             assert((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report) && (fs_type <= FS_NTFS));
 2054             if ( (boot_type != BT_IMAGE) || !IS_EFI_BOOTABLE(img_report) || (fs_type > FS_NTFS) ) {
 2055                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE;
 2056                 goto out;
 2057             }
 2058         } else if ( (boot_type == BT_SYSLINUX_V4) || (boot_type == BT_SYSLINUX_V6) ||
 2059             ((boot_type == BT_IMAGE) && (HAS_SYSLINUX(img_report) || HAS_REACTOS(img_report)) &&
 2060                 (!HAS_WINDOWS(img_report) || !allow_dual_uefi_bios)) ) {
 2061             if (!InstallSyslinux(DriveIndex, drive_name[0], fs_type)) {
 2062                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE;
 2063                 goto out;
 2064             }
 2065         } else {
 2066             // We still have a lock, which we need to modify the volume boot record
 2067             // => no need to reacquire the lock...
 2068             hLogicalVolume = GetLogicalHandle(DriveIndex, partition_offset[PI_MAIN], FALSE, TRUE, FALSE);
 2069             if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) {
 2070                 uprintf("Could not re-mount volume for partition boot record access");
 2071                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
 2072                 goto out;
 2073             }
 2074             // NB: if you unmount the logical volume here, XP will report error:
 2075             // [0x00000456] The media in the drive may have changed
 2076             PrintInfoDebug(0, MSG_229);
 2077             if (!WritePBR(hLogicalVolume)) {
 2078                 if (!IS_ERROR(FormatStatus))
 2079                     FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 2080                 goto out;
 2081             }
 2082             // We must close and unlock the volume to write files to it
 2083             safe_unlockclose(hLogicalVolume);
 2084         }
 2085     } else {
 2086         if (IsChecked(IDC_EXTENDED_LABEL))
 2087             SetAutorun(drive_name);
 2088     }
 2089     CHECK_FOR_USER_CANCEL;
 2090 
 2091     // We issue a complete remount of the filesystem on account of:
 2092     // - Ensuring the file explorer properly detects that the volume was updated
 2093     // - Ensuring that an NTFS system will be reparsed so that it becomes bootable
 2094     if (!RemountVolume(drive_name, FALSE))
 2095         goto out;
 2096     CHECK_FOR_USER_CANCEL;
 2097 
 2098     if (boot_type != BT_NON_BOOTABLE) {
 2099         if ((boot_type == BT_MSDOS) || (boot_type == BT_FREEDOS)) {
 2100             UpdateProgress(OP_FILE_COPY, -1.0f);
 2101             PrintInfoDebug(0, MSG_230);
 2102             if (!ExtractDOS(drive_name)) {
 2103                 if (!IS_ERROR(FormatStatus))
 2104                     FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
 2105                 goto out;
 2106             }
 2107         } else if (boot_type == BT_GRUB4DOS) {
 2108             grub4dos_dst[0] = drive_name[0];
 2109             IGNORE_RETVAL(_chdirU(app_dir));
 2110             uprintf("Installing: %s (Grub4DOS loader) %s", grub4dos_dst,
 2111                 IsFileInDB(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr")?"✓":"✗");
 2112             if (!CopyFileU(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr", grub4dos_dst, FALSE))
 2113                 uprintf("Failed to copy file: %s", WindowsErrorString());
 2114         } else if ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso || img_report.is_windows_img)) {
 2115             UpdateProgress(OP_FILE_COPY, 0.0f);
 2116             drive_name[2] = 0;  // Ensure our drive is something like 'D:'
 2117             if (windows_to_go) {
 2118                 PrintInfoDebug(0, MSG_268);
 2119                 if (!SetupWinToGo(DriveIndex, drive_name, (extra_partitions & XP_ESP))) {
 2120                     if (!IS_ERROR(FormatStatus))
 2121                         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
 2122                     goto out;
 2123                 }
 2124             } else {
 2125                 assert(!img_report.is_windows_img);
 2126                 if (!ExtractISO(image_path, drive_name, FALSE)) {
 2127                     if (!IS_ERROR(FormatStatus))
 2128                         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
 2129                     goto out;
 2130                 }
 2131                 if (HAS_KOLIBRIOS(img_report)) {
 2132                     kolibri_dst[0] = drive_name[0];
 2133                     uprintf("Installing: %s (KolibriOS loader)", kolibri_dst);
 2134                     if (ExtractISOFile(image_path, "HD_Load/USB_Boot/MTLD_F32", kolibri_dst,
 2135                         FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM) == 0) {
 2136                         uprintf("Warning: loader installation failed - KolibriOS will not boot!");
 2137                     }
 2138                 }
 2139                 // EFI mode selected, with no 'boot###.efi' but Windows 7 x64's 'bootmgr.efi' (bit #0)
 2140                 if (((target_type == TT_UEFI) || allow_dual_uefi_bios) && HAS_WIN7_EFI(img_report)) {
 2141                     PrintInfo(0, MSG_232, lmprintf(MSG_307));
 2142                     uprintf("Win7 EFI boot setup");
 2143                     img_report.wininst_path[0][0] = drive_name[0];
 2144                     efi_dst[0] = drive_name[0];
 2145                     efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = 0;
 2146                     if (!CreateDirectoryA(efi_dst, 0)) {
 2147                         uprintf("Could not create directory '%s': %s", efi_dst, WindowsErrorString());
 2148                         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
 2149                     } else {
 2150                         efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = '\\';
 2151                         if (!WimExtractFile(img_report.wininst_path[0], 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst, FALSE)) {
 2152                             uprintf("Failed to setup Win7 EFI boot");
 2153                             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
 2154                         }
 2155                     }
 2156                 }
 2157                 if ( (target_type == TT_BIOS) && HAS_WINPE(img_report) ) {
 2158                     // Apply WinPe fixup
 2159                     if (!SetupWinPE(drive_name[0]))
 2160                         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
 2161                 }
 2162             }
 2163         }
 2164         UpdateProgress(OP_FINALIZE, -1.0f);
 2165         PrintInfoDebug(0, MSG_233);
 2166         if (IsChecked(IDC_EXTENDED_LABEL))
 2167             SetAutorun(drive_name);
 2168         // Issue another complete remount before we exit, to ensure we're clean
 2169         RemountVolume(drive_name, TRUE);
 2170         // NTFS fixup (WinPE/AIK images don't seem to boot without an extra checkdisk)
 2171         if ((boot_type == BT_IMAGE) && (img_report.is_iso) && (fs_type == FS_NTFS)) {
 2172             // Try to ensure that all messages from Checkdisk will be in English
 2173             if (PRIMARYLANGID(GetThreadUILanguage()) != LANG_ENGLISH) {
 2174                 SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
 2175                 if (PRIMARYLANGID(GetThreadUILanguage()) != LANG_ENGLISH)
 2176                     uprintf("Note: CheckDisk messages may be localized");
 2177             }
 2178             CheckDisk(drive_name[0]);
 2179             UpdateProgress(OP_FINALIZE, -1.0f);
 2180         }
 2181     }
 2182 
 2183 out:
 2184     safe_free(volume_name);
 2185     safe_free(buffer);
 2186     safe_closehandle(hSourceImage);
 2187     safe_unlockclose(hLogicalVolume);
 2188     safe_unlockclose(hPhysicalDrive);   // This can take a while
 2189     if (IS_ERROR(FormatStatus)) {
 2190         volume_name = GetLogicalName(DriveIndex, partition_offset[PI_MAIN], TRUE, TRUE);
 2191         if (volume_name != NULL) {
 2192             if (MountVolume(drive_name, volume_name))
 2193                 uprintf("Re-mounted volume as %C: after error", drive_name[0]);
 2194             free(volume_name);
 2195         }
 2196     }
 2197     PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
 2198     ExitThread(0);
 2199 }
 2200 
 2201 DWORD WINAPI SaveImageThread(void* param)
 2202 {
 2203     BOOL s;
 2204     DWORD rSize, wSize;
 2205     IMG_SAVE *img_save = (IMG_SAVE*)param;
 2206     HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE;
 2207     HANDLE hDestImage = INVALID_HANDLE_VALUE;
 2208     LARGE_INTEGER li;
 2209     uint8_t *buffer = NULL;
 2210     uint64_t wb;
 2211     int i;
 2212 
 2213     PrintInfoDebug(0, MSG_225);
 2214     switch (img_save->Type) {
 2215     case IMG_SAVE_TYPE_VHD:
 2216         hPhysicalDrive = GetPhysicalHandle(img_save->DeviceNum, TRUE, FALSE, FALSE);
 2217         break;
 2218     case IMG_SAVE_TYPE_ISO:
 2219         hPhysicalDrive = CreateFileA(img_save->DevicePath, GENERIC_READ, FILE_SHARE_READ,
 2220             NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
 2221         break;
 2222     default:
 2223         uprintf("Invalid image type");
 2224     }
 2225     if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
 2226         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
 2227         goto out;
 2228     }
 2229 
 2230     // Write an image file
 2231     // We may have poked the MBR and other stuff, so need to rewind
 2232     li.QuadPart = 0;
 2233     if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
 2234         uprintf("Warning: Unable to rewind device position - wrong data might be copied!");
 2235     hDestImage = CreateFileU(img_save->ImagePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
 2236         CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 2237     if (hDestImage == INVALID_HANDLE_VALUE) {
 2238         uprintf("Could not open image '%s': %s", img_save->ImagePath, WindowsErrorString());
 2239         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
 2240         goto out;
 2241     }
 2242 
 2243     buffer = (uint8_t*)_mm_malloc(img_save->BufSize, 16);
 2244     if (buffer == NULL) {
 2245         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
 2246         uprintf("could not allocate buffer");
 2247         goto out;
 2248     }
 2249 
 2250     uprintf("Will use a buffer size of %s", SizeToHumanReadable(img_save->BufSize, FALSE, FALSE));
 2251     uprintf("Saving to image '%s'...", img_save->ImagePath);
 2252 
 2253     // Don't bother trying for something clever, using double buffering overlapped and whatnot:
 2254     // With Windows' default optimizations, sync read + sync write for sequential operations
 2255     // will be as fast, if not faster, than whatever async scheme you can come up with.
 2256     UpdateProgressWithInfoInit(NULL, FALSE);
 2257     for (wb = 0; ; wb += wSize) {
 2258         if (img_save->Type == IMG_SAVE_TYPE_ISO) {
 2259             // Optical drives do not appear to increment the sectors to read automatically
 2260             li.QuadPart = wb;
 2261             if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
 2262                 uprintf("Warning: Unable to set device position - wrong data might be copied!");
 2263         }
 2264         s = ReadFile(hPhysicalDrive, buffer,
 2265             (DWORD)MIN(img_save->BufSize, img_save->DeviceSize - wb), &rSize, NULL);
 2266         if (!s) {
 2267             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
 2268             uprintf("Read error: %s", WindowsErrorString());
 2269             goto out;
 2270         }
 2271         if (rSize == 0)
 2272             break;
 2273         UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, img_save->DeviceSize);
 2274         for (i = 1; i <= WRITE_RETRIES; i++) {
 2275             CHECK_FOR_USER_CANCEL;
 2276             s = WriteFile(hDestImage, buffer, rSize, &wSize, NULL);
 2277             if ((s) && (wSize == rSize))
 2278                 break;
 2279             if (s)
 2280                 uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize);
 2281             else
 2282                 uprintf("Write error: %s", WindowsErrorString());
 2283             if (i < WRITE_RETRIES) {
 2284                 li.QuadPart = wb;
 2285                 uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
 2286                 Sleep(WRITE_TIMEOUT);
 2287                 if (!SetFilePointerEx(hDestImage, li, NULL, FILE_BEGIN)) {
 2288                     uprintf("Write error: Could not reset position - %s", WindowsErrorString());
 2289                     goto out;
 2290                 }
 2291             } else {
 2292                 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 2293                 goto out;
 2294             }
 2295             Sleep(200);
 2296         }
 2297         if (i > WRITE_RETRIES)
 2298             goto out;
 2299     }
 2300     if (wb != img_save->DeviceSize) {
 2301         uprintf("Error: wrote %s, expected %s", SizeToHumanReadable(wb, FALSE, FALSE),
 2302             SizeToHumanReadable(img_save->DeviceSize, FALSE, FALSE));
 2303         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 2304         goto out;
 2305     }
 2306     if (img_save->Type == IMG_SAVE_TYPE_VHD) {
 2307         uprintf("Appending VHD footer...");
 2308         if (!AppendVHDFooter(img_save->ImagePath)) {
 2309             FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
 2310             goto out;
 2311         }
 2312     }
 2313     uprintf("Operation complete (Wrote %s).", SizeToHumanReadable(wb, FALSE, FALSE));
 2314 
 2315 out:
 2316     safe_free(img_save->ImagePath);
 2317     safe_mm_free(buffer);
 2318     safe_closehandle(hDestImage);
 2319     safe_unlockclose(hPhysicalDrive);
 2320     PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
 2321     ExitThread(0);
 2322 }