/* * Rufus: The Reliable USB Formatting Utility * Formatting function calls * Copyright © 2007-2009 Tom Thornhill/Ridgecrop * Copyright © 2011-2016 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 "rufus.h" #include "missing.h" #include "resource.h" #include "msapi_utf8.h" #include "localization.h" #include "br.h" #include "fat16.h" #include "fat32.h" #include "ntfs.h" #include "partition_info.h" #include "file.h" #include "drive.h" #include "format.h" #include "badblocks.h" #include "bled/bled.h" #include "../res/grub/grub_version.h" /* * Globals */ DWORD FormatStatus; badblocks_report report; static uint64_t LastRefresh; 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; extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive; uint8_t *grub2_buf = NULL; long grub2_len; static BOOL WritePBR(HANDLE hLogicalDrive); /* * Convert the fmifs outputs messages (that use an OEM code page) to UTF-8 */ static void OutputUTF8Message(const char* src) { int len; char *dst = NULL; wchar_t* wdst = NULL; if (src == NULL) goto out; len = (int)safe_strlen(src); while ((len > 0) && ((src[len-1] == 0x0A) || (src[len-1] == 0x0D) || (src[len-1] == ' '))) len--; if (len == 0) goto out; len = MultiByteToWideChar(CP_OEMCP, 0, src, len, NULL, 0); if (len == 0) goto out; wdst = (wchar_t*)calloc(len+1, sizeof(wchar_t)); if ((wdst == NULL) || (MultiByteToWideChar(CP_OEMCP, 0, src, len, wdst, len+1) == 0)) goto out; dst = wchar_to_utf8(wdst); if (dst == NULL) goto out; uprintf("%s", dst); out: safe_free(dst); safe_free(wdst); } /* * 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; PrintInfo(0, MSG_217, 1.0f * (*percent)); UpdateProgress(OP_FORMAT, 1.0f * (*percent)); break; case FCC_STRUCTURE_PROGRESS: // No progress on quick format if (task_number < nb_steps[fs_index] - 1) { if (task_number == 0) uprintf("Creating file system..."); PrintInfo(0, MSG_218, ++task_number, nb_steps[fs_index]); format_percent += 100.0f / (1.0f * nb_steps[fs_index]); UpdateProgress(OP_CREATE_FS, format_percent); } break; case FCC_DONE: PrintInfo(0, MSG_218, nb_steps[fs_index], nb_steps[fs_index]); UpdateProgress(OP_CREATE_FS, 100.0f); if(*(BOOLEAN*)pData == FALSE) { uprintf("Error while formatting"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; } break; case FCC_DONE_WITH_STRUCTURE: // We get this message when formatting Small FAT16 // pData Seems to be a struct with at least one (32 BIT!!!) string pointer to the size in MB // uprintf("Done with that sort of thing: Action=%d pData=%0p\n", Action, pData); // /!\ THE FOLLOWING ONLY WORKS ON VISTA OR LATER - DO NOT ENABLE ON XP! // DumpBufferHex(pData, 8); // uprintf("Volume size: %s MB\n", (char*)(LONG_PTR)(*(ULONG32*)pData)); break; case FCC_INCOMPATIBLE_FILE_SYSTEM: uprintf("Incompatible File System"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS); break; case FCC_ACCESS_DENIED: uprintf("Access denied"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; break; case FCC_MEDIA_WRITE_PROTECTED: uprintf("Media is write protected"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT; break; case FCC_VOLUME_IN_USE: uprintf("Volume is in use"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE; break; case FCC_DEVICE_NOT_READY: uprintf("The device is not ready"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_READY; break; case FCC_CANT_QUICK_FORMAT: uprintf("Cannot quick format this volume"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_QUICK_FORMAT); break; case FCC_BAD_LABEL: uprintf("Bad label"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_LABEL_TOO_LONG; break; case FCC_OUTPUT: OutputUTF8Message(((PTEXTOUTPUT)pData)->Output); break; case FCC_CLUSTER_SIZE_TOO_BIG: case FCC_CLUSTER_SIZE_TOO_SMALL: uprintf("Unsupported cluster size"); 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", (Command == FCC_VOLUME_TOO_BIG)?"big":"small"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_VOLUME_SIZE); break; case FCC_NO_MEDIA_IN_DRIVE: uprintf("No media in drive"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE; break; default: uprintf("FormatExCallback: Received unhandled command 0x02%X - aborting", 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; PrintInfo(0, 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: OutputUTF8Message(((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; jGeometry, sizeof(dgDrive)); } if (dgDrive.BytesPerSector < 512) dgDrive.BytesPerSector = 512; if (IS_ERROR(FormatStatus)) goto out; if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &piDrive, sizeof(piDrive), &cbRet, NULL)) { if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &xpiDrive, sizeof(xpiDrive), &cbRet, NULL)) { uprintf("IOCTL_DISK_GET_PARTITION_INFO error: %s\n", WindowsErrorString()); die("Failed to get partition info (both regular and _ex)\n", ERROR_NOT_SUPPORTED); } memset(&piDrive, 0, sizeof(piDrive)); piDrive.StartingOffset.QuadPart = xpiDrive.StartingOffset.QuadPart; piDrive.PartitionLength.QuadPart = xpiDrive.PartitionLength.QuadPart; piDrive.HiddenSectors = (DWORD) (xpiDrive.StartingOffset.QuadPart / dgDrive.BytesPerSector); } if (IS_ERROR(FormatStatus)) goto out; BytesPerSect = dgDrive.BytesPerSector; // Checks on Disk Size qTotalSectors = piDrive.PartitionLength.QuadPart/dgDrive.BytesPerSector; // Low end limit - 65536 sectors if (qTotalSectors < 65536) { // Most FAT32 implementations would probably mount this volume just fine, // but the spec says that we shouldn't do this, so we won't die("This drive is too small for FAT32 - there must be at least 64K clusters\n", APPERR(ERROR_INVALID_CLUSTER_SIZE)); } if (qTotalSectors >= 0xffffffff) { // This is a more fundamental limitation on FAT32 - the total sector count in the root dir // is 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)); } // coverity[tainted_data] 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]=0x58; // jmp.s $+0x5a is 0xeb 0x58, not 0xeb 0x5a. Thanks Marco! 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.QuadPart, TRUE, FALSE), TotalSectors); uprintf("Cluster size %d bytes, %d Bytes Per Sector\n", SectorsPerCluster*BytesPerSect, BytesPerSect); uprintf("Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff); uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs\n", ReservedSectCount, FatSize, NumFATs); uprintf("%d Total clusters\n", ClusterCount); // 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 (_GetTickCount64() > LastRefresh + MAX_REFRESH) { LastRefresh = _GetTickCount64(); format_percent = (100.0f*i)/(1.0f*(SystemAreaSize+BurstSize)); PrintInfo(0, 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 remove it for (i=strlen(FSType); i>2; i--) { if (FSType[i] == '(') { FSType[i-1] = 0; break; } } fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); if ((fs == FS_UDF) && !((dur_mins == 0) && (dur_secs == 0))) { PrintInfoDebug(0, MSG_220, FSType, dur_mins, dur_secs); } else { PrintInfoDebug(0, MSG_222, FSType); } VolumeName = GetLogicalName(DriveIndex, TRUE, TRUE); wVolumeName = utf8_to_wchar(VolumeName); if (wVolumeName == NULL) { uprintf("Could not read volume name\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE; goto out; } // Hey, nice consistency here, Microsoft! - FormatEx() fails if wVolumeName has // a trailing backslash, but EnableCompression() fails without... wVolumeName[wcslen(wVolumeName)-1] = 0; // Remove trailing backslash // LoadLibrary("fmifs.dll") appears to changes the locale, which can lead to // problems with tolower(). Make sure we restore the locale. For more details, // see http://comments.gmane.org/gmane.comp.gnu.mingw.user/39300 locale = setlocale(LC_ALL, NULL); PF_INIT_OR_OUT(FormatEx, Fmifs); PF_INIT(EnableVolumeCompression, Fmifs); setlocale(LC_ALL, locale); GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType)); // We may have a " (Default)" trail for (i=0; i max_size) { uprintf(" SBR size is too large - You may need to uncheck 'Add fixes for old BIOSes'."); return FALSE; } r = write_data(fp, mbr_size, buf, (uint64_t)size); safe_free(grub2_buf); return (r != 0); } /* * Process the Partition Boot Record */ static __inline const char* bt_to_name(int bt) { switch (bt) { case BT_FREEDOS: return "FreeDOS"; case BT_REACTOS: return "ReactOS"; default: return ((bt==BT_ISO)&&(img_report.has_kolibrios))?"KolibriOS":"Standard"; } } static BOOL WritePBR(HANDLE hLogicalVolume) { int i; FAKE_FD fake_fd = { 0 }; FILE* fp = (FILE*)&fake_fd; int bt = (int)ComboBox_GetItemData(hBootType, ComboBox_GetCurSel(hBootType)); const char* using_msg = "Using %s %s partition boot record\n"; fake_fd._handle = (char*)hLogicalVolume; set_bytes_per_sector(SelectedDrive.SectorSize); switch (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))) { case FS_FAT16: uprintf(using_msg, bt_to_name(bt), "FAT16"); if (!is_fat_16_fs(fp)) { uprintf("New volume does not have a FAT16 boot sector - aborting\n"); break; } uprintf("Confirmed new volume has a FAT16 boot sector\n"); if (bt == BT_FREEDOS) { if (!write_fat_16_fd_br(fp, 0)) break; } else if (bt == BT_REACTOS) { if (!write_fat_16_ros_br(fp, 0)) break; } else if ((bt == BT_ISO) && (img_report.has_kolibrios)) { uprintf("FAT16 is not supported for KolibriOS\n"); break; } else { if (!write_fat_16_br(fp, 0)) break; } // Disk Drive ID needs to be corrected on XP if (!write_partition_physical_disk_drive_id_fat16(fp)) break; return TRUE; case FS_FAT32: uprintf(using_msg, bt_to_name(bt), "FAT32"); for (i=0; i<2; i++) { if (!is_fat_32_fs(fp)) { uprintf("New volume does not have a %s FAT32 boot sector - aborting\n", i?"secondary":"primary"); break; } uprintf("Confirmed new volume has a %s FAT32 boot sector\n", i?"secondary":"primary"); uprintf("Setting %s FAT32 boot sector for boot...\n", i?"secondary":"primary"); if (bt == BT_FREEDOS) { if (!write_fat_32_fd_br(fp, 0)) break; } else if (bt == BT_REACTOS) { if (!write_fat_32_ros_br(fp, 0)) break; } else if ((bt == BT_ISO) && (img_report.has_kolibrios)) { if (!write_fat_32_kos_br(fp, 0)) break; } else { if (!write_fat_32_br(fp, 0)) break; } // Disk Drive ID needs to be corrected on XP if (!write_partition_physical_disk_drive_id_fat32(fp)) break; fake_fd._offset += 6 * SelectedDrive.SectorSize; } return TRUE; case FS_NTFS: uprintf(using_msg, bt_to_name(bt), "NTFS"); if (!is_ntfs_fs(fp)) { uprintf("New volume does not have an NTFS boot sector - aborting\n"); break; } uprintf("Confirmed new volume has an NTFS boot sector\n"); if (!write_ntfs_br(fp)) break; // Note: NTFS requires a full remount after writing the PBR. We dismount when we lock // and also go through a forced remount, so that shouldn't be an issue. // But with NTFS, if you don't remount, you don't boot! return TRUE; default: uprintf("Unsupported FS for FS BR processing - aborting\n"); break; } FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; return FALSE; } /* * Setup WinPE for bootable USB */ static BOOL SetupWinPE(char drive_letter) { char src[64], dst[32]; const char* basedir[] = { "i386", "minint" }; const char* patch_str_org[] = { "\\minint\\txtsetup.sif", "\\minint\\system32\\" }; const char* patch_str_rep[] = { "\\i386\\txtsetup.sif", "\\i386\\system32\\" }; const char *win_nt_bt_org = "$win_nt$.~bt", *win_nt_bt_rep = "i386"; const char *rdisk_zero = "rdisk(0)"; const LARGE_INTEGER liZero = { {0, 0} }; char setupsrcdev[64]; HANDLE handle = INVALID_HANDLE_VALUE; DWORD i, j, size, rw_size, index = 0; BOOL r = FALSE; char* buffer = NULL; index = ((img_report.winpe&WINPE_I386) == WINPE_I386)?0:1; // Allow other values than harddisk 1, as per user choice for disk ID safe_sprintf(setupsrcdev, sizeof(setupsrcdev), "SetupSourceDevice = \"\\device\\harddisk%d\\partition1\"", ComboBox_GetCurSel(hDiskID)); // Copy of ntdetect.com in root safe_sprintf(src, sizeof(src), "%c:\\%s\\ntdetect.com", drive_letter, basedir[index]); safe_sprintf(dst, sizeof(dst), "%c:\\ntdetect.com", drive_letter); CopyFileA(src, dst, TRUE); if (!img_report.uses_minint) { // Create a copy of txtsetup.sif, as we want to keep the i386 files unmodified safe_sprintf(src, sizeof(src), "%c:\\%s\\txtsetup.sif", drive_letter, basedir[index]); safe_sprintf(dst, sizeof(dst), "%c:\\txtsetup.sif", drive_letter); if (!CopyFileA(src, dst, TRUE)) { uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString()); } if (insert_section_data(dst, "[SetupData]", setupsrcdev, FALSE) == NULL) { uprintf("Failed to add SetupSourceDevice in %s\n", dst); goto out; } uprintf("Successfully added '%s' to %s\n", setupsrcdev, dst); } safe_sprintf(src, sizeof(src), "%c:\\%s\\setupldr.bin", drive_letter, basedir[index]); safe_sprintf(dst, sizeof(dst), "%c:\\BOOTMGR", drive_letter); if (!CopyFileA(src, dst, TRUE)) { uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString()); } // \minint with /minint option doesn't require further processing => return true // \minint and no \i386 without /minint is unclear => return error if (img_report.winpe&WINPE_MININT) { if (img_report.uses_minint) { uprintf("Detected \\minint directory with /minint option: nothing to patch\n"); r = TRUE; } else if (!(img_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; } buffer = (char*)malloc(size); if (buffer == NULL) goto out; if ((!ReadFile(handle, buffer, size, &rw_size, NULL)) || (size != rw_size)) { uprintf("Could not read file %s: %s\n", dst, WindowsErrorString()); goto out; } SetFilePointerEx(handle, liZero, 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) && (buffer[0x2060] == 0x74) && (buffer[0x2061] == 0x03)) { buffer[0x2060] = 0xeb; buffer[0x2061] = 0x1a; uprintf(" 0x00002060: 0x74 0x03 -> 0xEB 0x1A (disable Win2k3 CRC check)\n"); } for (i=1; i '%s'\n", i, &buffer[i], patch_str_rep[j]); strcpy(&buffer[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 (!img_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(&buffer[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) { buffer[i+6] = 0x30 + ComboBox_GetCurSel(hDiskID); uprintf(" 0x%08X: '%s' -> 'rdisk(%c)'\n", i, rdisk_zero, buffer[i+6]); } // $WIN_NT$_~BT -> i386 if (safe_strnicmp(&buffer[i], win_nt_bt_org, strlen(win_nt_bt_org)-1) == 0) { uprintf(" 0x%08X: '%s' -> '%s%s'\n", i, &buffer[i], win_nt_bt_rep, &buffer[i+strlen(win_nt_bt_org)]); strcpy(&buffer[i], win_nt_bt_rep); // This ensures that we keep the terminator backslash buffer[i+strlen(win_nt_bt_rep)] = buffer[i+strlen(win_nt_bt_org)]; buffer[i+strlen(win_nt_bt_rep)+1] = 0; } } } if (!WriteFileWithRetry(handle, buffer, size, &rw_size, WRITE_RETRIES)) { uprintf("Could not write patched file: %s\n", WindowsErrorString()); goto out; } r = TRUE; out: safe_closehandle(handle); safe_free(buffer); return r; } // http://technet.microsoft.com/en-ie/library/jj721578.aspx // As opposed to the technet guide above, we no longer set internal drives offline, // due to people wondering why they can't see them by default, and also due to dism // incompatibilities from one version of Windows to the next. // Maybe when we use wimlib we'll review this, but for now just turn it off. //#define SET_INTERNAL_DRIVES_OFFLINE static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) { #ifdef SET_INTERNAL_DRIVES_OFFLINE static char san_policy_path[] = "?:\\san_policy.xml"; #endif static char unattend_path[] = "?:\\Windows\\System32\\sysprep\\unattend.xml"; char *mounted_iso, *ms_efi = NULL, image[128], cmd[MAX_PATH]; unsigned char *buffer; wchar_t wVolumeName[] = L"?:"; DWORD bufsize; ULONG cluster_size; FILE* fd; PF_DECL(FormatEx); PF_INIT(FormatEx, Fmifs); uprintf("Windows To Go mode selected"); // Additional sanity checks if ( ((use_ms_efi) && (SelectedDrive.MediaType != FixedMedia)) || ((nWindowsVersion < WINDOWS_8) || ((WimExtractCheck() & 4) == 0)) ) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED; return FALSE; } if (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)) != FS_NTFS) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS); return FALSE; } // First, we need to access the install.wim image, that resides on the ISO mounted_iso = MountISO(image_path); if (mounted_iso == NULL) { uprintf("Could not mount ISO for Windows To Go installation"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); return FALSE; } uprintf("Mounted ISO as '%s'", mounted_iso); // Now we use the WIM API to apply that image static_sprintf(image, "%s%s", mounted_iso, &img_report.install_wim_path[2]); if (!WimApplyImage(image, 1, drive_name)) { uprintf("Failed to apply Windows To Go image"); if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); UnMountISO(); return FALSE; } UnMountISO(); if (use_ms_efi) { uprintf("Setting up MS EFI system partition"); if (pfFormatEx == NULL) return FALSE; ms_efi = AltMountVolume(drive_name, 3); // MSR, main, EFI if (ms_efi == NULL) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); return FALSE; } uprintf("Formatting EFI system partition %s", ms_efi); // According to Ubuntu (https://bugs.launchpad.net/ubuntu/+source/partman-efi/+bug/811485) you want to use FAT32. // However, you have to be careful that the cluster size needs to be greater or equal to the sector size, which // in turn has an impact on the minimum EFI partition size we can create (see ms_efi_size_MB in drive.c) if (SelectedDrive.SectorSize <= 1024) cluster_size = 1024; else if (SelectedDrive.SectorSize <= 4096) cluster_size = 4096; else // Go for broke cluster_size = (ULONG)SelectedDrive.SectorSize; fs_index = 1; // FAT32 task_number = 0; wVolumeName[0] = ms_efi[0]; // Boy do you *NOT* want to specify a label here, and spend HOURS figuring out why your EFI partition cannot boot... pfFormatEx(wVolumeName, SelectedDrive.MediaType, L"FAT32", L"", TRUE, cluster_size, FormatExCallback); if (IS_ERROR(FormatStatus)) { uprintf("Failed to format EFI partition"); AltUnmountVolume(ms_efi); return FALSE; } Sleep(200); } // We invoke the 'bcdboot' command from the host, as the one from the drive produces problems (#558) // Also, since Rufus should (usually) be running as a 32 bit app, on 64 bit systems, we need to use // 'C:\Windows\Sysnative' and not 'C:\Windows\System32' to invoke bcdboot, as 'C:\Windows\System32' // will get converted to 'C:\Windows\SysWOW64' behind the scenes, and ther is no bcdboot.exe there. uprintf("Enabling boot..."); static_sprintf(cmd, "%s\\bcdboot.exe %s\\Windows /v /f ALL /s %s", sysnative_dir, drive_name, (use_ms_efi)?ms_efi:drive_name); if (RunCommand(cmd, sysnative_dir, TRUE) != 0) { // Try to continue... but report a failure uprintf("Failed to enable boot using command '%s'", cmd); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT); } if (use_ms_efi) { Sleep(200); AltUnmountVolume(ms_efi); } PrintInfo(0, MSG_267, 99.9f); UpdateProgress(OP_DOS, 99.9f); // The following are non fatal if they fail #ifdef SET_INTERNAL_DRIVES_OFFLINE uprintf("Applying 'san_policy.xml', to set the target's internal drives offline..."); buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_TOGO_SAN_POLICY_XML), _RT_RCDATA, "san_policy.xml", &bufsize, FALSE); san_policy_path[0] = drive_name[0]; fd = fopenU(san_policy_path, "wb"); if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) { uprintf("Could not write '%s'\n", san_policy_path); if (fd) fclose(fd); } else { fclose(fd); // Can't use the one from the USB (at least for Windows 10 preview), as you'll get // "Error: 0x800401f0 An error occurred while initializing COM security". // On the other hand, using Windows 8.1 dism against Windows 10 doesn't work either // (you get a message about needing to upgrade to latest AIK)... static_sprintf(cmd, "dism /Image:%s\\ /Apply-Unattend:%s", drive_name, san_policy_path); if (RunCommand(cmd, NULL, TRUE) != 0) uprintf("Command '%s' failed to run", cmd); } #endif uprintf("Copying 'unattend.xml', to disable the use of the Windows Recovery Environment..."); buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_TOGO_UNATTEND_XML), _RT_RCDATA, "unattend.xml", &bufsize, FALSE); unattend_path[0] = drive_name[0]; fd = fopenU(unattend_path, "wb"); if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) { uprintf("Could not write '%s'\n", unattend_path); } fclose(fd); PrintInfo(0, MSG_267, 100.0f); UpdateProgress(OP_DOS, 100.0f); return TRUE; } /* * 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'. You will also get that popup if you start a * bad blocks check and cancel it before it completes. We have to close that popup manually. */ static 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(100); } ExitThread(0); } static void update_progress(const uint64_t processed_bytes) { if (_GetTickCount64() > LastRefresh + MAX_REFRESH) { LastRefresh = _GetTickCount64(); format_percent = (100.0f*processed_bytes)/(1.0f*img_report.projected_size); PrintInfo(0, MSG_261, format_percent); UpdateProgress(OP_FORMAT, format_percent); } } /* Write an image file or zero a drive */ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) { BOOL s, ret = FALSE; LARGE_INTEGER li; DWORD rSize, wSize, BufSize; uint64_t wb, target_size = hSourceImage?img_report.projected_size:SelectedDrive.DiskSize; uint8_t *buffer = NULL; int i; // We poked the MBR and other stuff, so we need to rewind li.QuadPart = 0; if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) uprintf("Warning: Unable to rewind image position - wrong data might be copied!"); LastRefresh = 0; if (img_report.compression_type != BLED_COMPRESSION_NONE) { uprintf("Writing Compressed Image..."); bled_init(_uprintf, update_progress, &FormatStatus); bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type); bled_exit(); } else { uprintf(hSourceImage?"Writing Image...":"Zeroing drive..."); // Our buffer size must be a multiple of the sector size and *ALIGNED* to the sector size BufSize = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; buffer = (uint8_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize); if (buffer == NULL) { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; uprintf("Could not allocate disk write buffer"); goto out; } // Sanity check if ((uintptr_t)buffer % SelectedDrive.SectorSize != 0) { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; uprintf("Write buffer is not aligned"); goto out; } // Don't bother trying for something clever, using double buffering overlapped and whatnot: // With Windows' default optimizations, sync read + sync write for sequential operations // will be as fast, if not faster, than whatever async scheme you can come up with. rSize = BufSize; for (wb = 0, wSize = 0; wb < (uint64_t)SelectedDrive.DiskSize; wb += wSize) { if (_GetTickCount64() > LastRefresh + MAX_REFRESH) { LastRefresh = _GetTickCount64(); format_percent = (100.0f*wb) / (1.0f*target_size); PrintInfo(0, hSourceImage?MSG_261:MSG_286, format_percent); UpdateProgress(OP_FORMAT, format_percent); } if (hSourceImage != NULL) { s = ReadFile(hSourceImage, buffer, BufSize, &rSize, NULL); if (!s) { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; uprintf("read error: %s", WindowsErrorString()); goto out; } if (rSize == 0) break; } // Don't overflow our projected size (mostly for VHDs) if (wb + rSize > target_size) { rSize = (DWORD)(target_size - wb); } // WriteFile fails unless the size is a multiple of sector size if (rSize % SelectedDrive.SectorSize != 0) rSize = ((rSize + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; for (i = 0; i < WRITE_RETRIES; i++) { CHECK_FOR_USER_CANCEL; s = WriteFile(hPhysicalDrive, buffer, rSize, &wSize, NULL); if ((s) && (wSize == rSize)) break; if (s) uprintf("write error: Wrote %d bytes, expected %d bytes\n", wSize, rSize); else uprintf("write error: %s", WindowsErrorString()); if (i < WRITE_RETRIES - 1) { li.QuadPart = wb; SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN); uprintf(" RETRYING...\n"); } else { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; goto out; } } if (i >= WRITE_RETRIES) goto out; } } RefreshDriveLayout(hPhysicalDrive); ret = TRUE; out: safe_mm_free(buffer); return ret; } /* * 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. */ DWORD WINAPI FormatThread(void* param) { int i, r, pt, tt, fs, bt; BOOL ret, use_large_fat32, windows_to_go; DWORD DriveIndex = (DWORD)(uintptr_t)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; HANDLE hSourceImage = INVALID_HANDLE_VALUE; SYSTEMTIME lt; FILE* log_fd; uint8_t *buffer = NULL, extra_partitions = 0; char *bb_msg, *guid_volume = NULL; char drive_name[] = "?:\\"; char drive_letters[27], fs_type[32]; char logfile[MAX_PATH], *userdir; char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; char kolibri_dst[] = "?:\\MTLD_F32"; char grub4dos_dst[] = "?:\\grldr"; PF_TYPE_DECL(WINAPI, LANGID, GetThreadUILanguage, (void)); PF_TYPE_DECL(WINAPI, LANGID, SetThreadUILanguage, (LANGID)); PF_INIT(GetThreadUILanguage, Kernel32); PF_INIT(SetThreadUILanguage, Kernel32); fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); bt = (int)ComboBox_GetItemData(hBootType, ComboBox_GetCurSel(hBootType)); pt = GETPARTTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); tt = GETTARGETTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); use_large_fat32 = (fs == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32)); windows_to_go = (togo_mode) && HAS_TOGO(img_report) && (Button_GetCheck(GetDlgItem(hMainDialog, IDC_WINDOWS_TO_GO)) == BST_CHECKED); // Find out if we need to add any extra partitions if ((windows_to_go) && (tt == TT_UEFI) && (pt == PARTITION_STYLE_GPT)) // According to Microsoft, every GPT disk (we RUN Windows from) must have an MSR due to not having hidden sectors // http://msdn.microsoft.com/en-us/library/windows/hardware/dn640535.aspx#gpt_faq_what_disk_require_msr extra_partitions = XP_MSR | XP_EFI; else if ( (fs == FS_NTFS) && ((bt == BT_UEFI_NTFS) || ((bt == BT_ISO) && (img_report.has_efi) && ((tt == TT_UEFI) || (windows_to_go) || (allow_dual_uefi_bios)))) ) extra_partitions = XP_UEFI_NTFS; else if (IsChecked(IDC_EXTRA_PARTITION)) extra_partitions = XP_COMPAT; PrintInfoDebug(0, MSG_225); hPhysicalDrive = GetPhysicalHandle(DriveIndex, TRUE, lock_drive); 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 (!GetDriveLetters(DriveIndex, drive_letters)) { uprintf("Failed to get a drive letter\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); goto out; } if (drive_letters[0] == 0) { uprintf("No drive letter was assigned...\n"); drive_name[0] = GetUnusedDriveLetter(); if (drive_name[0] == 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 { // Unmount all mounted volumes that belong to this drive // Do it in reverse so that we always end on the first volume letter for (i=(int)safe_strlen(drive_letters); i>0; i--) { drive_name[0] = drive_letters[i-1]; if (IsChecked(IDC_BOOT) && ((bt == BT_ISO) || (bt == BT_IMG))) { // If we are using an image, check that it isn't located on the drive we are trying to format if ((PathGetDriveNumberU(image_path) + 'A') == drive_letters[i-1]) { uprintf("ABORTED: Cannot use an image that is located on the target drive!\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; goto out; } } 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; if (!zero_drive) { PrintInfoDebug(0, MSG_226); AnalyzeMBR(hPhysicalDrive, "Drive"); if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) { AnalyzePBR(hLogicalVolume); } UpdateProgress(OP_ANALYZE_MBR, -1.0f); } if (zero_drive) { WriteDrive(hPhysicalDrive, NULL); goto out; } // Zap any existing partitions. This helps prevent access errors. // Note, Microsoft's way of cleaning partitions (IOCTL_DISK_CREATE_DISK, which is what we apply // in InitializeDisk) is *NOT ENOUGH* to reset a disk and can render it inoperable for partitioning // or formatting under Windows. See https://github.com/pbatard/rufus/issues/759 for details. if ((!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, FALSE)) || (!InitializeDisk(hPhysicalDrive)) ) { uprintf("Could not reset partitions\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } CreateThread(NULL, 0, CloseFormatPromptThread, NULL, 0, NULL); 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.SectorSize, 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.SectorSize, FALSE); fclose(log_fd); _unlink(logfile); goto out; } uprintf("Bad Blocks: Check completed, %d 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.bb_count, report.num_read_errors, report.num_write_errors, report.num_corruption_errors); #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-security" #endif fprintf(log_fd, bb_msg); #if defined(__clang__) #pragma clang diagnostic pop #endif 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 = MessageBoxExU(hMainDialog, lmprintf(MSG_012, bb_msg, logfile), lmprintf(MSG_010), MB_ABORTRETRYIGNORE|MB_ICONWARNING|MB_IS_RTL, selected_langid); } 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; } // 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.SectorSize, use_large_fat32)) { uprintf("unable to zero MBR/GPT\n"); if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } } // Write an image file if (IsChecked(IDC_BOOT) && (bt == BT_IMG)) { hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hSourceImage == INVALID_HANDLE_VALUE) { uprintf("Could not open image '%s': %s", image_path, WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } WriteDrive(hPhysicalDrive, hSourceImage); // If the image contains a partition we might be able to access, try to re-mount it safe_unlockclose(hPhysicalDrive); safe_unlockclose(hLogicalVolume); Sleep(200); WaitForLogical(DriveIndex); if (GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_type, sizeof(fs_type), TRUE)) { guid_volume = GetLogicalName(DriveIndex, TRUE, TRUE); if ((guid_volume != NULL) && (MountVolume(drive_name, guid_volume))) uprintf("Remounted %s on %s\n", guid_volume, drive_name); } goto out; } UpdateProgress(OP_ZERO_MBR, -1.0f); CHECK_FOR_USER_CANCEL; if (!CreatePartition(hPhysicalDrive, pt, fs, (pt==PARTITION_STYLE_MBR) && (tt==TT_UEFI), extra_partitions)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } UpdateProgress(OP_PARTITION, -1.0f); // Close the (unmounted) volume before formatting if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) { PrintInfoDebug(0, 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; // Wait for the logical drive we just created to appear uprintf("Waiting for logical drive to reappear...\n"); Sleep(200); if (!WaitForLogical(DriveIndex)) uprintf("Logical drive was not found!"); // 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, TRUE)); goto out; } // Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted if (pt == PARTITION_STYLE_MBR) { PrintInfoDebug(0, MSG_228); // "Writing master boot record..." if ((!WriteMBR(hPhysicalDrive)) || (!WriteSBR(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; // Refresh the drive label - This is needed as Windows may have altered it from // the name we proposed, and we require an exact label, to patch config files. if (!GetVolumeInformationU(drive_name, img_report.usb_label, ARRAYSIZE(img_report.usb_label), NULL, NULL, NULL, NULL, 0)) { uprintf("Warning: Failed to refresh label: %s", WindowsErrorString()); } if (IsChecked(IDC_BOOT)) { if (bt == BT_UEFI_NTFS) { // All good } else if (tt == TT_UEFI) { // For once, no need to do anything - just check our sanity if ( (bt != BT_ISO) || (!img_report.has_efi) || (fs > FS_NTFS) ) { uprintf("Spock gone crazy error in %s:%d", __FILE__, __LINE__); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; goto out; } } else if ( (bt == BT_SYSLINUX_V4) || (bt == BT_SYSLINUX_V6) || ((bt == BT_ISO) && (HAS_SYSLINUX(img_report) || IS_REACTOS(img_report)) && (!allow_dual_uefi_bios) && (IS_FAT(fs))) ) { if (!InstallSyslinux(DriveIndex, drive_name[0], fs)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; } } else { // 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 PrintInfoDebug(0, 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 (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 ((bt == BT_MSDOS) || (bt == BT_FREEDOS)) { UpdateProgress(OP_DOS, -1.0f); PrintInfoDebug(0, MSG_230); if (!ExtractDOS(drive_name)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; goto out; } } else if (bt == BT_GRUB4DOS) { grub4dos_dst[0] = drive_name[0]; IGNORE_RETVAL(_chdirU(app_dir)); uprintf("Installing: %s (Grub4DOS loader) %s\n", grub4dos_dst, IsFileInDB(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr")?"✓":"✗"); if (!CopyFileU(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr", grub4dos_dst, FALSE)) uprintf("Failed to copy file: %s", WindowsErrorString()); } else if ((bt == BT_ISO) && (image_path != NULL)) { UpdateProgress(OP_DOS, 0.0f); drive_name[2] = 0; // Ensure our drive is something like 'D:' if (windows_to_go) { PrintInfoDebug(0, MSG_268); if (!SetupWinToGo(drive_name, (extra_partitions & XP_EFI))) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); goto out; } } else { PrintInfoDebug(0, MSG_231); if (!ExtractISO(image_path, drive_name, FALSE)) { if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); goto out; } if (img_report.has_kolibrios) { kolibri_dst[0] = drive_name[0]; uprintf("Installing: %s (KolibriOS loader)\n", kolibri_dst); if (ExtractISOFile(image_path, "HD_Load/USB_Boot/MTLD_F32", kolibri_dst, FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM) == 0) { uprintf("Warning: loader installation failed - KolibriOS will not boot!\n"); } } // EFI mode selected, with no 'boot###.efi' but Windows 7 x64's 'bootmgr.efi' (bit #0) if ((tt == TT_UEFI) && IS_WIN7_EFI(img_report)) { PrintInfoDebug(0, MSG_232); img_report.install_wim_path[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(img_report.install_wim_path, 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 ( (tt == TT_BIOS) && (IS_WINPE(img_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); PrintInfoDebug(0, 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 ((bt == BT_ISO) && (fs == FS_NTFS)) { // Try to ensure that all messages from Checkdisk will be in English if ((pfGetThreadUILanguage != NULL) && (PRIMARYLANGID(pfGetThreadUILanguage()) != LANG_ENGLISH)) { pfSetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); if (PRIMARYLANGID(pfGetThreadUILanguage()) != LANG_ENGLISH) uprintf("Note: CheckDisk messages may be localized"); } CheckDisk(drive_name[0]); UpdateProgress(OP_FINALIZE, -1.0f); } } out: zero_drive = FALSE; safe_free(guid_volume); safe_free(buffer); safe_closehandle(hSourceImage); safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); // This can take a while if (IS_ERROR(FormatStatus)) { guid_volume = GetLogicalName(DriveIndex, TRUE, FALSE); if (guid_volume != NULL) { if (MountVolume(drive_name, guid_volume)) uprintf("Re-mounted volume as '%c:' after error\n", drive_name[0]); free(guid_volume); } } PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); ExitThread(0); } DWORD WINAPI SaveImageThread(void* param) { BOOL s; DWORD rSize, wSize; VHD_SAVE *vhd_save = (VHD_SAVE*)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hDestImage = INVALID_HANDLE_VALUE; LARGE_INTEGER li; uint8_t *buffer = NULL; uint64_t wb; int i; PrintInfoDebug(0, MSG_225); LastRefresh = 0; hPhysicalDrive = GetPhysicalHandle(vhd_save->DeviceNum, FALSE, TRUE); if (hPhysicalDrive == INVALID_HANDLE_VALUE) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } // Write an image file // We poked the MBR and other stuff, so we need to rewind li.QuadPart = 0; if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) uprintf("Warning: Unable to rewind device position - wrong data might be copied!"); hDestImage = CreateFileU(vhd_save->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hDestImage == INVALID_HANDLE_VALUE) { uprintf("Could not open image '%s': %s", vhd_save->path, WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } uprintf("Saving to image '%s'...", vhd_save->path); buffer = (uint8_t*)_mm_malloc(DD_BUFFER_SIZE, 16); if (buffer == NULL) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY; uprintf("could not allocate buffer"); goto out; } // Don't bother trying for something clever, using double buffering overlapped and whatnot: // With Windows' default optimizations, sync read + sync write for sequential operations // will be as fast, if not faster, than whatever async scheme you can come up with. for (wb = 0; ; wb += wSize) { s = ReadFile(hPhysicalDrive, buffer, (DWORD)MIN(DD_BUFFER_SIZE, SelectedDrive.DiskSize - wb), &rSize, NULL); if (!s) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT; uprintf("read error: %s", WindowsErrorString()); goto out; } if (rSize == 0) break; if (_GetTickCount64() > LastRefresh + MAX_REFRESH) { LastRefresh = _GetTickCount64(); format_percent = (100.0f*wb)/(1.0f*SelectedDrive.DiskSize); PrintInfo(0, MSG_261, format_percent); UpdateProgress(OP_FORMAT, format_percent); } for (i=0; i= WRITE_RETRIES) goto out; } if (wb != SelectedDrive.DiskSize) { uprintf("Error: wrote %" PRIu64 " bytes, expected %" PRIu64, wb, SelectedDrive.DiskSize); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } uprintf("%" PRIu64 " bytes written", wb); uprintf("Appending VHD footer..."); if (!AppendVHDFooter(vhd_save->path)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } uprintf("Done"); out: safe_free(vhd_save->path); safe_mm_free(buffer); safe_closehandle(hDestImage); safe_unlockclose(hPhysicalDrive); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); ExitThread(0); }