/* * 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 "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); // uprintf("%d percent completed.\n", *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)); } /* * Call on fmifs.dll's FormatEx() to format the drive */ static BOOL FormatDrive(char DriveLetter) { BOOL r = FALSE; PF_DECL(FormatEx); WCHAR wDriveRoot[] = L"?:\\"; WCHAR wFSType[32]; WCHAR wLabel[128]; size_t i; wDriveRoot[0] = (WCHAR)DriveLetter; PrintStatus(0, TRUE, "Formatting..."); PF_INIT_OR_OUT(FormatEx, fmifs); GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType)); // We may have a " (Default)" trail for (i=0; i delete the log file // NB: the log doesn't get deleted on abort 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)) { // Errorcode has already been set goto out; } UpdateProgress(OP_FIX_MBR, -1.0f); if (IsChecked(IDC_DOS)) { switch (ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType))) { case DT_FREEDOS: case DT_WINME: case DT_ISO: // 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; } // Make sure the volume is dismounted before writing the PBR // This is especially critical, as NTFS will only be made bootable // after the FS is re-mounted by Windows UnmountDrive(hLogicalVolume); PrintStatus(0, TRUE, "Writing partition boot record..."); if (!WritePBR(hLogicalVolume, ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType)) == DT_FREEDOS)) { // Errorcode has already been set goto out; } // We must close and unlock the volume to write files to it safe_unlockclose(hLogicalVolume); UpdateProgress(OP_DOS, -1.0f); if (ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType)) != DT_ISO) { 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 (iso_path != NULL) { PrintStatus(0, TRUE, "Copying ISO files..."); drive_name[2] = 0; if ( (!ExtractISO(iso_path, drive_name, FALSE)) && (!FormatStatus)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; } drive_name[2] = '\\'; } break; // Syslinux requires patching of the PBR after the files have been extracted case DT_SYSLINUX: if (!InstallSyslinux(num, drive_name)) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; } break; } } // TODO: the only way to properly recover from a cancel will be through a device reset // We issue a complete remount of the filesystem at the end 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 (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); } } else { uprintf("Could not remount %s %s\n", drive_name, WindowsErrorString()); } } out: safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, 0, 0); ExitThread(0); }