/* * Rufus: The Reliable USB Formatting Utility * Formatting function calls * Copyright (c) 2007-2009 Tom Thornhill/Ridgecrop * Copyright (c) 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" /* * Globals */ DWORD FormatStatus; badblocks_report report; static float format_percent = 0.0f; static int task_number = 0; /* Number of steps for each FS for FCC_STRUCTURE_PROGRESS */ const int nb_steps[FS_MAX] = { 5, 5, 12, 10 }; static int fs_index = 0; /* * 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, "Formatting: %d%% completed.", *percent); UpdateProgress(OP_FORMAT, 1.0f * (*percent)); break; case FCC_STRUCTURE_PROGRESS: // No progress on quick format PrintStatus(0, TRUE, "Creating file system: Task %d/%d completed.", ++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, "Creating file system: Task %d/%d completed.", 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, "NTFS Fixup: %d%% completed.", *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 32 GB FAT32 static BOOL FormatFAT32(DWORD DriveIndex) { BOOL r = FALSE; char DriveLetter; DWORD i; HANDLE hLogicalVolume; DWORD cbRet; DISK_GEOMETRY dgDrive; PARTITION_INFORMATION piDrive; // Recommended values DWORD ReservedSectCount = 32; DWORD NumFATs = 2; DWORD BackupBootSect = 6; DWORD VolumeId = 0; // calculated before format WCHAR wLabel[64], wDriveName[] = L"#:\\"; DWORD BurstSize = 128; // Zero in blocks of 64K typically // Calculated later DWORD FatSize = 0; DWORD BytesPerSect = 0; DWORD ClusterSize = 0; DWORD SectorsPerCluster = 0; DWORD TotalSectors = 0; DWORD SystemAreaSize = 0; DWORD UserAreaSize = 0; ULONGLONG qTotalSectors = 0; // Structures to be written to the disk FAT_BOOTSECTOR32 *pFAT32BootSect = NULL; FAT_FSINFO *pFAT32FsInfo = NULL; DWORD *pFirstSectOfFat = NULL; BYTE* pZeroSect = NULL; char VolId[12] = "NO NAME "; // Debug temp vars ULONGLONG FatNeeded, ClusterCount; PrintStatus(0, TRUE, "Formatting..."); uprintf("Using large FAT32 format method\n"); VolumeId = GetVolumeID(); // Open the drive (volume should already be locked) hLogicalVolume = GetDriveHandle(DriveIndex, &DriveLetter, TRUE, FALSE); if (IS_ERROR(FormatStatus)) goto out; if (hLogicalVolume == INVALID_HANDLE_VALUE) die("Could not access logical volume\n", ERROR_OPEN_FAILED); // Make sure we get exclusive access if (!UnmountDrive(hLogicalVolume)) return r; // Work out drive params if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dgDrive, sizeof(dgDrive), &cbRet, NULL)) { die("Failed to get device geometry\n", ERROR_NOT_SUPPORTED); } if (IS_ERROR(FormatStatus)) goto out; if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &piDrive, sizeof(piDrive), &cbRet, NULL)) { die("Failed to get parition info\n", ERROR_NOT_SUPPORTED); } 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 // í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' // zero'd 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 commited - print some info first uprintf("Size : %gGB %u sectors\n", (double) (piDrive.PartitionLength.QuadPart / (1000*1000*1000)), 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 resered, 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) { format_percent = (100.0f*i)/(1.0f*(SystemAreaSize+BurstSize)); PrintStatus(0, FALSE, "Formatting: %d%% completed.", (int)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 ("Initialising 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; } /* * Issue a complete remount of the volume */ static BOOL RemountVolume(char drive_letter) { char drive_guid[50]; char drive_name[] = "?:\\"; drive_name[0] = drive_letter; if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) { if (DeleteVolumeMountPointA(drive_name)) { Sleep(200); if (SetVolumeMountPointA(drive_name, drive_guid)) { uprintf("Successfully remounted %s on %s\n", &drive_guid[4], drive_name); } else { uprintf("Failed to remount %s on %s\n", &drive_guid[4], drive_name); // This will leave the drive unaccessible and must be flagged as an error FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); return FALSE; } } else { uprintf("Could not remount %s %s\n", drive_name, WindowsErrorString()); // Try to continue regardless } } return TRUE; } /* * Standalone thread for the formatting operation */ DWORD WINAPI FormatThread(LPVOID param) { int r, pt, bt, fs, dt; BOOL ret; DWORD num = (DWORD)(uintptr_t)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; SYSTEMTIME lt; char drive_name[] = "?:\\"; char bb_msg[512]; 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))); hPhysicalDrive = GetDriveHandle(num, NULL, 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... // ... but we can't write sectors that are part of a volume, even if we have // access to physical, unless we have a lock (which doesn't have to be write) // Also, having a volume handle allows us to unmount the volume hLogicalVolume = GetDriveHandle(num, drive_name, 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; } UnmountDrive(hLogicalVolume); AnalyzeMBR(hPhysicalDrive); AnalyzePBR(hLogicalVolume); if (IsChecked(IDC_BADBLOCKS)) { do { // create a log file for bad blocks report. Since %USERPROFILE% may // have localised 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, "Rufus 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 (!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) { safe_sprintf(bb_msg, sizeof(bb_msg), "Check completed: %u bad block%s found.\n" " %d read errors\n %d write errors\n %d corruption errors\n", report.bb_count, (report.bb_count==1)?"":"s", report.num_read_errors, report.num_write_errors, report.num_corruption_errors); fprintf(log_fd, "%s", bb_msg); GetLocalTime(<); fprintf(log_fd, "Rufus 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); safe_sprintf(&bb_msg[strlen(bb_msg)], sizeof(bb_msg)-strlen(bb_msg)-1, "\nA more detailed report can be found in:\n%s\n", logfile); r = MessageBoxU(hMainDialog, bb_msg, "Bad blocks found", 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, but keep the lock safe_closehandle(hLogicalVolume); // Especially after destructive badblocks test, you must zero the MBR/GPT completely // before repartitioning. Else, all kind of bad things can happen. if (!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.Geometry.BytesPerSector)) { uprintf("unable to zero MBR/GPT\n"); if (!FormatStatus) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } UpdateProgress(OP_ZERO_MBR, -1.0f); if (!CreatePartition(hPhysicalDrive, pt, fs)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } UpdateProgress(OP_PARTITION, -1.0f); // Add a small delay after partitioning to be safe Sleep(200); // If FAT32 is requested and we have a large drive (>32 GB) use // large FAT32 format, else use MS's FormatEx. ret = ((fs == FS_FAT32) && (SelectedDrive.DiskSize > LARGE_FAT32_SIZE))? FormatFAT32(num):FormatDrive(drive_name[0]); if (!ret) { // Error will be set by FormatDrive() in FormatStatus uprintf("Format error: %s\n", StrError(FormatStatus)); goto out; } if (pt == PARTITION_STYLE_MBR) { PrintStatus(0, TRUE, "Writing master boot record..."); if (!WriteMBR(hPhysicalDrive)) { if (!FormatStatus) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } UpdateProgress(OP_FIX_MBR, -1.0f); } 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) || ((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 = GetDriveHandle(num, drive_name, TRUE, FALSE); if (hLogicalVolume == INVALID_HANDLE_VALUE) { 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, "Writing partition boot record..."); if (!WritePBR(hLogicalVolume)) { if (!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) || ((dt == DT_ISO) && ((fs == FS_FAT16) || (fs == FS_FAT32))) ) { PrintStatus(0, TRUE, "Installing Syslinux..."); if (!InstallSyslinux(num, drive_name)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; } } } else { if (IsChecked(IDC_SET_ICON)) SetAutorun(drive_name); } // 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[0])) goto out; if (IsChecked(IDC_BOOT)) { if ((dt == DT_WINME) || (dt == DT_FREEDOS)) { UpdateProgress(OP_DOS, -1.0f); PrintStatus(0, TRUE, "Copying DOS files..."); if (!ExtractDOS(drive_name)) { if (!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, "Copying ISO files..."); drive_name[2] = 0; if (!ExtractISO(iso_path, drive_name, FALSE)) { if (!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: progress PrintStatus(0, TRUE, "Win7 EFI boot setup (this may take a while)..."); 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, "Finalizing..."); if (IsChecked(IDC_SET_ICON)) SetAutorun(drive_name); // Issue another complete remount before we exit, to ensure we're clean RemountVolume(drive_name[0]); // 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: SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, 0, 0); ExitThread(0); }