/* * Rufus: The Reliable USB Formatting Utility * Formatting function calls * Copyright © 2007-2009 Tom Thornhill/Ridgecrop * Copyright © 2011-2013 Pete Batard * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef _CRTDBG_MAP_ALLOC #include #include #endif #include #include #include #include #include #include #include #include #include #include "msapi_utf8.h" #include "rufus.h" #include "resource.h" #include "br.h" #include "fat16.h" #include "fat32.h" #include "ntfs.h" #include "partition_info.h" #include "file.h" #include "format.h" #include "badblocks.h" #include "localization.h" /* * Globals */ DWORD FormatStatus; badblocks_report report; static float format_percent = 0.0f; static int task_number = 0; extern const int nb_steps[FS_MAX]; extern uint32_t dur_mins, dur_secs; static int fs_index = 0; BOOL force_large_fat32 = FALSE; static BOOL WritePBR(HANDLE hLogicalDrive); /* * FormatEx callback. Return FALSE to halt operations */ static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData) { DWORD* percent; if (IS_ERROR(FormatStatus)) return FALSE; switch(Command) { case FCC_PROGRESS: percent = (DWORD*)pData; PrintStatus(0, FALSE, lmprintf(MSG_217, *percent)); UpdateProgress(OP_FORMAT, 1.0f * (*percent)); break; case FCC_STRUCTURE_PROGRESS: // No progress on quick format PrintStatus(0, TRUE, lmprintf(MSG_218, ++task_number, nb_steps[fs_index])); format_percent += 100.0f / (1.0f * nb_steps[fs_index]); UpdateProgress(OP_CREATE_FS, format_percent); break; case FCC_DONE: PrintStatus(0, TRUE, lmprintf(MSG_218, nb_steps[fs_index], nb_steps[fs_index])); UpdateProgress(OP_CREATE_FS, 100.0f); if(*(BOOLEAN*)pData == FALSE) { uprintf("Error while formatting.\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; } break; case FCC_DONE_WITH_STRUCTURE: // We get this message when formatting Small FAT16 // pData Seems to be a struct with at least one (32 BIT!!!) string pointer to the size in MB // uprintf("Done with that sort of thing: Action=%d pData=%0p\n", Action, pData); // /!\ THE FOLLOWING ONLY WORKS ON VISTA OR LATER - DO NOT ENABLE ON XP! // DumpBufferHex(pData, 8); // uprintf("Volume size: %s MB\n", (char*)(LONG_PTR)(*(ULONG32*)pData)); break; case FCC_INCOMPATIBLE_FILE_SYSTEM: uprintf("Incompatible File System\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS); break; case FCC_ACCESS_DENIED: uprintf("Access denied\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; break; case FCC_MEDIA_WRITE_PROTECTED: uprintf("Media is write protected\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT; break; case FCC_VOLUME_IN_USE: uprintf("Volume is in use\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE; break; case FCC_CANT_QUICK_FORMAT: uprintf("Cannot quick format this volume\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_QUICK_FORMAT); break; case FCC_BAD_LABEL: uprintf("Bad label\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_LABEL_TOO_LONG; break; case FCC_OUTPUT: uprintf("%s\n", ((PTEXTOUTPUT)pData)->Output); break; case FCC_CLUSTER_SIZE_TOO_BIG: case FCC_CLUSTER_SIZE_TOO_SMALL: uprintf("Unsupported cluster size\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_CLUSTER_SIZE); break; case FCC_VOLUME_TOO_BIG: case FCC_VOLUME_TOO_SMALL: uprintf("Volume is too %s\n", FCC_VOLUME_TOO_BIG?"big":"small"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_VOLUME_SIZE); case FCC_NO_MEDIA_IN_DRIVE: uprintf("No media in drive\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE; break; default: uprintf("FormatExCallback: received unhandled command %X\n", Command); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED; break; } return (!IS_ERROR(FormatStatus)); } /* * Chkdsk callback. Return FALSE to halt operations */ static BOOLEAN __stdcall ChkdskCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData) { DWORD* percent; if (IS_ERROR(FormatStatus)) return FALSE; switch(Command) { case FCC_PROGRESS: case FCC_CHECKDISK_PROGRESS: percent = (DWORD*)pData; PrintStatus(0, FALSE, lmprintf(MSG_219, *percent)); break; case FCC_DONE: if(*(BOOLEAN*)pData == FALSE) { uprintf("Error while checking disk.\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; } break; case FCC_UNKNOWN1A: case FCC_DONE_WITH_STRUCTURE: // Silence these specific calls break; case FCC_INCOMPATIBLE_FILE_SYSTEM: uprintf("Incompatible File System\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS); break; case FCC_ACCESS_DENIED: uprintf("Access denied\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; break; case FCC_MEDIA_WRITE_PROTECTED: uprintf("Media is write protected\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT; break; case FCC_VOLUME_IN_USE: uprintf("Volume is in use\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE; break; case FCC_OUTPUT: uprintf("%s\n", ((PTEXTOUTPUT)pData)->Output); break; case FCC_NO_MEDIA_IN_DRIVE: uprintf("No media in drive\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE; break; case FCC_READ_ONLY_MODE: uprintf("Media has been switched to read-only - Leaving checkdisk\n"); break; default: uprintf("ChkdskExCallback: received unhandled command %X\n", Command); // Assume the command isn't an error break; } return (!IS_ERROR(FormatStatus)); } /* * Converts an UTF-16 label to a valid FAT/NTFS one */ static void ToValidLabel(WCHAR* name, BOOL bFAT) { size_t i, j, k; BOOL found; WCHAR unauthorized[] = L"*?,;:/\\|+=<>[]\""; WCHAR to_underscore[] = L"\t."; if (name == NULL) return; for (i=0, k=0; i= 0x80) { name[k++] = '_'; found = TRUE; } if (found) continue; } found = FALSE; for (j=0; j= 0xffffffff) { // This is a more fundamental limitation on FAT32 - the total sector count in the root dir // ís 32bit. With a bit of creativity, FAT32 could be extended to handle at least 2^28 clusters // There would need to be an extra field in the FSInfo sector, and the old sector count could // be set to 0xffffffff. This is non standard though, the Windows FAT driver FASTFAT.SYS won't // understand this. Perhaps a future version of FAT32 and FASTFAT will handle this. die ("This drive is too big for FAT32 - max 2TB supported\n", APPERR(ERROR_INVALID_VOLUME_SIZE)); } pFAT32BootSect = (FAT_BOOTSECTOR32*) calloc(BytesPerSect, 1); pFAT32FsInfo = (FAT_FSINFO*) calloc(BytesPerSect, 1); pFirstSectOfFat = (DWORD*) calloc(BytesPerSect, 1); if (!pFAT32BootSect || !pFAT32FsInfo || !pFirstSectOfFat) { die("Failed to allocate memory\n", ERROR_NOT_ENOUGH_MEMORY); } // fill out the boot sector and fs info pFAT32BootSect->sJmpBoot[0]=0xEB; pFAT32BootSect->sJmpBoot[1]=0x5A; pFAT32BootSect->sJmpBoot[2]=0x90; strncpy((char*)pFAT32BootSect->sOEMName, "MSWIN4.1", 8); pFAT32BootSect->wBytsPerSec = (WORD) BytesPerSect; ClusterSize = (DWORD)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)); SectorsPerCluster = ClusterSize / BytesPerSect; pFAT32BootSect->bSecPerClus = (BYTE) SectorsPerCluster ; pFAT32BootSect->wRsvdSecCnt = (WORD) ReservedSectCount; pFAT32BootSect->bNumFATs = (BYTE) NumFATs; pFAT32BootSect->wRootEntCnt = 0; pFAT32BootSect->wTotSec16 = 0; pFAT32BootSect->bMedia = 0xF8; pFAT32BootSect->wFATSz16 = 0; pFAT32BootSect->wSecPerTrk = (WORD) dgDrive.SectorsPerTrack; pFAT32BootSect->wNumHeads = (WORD) dgDrive.TracksPerCylinder; pFAT32BootSect->dHiddSec = (DWORD) piDrive.HiddenSectors; TotalSectors = (DWORD) (piDrive.PartitionLength.QuadPart/dgDrive.BytesPerSector); pFAT32BootSect->dTotSec32 = TotalSectors; FatSize = GetFATSizeSectors(pFAT32BootSect->dTotSec32, pFAT32BootSect->wRsvdSecCnt, pFAT32BootSect->bSecPerClus, pFAT32BootSect->bNumFATs, BytesPerSect); pFAT32BootSect->dFATSz32 = FatSize; pFAT32BootSect->wExtFlags = 0; pFAT32BootSect->wFSVer = 0; pFAT32BootSect->dRootClus = 2; pFAT32BootSect->wFSInfo = 1; pFAT32BootSect->wBkBootSec = (WORD) BackupBootSect; pFAT32BootSect->bDrvNum = 0x80; pFAT32BootSect->Reserved1 = 0; pFAT32BootSect->bBootSig = 0x29; pFAT32BootSect->dBS_VolID = VolumeId; memcpy(pFAT32BootSect->sVolLab, VolId, 11); memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8); ((BYTE*)pFAT32BootSect)[510] = 0x55; ((BYTE*)pFAT32BootSect)[511] = 0xaa; // FATGEN103.DOC says "NOTE: Many FAT documents mistakenly say that this 0xAA55 signature occupies the "last 2 bytes of // the boot sector". This statement is correct if - and only if - BPB_BytsPerSec is 512. If BPB_BytsPerSec is greater than // 512, the offsets of these signature bytes do not change (although it is perfectly OK for the last two bytes at the end // of the boot sector to also contain this signature)." // // Windows seems to only check the bytes at offsets 510 and 511. Other OSs might check the ones at the end of the sector, // so we'll put them there too. if (BytesPerSect != 512) { ((BYTE*)pFAT32BootSect)[BytesPerSect-2] = 0x55; ((BYTE*)pFAT32BootSect)[BytesPerSect-1] = 0xaa; } // FSInfo sect pFAT32FsInfo->dLeadSig = 0x41615252; pFAT32FsInfo->dStrucSig = 0x61417272; pFAT32FsInfo->dFree_Count = (DWORD) -1; pFAT32FsInfo->dNxt_Free = (DWORD) -1; pFAT32FsInfo->dTrailSig = 0xaa550000; // First FAT Sector pFirstSectOfFat[0] = 0x0ffffff8; // Reserved cluster 1 media id in low byte pFirstSectOfFat[1] = 0x0fffffff; // Reserved cluster 2 EOC pFirstSectOfFat[2] = 0x0fffffff; // end of cluster chain for root dir // Write boot sector, fats // Sector 0 Boot Sector // Sector 1 FSInfo // Sector 2 More boot code - we write zeros here // Sector 3 unused // Sector 4 unused // Sector 5 unused // Sector 6 Backup boot sector // Sector 7 Backup FSInfo sector // Sector 8 Backup 'more boot code' // zeroed sectors upto ReservedSectCount // FAT1 ReservedSectCount to ReservedSectCount + FatSize // ... // FATn ReservedSectCount to ReservedSectCount + FatSize // RootDir - allocated to cluster2 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize); ClusterCount = UserAreaSize / SectorsPerCluster; // Sanity check for a cluster count of >2^28, since the upper 4 bits of the cluster values in // the FAT are reserved. if (ClusterCount > 0x0FFFFFFF) { die("This drive has more than 2^28 clusters, try to specify a larger cluster size or use the default\n", ERROR_INVALID_CLUSTER_SIZE); } // Sanity check - < 64K clusters means that the volume will be misdetected as FAT16 if (ClusterCount < 65536) { die("FAT32 must have at least 65536 clusters, try to specify a smaller cluster size or use the default\n", ERROR_INVALID_CLUSTER_SIZE); } // Sanity check, make sure the fat is big enough // Convert the cluster count into a Fat sector count, and check the fat size value we calculated // earlier is OK. FatNeeded = ClusterCount * 4; FatNeeded += (BytesPerSect-1); FatNeeded /= BytesPerSect; if (FatNeeded > FatSize) { die("This drive is too big for large FAT32 format\n", APPERR(ERROR_INVALID_VOLUME_SIZE)); } // Now we're committed - print some info first uprintf("Size : %s %u sectors\n", SizeToHumanReadable(piDrive.PartitionLength), TotalSectors); uprintf("Cluster size %d bytes, %d Bytes Per Sector\n", SectorsPerCluster*BytesPerSect, BytesPerSect); uprintf("Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff); uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs\n", ReservedSectCount, FatSize, NumFATs); uprintf("%d Total clusters\n", ClusterCount); // Fix up the FSInfo sector pFAT32FsInfo->dFree_Count = (UserAreaSize/SectorsPerCluster) - 1; pFAT32FsInfo->dNxt_Free = 3; // clusters 0-1 reserved, we used cluster 2 for the root dir uprintf("%d Free Clusters\n", pFAT32FsInfo->dFree_Count); // Work out the Cluster count // First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster SystemAreaSize = ReservedSectCount + (NumFATs*FatSize) + SectorsPerCluster; uprintf("Clearing out %d sectors for reserved sectors, FATs and root cluster...\n", SystemAreaSize); // Not the most effective, but easy on RAM pZeroSect = (BYTE*)calloc(BytesPerSect, BurstSize); if (!pZeroSect) { die("Failed to allocate memory\n", ERROR_NOT_ENOUGH_MEMORY); } format_percent = 0.0f; for (i=0; i<(SystemAreaSize+BurstSize-1); i+=BurstSize) { if (GetTickCount() > LastRefresh + 25) { LastRefresh = GetTickCount(); format_percent = (100.0f*i)/(1.0f*(SystemAreaSize+BurstSize)); PrintStatus(0, FALSE, lmprintf(MSG_217, format_percent)); UpdateProgress(OP_FORMAT, format_percent); } if (IS_ERROR(FormatStatus)) goto out; // For cancellation if (write_sectors(hLogicalVolume, BytesPerSect, i, BurstSize, pZeroSect) != (BytesPerSect*BurstSize)) { die("Error clearing reserved sectors\n", ERROR_WRITE_FAULT); } } uprintf ("Initializing reserved sectors and FATs...\n"); // Now we should write the boot sector and fsinfo twice, once at 0 and once at the backup boot sect position for (i=0; i<2; i++) { int SectorStart = (i==0) ? 0 : BackupBootSect; write_sectors(hLogicalVolume, BytesPerSect, SectorStart, 1, pFAT32BootSect); write_sectors(hLogicalVolume, BytesPerSect, SectorStart+1, 1, pFAT32FsInfo); } // Write the first fat sector in the right places for ( i=0; i return true // \minint and no \i386 without /minint is unclear => return error if (iso_report.winpe&WINPE_MININT) { if (iso_report.uses_minint) { uprintf("Detected \\minint directory with /minint option: nothing to patch\n"); r = TRUE; } else if (!(iso_report.winpe&WINPE_I386)) { uprintf("Detected \\minint directory only but no /minint option: not sure what to do\n"); } goto out; } // At this stage we only handle \i386 handle = CreateFileA(dst, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { uprintf("Could not open %s for patching: %s\n", dst, WindowsErrorString()); goto out; } size = GetFileSize(handle, NULL); if (size == INVALID_FILE_SIZE) { uprintf("Could not get size for file %s: %s\n", dst, WindowsErrorString()); goto out; } buf = (char*)malloc(size); if (buf == NULL) goto out; if ((!ReadFile(handle, buf, size, &rw_size, NULL)) || (size != rw_size)) { uprintf("Could not read file %s: %s\n", dst, WindowsErrorString()); goto out; } SetFilePointer(handle, 0, NULL, FILE_BEGIN); // Patch setupldr.bin uprintf("Patching file %s\n", dst); // Remove CRC check for 32 bit part of setupldr.bin from Win2k3 if ((size > 0x2061) && (buf[0x2060] == 0x74) && (buf[0x2061] == 0x03)) { buf[0x2060] = 0xeb; buf[0x2061] = 0x1a; uprintf(" 0x00002060: 0x74 0x03 -> 0xEB 0x1A (disable Win2k3 CRC check)\n"); } for (i=1; i '%s'\n", i, &buf[i], patch_str_rep[j]); strcpy(&buf[i], patch_str_rep[j]); i += (DWORD)max(strlen(patch_str_org[j]), strlen(patch_str_rep[j])); // in case org is a substring of rep } } } if (!iso_report.uses_minint) { // Additional setupldr.bin/bootmgr patching for (i=0; i rdisk(#) disk masquerading // NB: only the first one seems to be needed if (safe_strnicmp(&buf[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) { buf[i+6] = 0x30 + ComboBox_GetCurSel(hDiskID); uprintf(" 0x%08X: '%s' -> 'rdisk(%c)'\n", i, rdisk_zero, buf[i+6]); } // $WIN_NT$_~BT -> i386 if (safe_strnicmp(&buf[i], win_nt_bt_org, strlen(win_nt_bt_org)-1) == 0) { uprintf(" 0x%08X: '%s' -> '%s%s'\n", i, &buf[i], win_nt_bt_rep, &buf[i+strlen(win_nt_bt_org)]); strcpy(&buf[i], win_nt_bt_rep); // This ensures that we keep the terminator backslash buf[i+strlen(win_nt_bt_rep)] = buf[i+strlen(win_nt_bt_org)]; buf[i+strlen(win_nt_bt_rep)+1] = 0; } } } if ((!WriteFile(handle, buf, size, &rw_size, NULL)) || (size != rw_size)) { uprintf("Could not write patched file: %s\n", WindowsErrorString()); goto out; } safe_free(buf); safe_closehandle(handle); r = TRUE; out: safe_closehandle(handle); safe_free(buf); return r; } /* * Detect if a Windows Format prompt is active, by enumerating the * whole Windows tree and looking for the relevant popup */ static BOOL CALLBACK FormatPromptCallback(HWND hWnd, LPARAM lParam) { char str_buf[MAX_PATH]; HWND *hFound = (HWND*)lParam; static const char* security_string = "Microsoft Windows"; // The format prompt has the popup window style if (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUPWINDOW) { str_buf[0] = 0; GetWindowTextA(hWnd, str_buf, MAX_PATH); str_buf[MAX_PATH-1] = 0; if (safe_strcmp(str_buf, security_string) == 0) { *hFound = hWnd; return TRUE; } } return TRUE; } /* * When we format a drive that doesn't have any existing partitions, we can't lock it * prior to partitioning, which means that Windows will display a "You need to format the * disk in drive X: before you can use it'. We have to close that popup manually. */ DWORD WINAPI CloseFormatPromptThread(LPVOID param) { HWND hFormatPrompt; while(format_op_in_progress) { hFormatPrompt = NULL; EnumChildWindows(GetDesktopWindow(), FormatPromptCallback, (LPARAM)&hFormatPrompt); if (hFormatPrompt != NULL) { SendMessage(hFormatPrompt, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); uprintf("Closed Windows format prompt\n"); } Sleep(50); } ExitThread(0); } /* * Standalone thread for the formatting operation * According to http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562.aspx * To change a volume file system * Open a volume. * Lock the volume. * Format the volume. * Dismount the volume. * Unlock the volume. * Close the volume handle. */ #define CHECK_FOR_USER_CANCEL if (IS_ERROR(FormatStatus)) goto out DWORD WINAPI FormatThread(LPVOID param) { int r, pt, bt, fs, dt; BOOL ret, use_large_fat32; DWORD DriveIndex = (DWORD)(uintptr_t)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; SYSTEMTIME lt; char *bb_msg, *guid_volume = NULL; char drive_name[] = "?:\\"; char logfile[MAX_PATH], *userdir; char wim_image[] = "?:\\sources\\install.wim"; char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; FILE* log_fd; fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); dt = (int)ComboBox_GetItemData(hBootType, ComboBox_GetCurSel(hBootType)); pt = GETPARTTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); use_large_fat32 = (fs == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32)); PrintStatus(0, TRUE, lmprintf(MSG_225)); hPhysicalDrive = GetPhysicalHandle(DriveIndex, TRUE, TRUE); if (hPhysicalDrive == INVALID_HANDLE_VALUE) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } // At this stage with have both a handle and a lock to the physical drive... if (!GetDriveLetter(DriveIndex, &drive_name[0])) { uprintf("Failed to get a drive letter\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); goto out; } if (drive_name[0] == ' ') { uprintf("No drive letter was assigned...\n"); drive_name[0] = GetUnusedDriveLetter(); if (drive_name[0] == ' ') { uprintf("Could not find a suitable drive letter\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); goto out; } } else if (!DeleteVolumeMountPointA(drive_name)) { uprintf("Failed to delete mountpoint %s: %s\n", drive_name, WindowsErrorString()); // Try to continue. We will bail out if this causes an issue. } uprintf("Will use '%c:' as volume mountpoint\n", drive_name[0]); // ...but we need a lock to the logical drive to be able to write anything to it hLogicalVolume = GetLogicalHandle(DriveIndex, FALSE, TRUE); if (hLogicalVolume == INVALID_HANDLE_VALUE) { uprintf("Could not lock volume\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } else if (hLogicalVolume == NULL) { // NULL is returned for cases where the drive is not yet partitioned uprintf("Drive does not appear to be partitioned\n"); } else if (!UnmountVolume(hLogicalVolume)) { uprintf("Trying to continue regardless...\n"); } CHECK_FOR_USER_CANCEL; PrintStatus(0, TRUE, lmprintf(MSG_226)); AnalyzeMBR(hPhysicalDrive); if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) { AnalyzePBR(hLogicalVolume); } UpdateProgress(OP_ANALYZE_MBR, -1.0f); // Zap any existing partitions. This helps prevent access errors. // As this creates issues with FAT16 formatted MS drives, only do this for other filesystems if ( (fs != FS_FAT16) && (!DeletePartitions(hPhysicalDrive)) ) { uprintf("Could not reset partitions\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } if (IsChecked(IDC_BADBLOCKS)) { do { // create a log file for bad blocks report. Since %USERPROFILE% may // have localized characters, we use the UTF-8 API. userdir = getenvU("USERPROFILE"); safe_strcpy(logfile, MAX_PATH, userdir); safe_free(userdir); GetLocalTime(<); safe_sprintf(&logfile[strlen(logfile)], sizeof(logfile)-strlen(logfile)-1, "\\rufus_%04d%02d%02d_%02d%02d%02d.log", lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond); log_fd = fopenU(logfile, "w+"); if (log_fd == NULL) { uprintf("Could not create log file for bad blocks check\n"); } else { fprintf(log_fd, APPLICATION_NAME " bad blocks check started on: %04d.%02d.%02d %02d:%02d:%02d\n", lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond); fflush(log_fd); } if (!BadBlocks(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.Geometry.BytesPerSector, ComboBox_GetCurSel(hNBPasses)+1, &report, log_fd)) { uprintf("Bad blocks: Check failed.\n"); if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_BADBLOCKS_FAILURE); ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.Geometry.BytesPerSector); fclose(log_fd); _unlink(logfile); goto out; } uprintf("Bad Blocks: Check completed, %u bad block%s found. (%d/%d/%d errors)\n", report.bb_count, (report.bb_count==1)?"":"s", report.num_read_errors, report.num_write_errors, report.num_corruption_errors); r = IDOK; if (report.bb_count) { bb_msg = lmprintf(MSG_011, report.num_read_errors, report.num_write_errors, report.num_corruption_errors); fprintf(log_fd, bb_msg); GetLocalTime(<); fprintf(log_fd, APPLICATION_NAME " bad blocks check ended on: %04d.%02d.%02d %02d:%02d:%02d\n", lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond); fclose(log_fd); r = MessageBoxU(hMainDialog, lmprintf(MSG_012, bb_msg, logfile), lmprintf(MSG_010), MB_ABORTRETRYIGNORE|MB_ICONWARNING); } else { // We didn't get any errors => delete the log file fclose(log_fd); _unlink(logfile); } } while (r == IDRETRY); if (r == IDABORT) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED; goto out; } } // Close the (unmounted) volume before formatting if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) { PrintStatus(0, TRUE, lmprintf(MSG_227)); if (!CloseHandle(hLogicalVolume)) { uprintf("Could not close volume: %s\n", WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; goto out; } } hLogicalVolume = INVALID_HANDLE_VALUE; // TODO: (v1.5) Our start button should become cancel instead of close // Especially after destructive badblocks test, you must zero the MBR/GPT completely // before repartitioning. Else, all kind of bad things happen. if (!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.Geometry.BytesPerSector)) { uprintf("unable to zero MBR/GPT\n"); if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } UpdateProgress(OP_ZERO_MBR, -1.0f); CHECK_FOR_USER_CANCEL; CreateThread(NULL, 0, CloseFormatPromptThread, NULL, 0, NULL); if (!CreatePartition(hPhysicalDrive, pt, fs, (pt==PARTITION_STYLE_MBR)&&(bt==BT_UEFI))) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } UpdateProgress(OP_PARTITION, -1.0f); // Wait for the logical drive we just created to appear uprintf("Waiting for logical drive to reappear...\n"); Sleep(200); WaitForLogical(DriveIndex); // We try to continue even if this fails, just in case CHECK_FOR_USER_CANCEL; // If FAT32 is requested and we have a large drive (>32 GB) use // large FAT32 format, else use MS's FormatEx. ret = use_large_fat32?FormatFAT32(DriveIndex):FormatDrive(DriveIndex); if (!ret) { // Error will be set by FormatDrive() in FormatStatus uprintf("Format error: %s\n", StrError(FormatStatus)); goto out; } // Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted if (pt == PARTITION_STYLE_MBR) { PrintStatus(0, TRUE, lmprintf(MSG_228)); if (!WriteMBR(hPhysicalDrive)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } UpdateProgress(OP_FIX_MBR, -1.0f); } Sleep(200); WaitForLogical(DriveIndex); // Try to continue CHECK_FOR_USER_CANCEL; guid_volume = GetLogicalName(DriveIndex, TRUE, TRUE); if (guid_volume == NULL) { uprintf("Could not get GUID volume name\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_VOLUME_ID; goto out; } uprintf("Found volume GUID %s\n", guid_volume); if (!MountVolume(drive_name, guid_volume)) { uprintf("Could not remount %s on %s: %s\n", guid_volume, drive_name, WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_MOUNT_VOLUME); goto out; } CHECK_FOR_USER_CANCEL; if (IsChecked(IDC_BOOT)) { if (bt == BT_UEFI) { // For once, no need to do anything - just check our sanity if ( (dt != DT_ISO) || (!IS_EFI(iso_report)) || (fs > FS_FAT32) ) { uprintf("Spock gone crazy error!\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; goto out; } } else if ((((dt == DT_WINME) || (dt == DT_FREEDOS)) && (!use_large_fat32)) || ((dt == DT_ISO) && (fs == FS_NTFS))) { // We still have a lock, which we need to modify the volume boot record // => no need to reacquire the lock... hLogicalVolume = GetLogicalHandle(DriveIndex, TRUE, FALSE); if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) { uprintf("Could not re-mount volume for partition boot record access\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } // NB: if you unmount the logical volume here, XP will report error: // [0x00000456] The media in the drive may have changed PrintStatus(0, TRUE, lmprintf(MSG_229)); if (!WritePBR(hLogicalVolume)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } // We must close and unlock the volume to write files to it safe_unlockclose(hLogicalVolume); } else if ( (dt == DT_SYSLINUX_V4) || (dt == DT_SYSLINUX_V5) || ((dt == DT_ISO) && ((fs == FS_FAT16) || (fs == FS_FAT32))) ) { if (!InstallSyslinux(DriveIndex, drive_name[0])) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; } } } else { if (IsChecked(IDC_SET_ICON)) SetAutorun(drive_name); } CHECK_FOR_USER_CANCEL; // We issue a complete remount of the filesystem at on account of: // - Ensuring the file explorer properly detects that the volume was updated // - Ensuring that an NTFS system will be reparsed so that it becomes bootable if (!RemountVolume(drive_name)) goto out; CHECK_FOR_USER_CANCEL; if (IsChecked(IDC_BOOT)) { if ((dt == DT_WINME) || (dt == DT_FREEDOS)) { UpdateProgress(OP_DOS, -1.0f); PrintStatus(0, TRUE, lmprintf(MSG_230)); if (!ExtractDOS(drive_name)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; goto out; } } else if (dt == DT_ISO) { if (iso_path != NULL) { UpdateProgress(OP_DOS, 0.0f); PrintStatus(0, TRUE, lmprintf(MSG_231)); drive_name[2] = 0; if (!ExtractISO(iso_path, drive_name, FALSE)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; goto out; } if ((bt == BT_UEFI) && (!iso_report.has_efi) && (iso_report.has_win7_efi)) { // TODO: (v1.4.0) check ISO with EFI only PrintStatus(0, TRUE, lmprintf(MSG_232)); wim_image[0] = drive_name[0]; efi_dst[0] = drive_name[0]; efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = 0; if (!CreateDirectoryA(efi_dst, 0)) { uprintf("Could not create directory '%s': %s\n", WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } else { efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = '\\'; if (!WimExtractFile(wim_image, 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst)) { uprintf("Failed to setup Win7 EFI boot\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } } } } if ( (bt == BT_BIOS) && (IS_WINPE(iso_report.winpe)) ) { // Apply WinPe fixup if (!SetupWinPE(drive_name[0])) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } } UpdateProgress(OP_FINALIZE, -1.0f); PrintStatus(0, TRUE, lmprintf(MSG_233)); if (IsChecked(IDC_SET_ICON)) SetAutorun(drive_name); // Issue another complete remount before we exit, to ensure we're clean RemountVolume(drive_name); // NTFS fixup (WinPE/AIK images don't seem to boot without an extra checkdisk) if ((dt == DT_ISO) && (fs == FS_NTFS)) { CheckDisk(drive_name[0]); UpdateProgress(OP_FINALIZE, -1.0f); } } out: safe_free(guid_volume); SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); // This can take a while PostMessage(hMainDialog, UM_FORMAT_COMPLETED, 0, 0); ExitThread(0); }