/* * Rufus: The Reliable USB Formatting Utility * Formatting function calls * Copyright (c) 2011-2012 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; 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 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)) { // TODO: amend this is not all Win2k3 setupldr.bin use the same header 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(1) disk masquerading // TODO: only the first one seems to be needed if (safe_strnicmp(&buf[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) { buf[i+6] = '1'; uprintf(" 0x%08X: '%s' -> 'rdisk(1)'\n", i, rdisk_zero); } // $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, drive_name); } else { uprintf("Failed to remount %s on %s\n", drive_guid, 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) { 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; FILE* log_fd; int r, fs, dt; 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); ClearMBR(hPhysicalDrive); 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 completely // before repartitioning. Else, all kind of bad things happen if (!ClearMBR(hPhysicalDrive)) { uprintf("unable to zero MBR\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } UpdateProgress(OP_ZERO_MBR, -1.0f); if (!CreatePartition(hPhysicalDrive)) { 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 (!FormatDrive(drive_name[0])) { // Error will be set by FormatDrive() in FormatStatus uprintf("Format error: %s\n", StrError(FormatStatus)); goto out; } 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); fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); dt = (int)ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType)); if (IsChecked(IDC_DOS)) { 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_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_DOS)) { 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 (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); }