misc] move formatting functions into their own source

* also add missing #pragma once in msapi_utf8.h
This commit is contained in:
Pete Batard 2011-12-01 17:20:52 +00:00
parent bc252400a1
commit 79f3e78ec3
11 changed files with 644 additions and 569 deletions

441
rufus.c
View file

@ -40,26 +40,20 @@
#include <io.h>
// http://git.kernel.org/?p=fs/ext2/e2fsprogs.git;a=blob;f=misc/badblocks.c
// http://ms-sys.sourceforge.net/
// http://thestarman.pcministry.com/asm/mbr/MSWIN41.htm
// http://www.c-jump.com/CIS24/Slides/FAT/lecture.html#F01_0130_sector_assignments
#include "msapi_utf8.h"
#include "resource.h"
#include "rufus.h"
#include "sys_types.h"
#include "br.h"
#include "fat16.h"
#include "fat32.h"
#include "file.h"
#if !defined(GUID_DEVINTERFACE_DISK)
const GUID GUID_DEVINTERFACE_DISK = { 0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b} };
#endif
const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" };
static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" };
// Don't ask me - just following the MS standard here
const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes",
static const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes",
"16 kilobytes", "32 kilobytes", "64 kilobytes", "128 kilobytes", "256 kilobytes", "512 kilobytes",
"1024 kilobytes","2048 kilobytes","4096 kilobytes","8192 kilobytes","16 megabytes","32 megabytes" };
@ -81,27 +75,14 @@ HWND hStatus;
float fScale = 1.0f;
int default_fs;
ULONG default_clutersize;
RUFUS_DRIVE_INFO SelectedDrive;
BOOL bBootable;
BOOL bQuickFormat;
struct {
DWORD DeviceNumber;
LONGLONG DiskSize;
DISK_GEOMETRY Geometry;
DWORD FirstSector;
int FSType;
struct {
ULONG Allowed;
ULONG Default;
} ClusterSize[FS_MAX];
} SelectedDrive;
static HWND hDeviceList, hCapacity, hFileSystem, hClusterSize, hLabel;
DWORD FormatStatus;
HWND hDeviceList, hCapacity, hFileSystem, hClusterSize, hLabel;
static HWND hDeviceTooltip = NULL, hFSTooltip = NULL;
static StrArray DriveID, DriveLabel;
static DWORD FormatStatus;
static StrArray DriveID, DriveLabel;
/*
* Convert a partition type to its human readable form using
@ -119,7 +100,7 @@ static const char* GetPartitionType(BYTE Type)
/*
* Open a drive with optional write access - returns a drive HANDLE and the drive letter
* or INVALID_HANDLE_VALUE (/!\ which is != NULL /!\) on failure
* or INVALID_HANDLE_VALUE (/!\ which is DIFFERENT from NULL /!\) on failure
* This call is quite risky (left unchecked, inadvertently passing 0 as index would
* return a handle to C:, which we might then proceed to unknowingly repartition!),
* so we apply the following mitigation factors:
@ -128,7 +109,7 @@ static const char* GetPartitionType(BYTE Type)
* typically be the case on C:\ or any other drive in use, we report failure
* - We report the full path of any drive that was successfully opened for write acces
*/
static HANDLE GetDriveHandle(DWORD DriveIndex, char* DriveLetter, BOOL bWriteAccess, BOOL bLockDrive)
HANDLE GetDriveHandle(DWORD DriveIndex, char* DriveLetter, BOOL bWriteAccess, BOOL bLockDrive)
{
BOOL r;
DWORD size;
@ -212,12 +193,6 @@ out:
return hDrive;
}
static __inline BOOL UnlockDrive(HANDLE hDrive)
{
DWORD size;
return DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &size, NULL);
}
/*
* Return the drive letter and volume label
*/
@ -242,12 +217,10 @@ static BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label)
return TRUE;
}
#define KB 1024LL
#define MB 1048576LL
#define GB 1073741824LL
#define TB 1099511627776LL
/*
* Set cluster size values according to http://support.microsoft.com/kb/140365
* this call will return FALSE if we can't find a supportable FS for the drive
@ -559,7 +532,7 @@ static BOOL PopulateProperties(int ComboIndex)
/*
* Create a partition table
*/
static BOOL CreatePartition(HANDLE hDrive)
BOOL CreatePartition(HANDLE hDrive)
{
BYTE layout[sizeof(DRIVE_LAYOUT_INFORMATION_EX) + 3*sizeof(PARTITION_INFORMATION_EX)] = {0};
PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)layout;
@ -605,402 +578,6 @@ static BOOL CreatePartition(HANDLE hDrive)
return TRUE;
}
/*
* FormatEx callback. Return FALSE to halt operations
*/
static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
{
DWORD* percent;
int task_number = 0;
if (IS_ERROR(FormatStatus))
return FALSE;
switch(Command) {
case FCC_PROGRESS:
percent = (DWORD*)pData;
PostMessage(hMainDialog, UM_FORMAT_PROGRESS, (WPARAM)*percent, (LPARAM)0);
uprintf("%d percent completed.\n", *percent);
break;
case FCC_STRUCTURE_PROGRESS: // No progress on quick format
uprintf("Format task %d/? completed.\n", ++task_number);
break;
case FCC_DONE:
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 things: Action=%d pData=%0p\n", Action, pData);
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)|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)|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)|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)|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("Formatting...");
PF_INIT_OR_OUT(FormatEx, fmifs);
// TODO: properly set MediaType
GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType));
// We may have a " (Default)" trail
for (i=0; i<wcslen(wFSType); i++) {
if (wFSType[i] == ' ') {
uprintf("removed %d\n", i);
wFSType[i] = 0;
break;
}
}
GetWindowTextW(hLabel, wLabel, ARRAYSIZE(wLabel));
uprintf("Using cluster size: %d bytes\n", ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)));
pfFormatEx(wDriveRoot, RemovableMedia, wFSType, wLabel,
IsChecked(IDC_QUICKFORMAT), (ULONG)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)),
FormatExCallback);
if (!IS_ERROR(FormatStatus)) {
uprintf("Format completed.\n");
r = TRUE;
}
out:
return r;
}
static BOOL AnalyzeMBR(HANDLE hPhysicalDrive)
{
FILE fake_fd;
fake_fd._ptr = (char*)hPhysicalDrive;
fake_fd._bufsiz = SelectedDrive.Geometry.BytesPerSector;
// TODO: Apply this detection before partitioning
// TODO: since we detect all these, might as well give some MBR choice to the user?
if (is_br(&fake_fd)) {
uprintf("Drive has an x86 boot sector\n");
} else{
uprintf("Drive is missing an x86 boot sector!\n");
return FALSE;
}
// TODO: Add/Eliminate FAT12?
if (is_fat_16_br(&fake_fd) || is_fat_32_br(&fake_fd)) {
if (entire_fat_16_br_matches(&fake_fd)) {
uprintf("Exact FAT16 DOS boot record match\n");
} else if (entire_fat_16_fd_br_matches(&fake_fd)) {
uprintf("Exact FAT16 FreeDOS boot record match\n");
} else if (entire_fat_32_br_matches(&fake_fd)) {
uprintf("Exact FAT32 DOS boot record match\n");
} else if (entire_fat_32_nt_br_matches(&fake_fd)) {
uprintf("Exact FAT32 NT boot record match\n");
} else if (entire_fat_32_fd_br_matches(&fake_fd)) {
uprintf("Exactly FAT32 FreeDOS boot record match\n");
} else {
uprintf("Unknown FAT16 or FAT32 boot record\n");
}
} else if (is_dos_mbr(&fake_fd)) {
uprintf("Microsoft DOS/NT/95A master boot record match\n");
} else if (is_dos_f2_mbr(&fake_fd)) {
uprintf("Microsoft DOS/NT/95A master boot record with the undocumented\n");
uprintf("F2 instruction match\n");
} else if (is_95b_mbr(&fake_fd)) {
uprintf("Microsoft 95B/98/98SE/ME master boot record match\n");
} else if (is_2000_mbr(&fake_fd)) {
uprintf("Microsoft 2000/XP/2003 master boot record match\n");
} else if (is_vista_mbr(&fake_fd)) {
uprintf("Microsoft Vista master boot record match\n");
} else if (is_win7_mbr(&fake_fd)) {
uprintf("Microsoft 7 master boot record match\n");
} else if (is_zero_mbr(&fake_fd)) {
uprintf("Zeroed non-bootable master boot record match\n");
} else {
uprintf("Unknown boot record\n");
}
return TRUE;
}
/*
* Process the MBR
*/
static BOOL ProcessMBR(HANDLE hPhysicalDrive)
{
BOOL r = FALSE;
unsigned char* buf = NULL;
size_t SecSize = SelectedDrive.Geometry.BytesPerSector;
size_t nSecs = (0x200 + SecSize -1) / SecSize;
FILE fake_fd;
if (!AnalyzeMBR(hPhysicalDrive)) return FALSE;
// FormatEx rewrites the MBR and removes the LBA attribute of FAT16
// and FAT32 partitions - we need to correct this in the MBR
// TODO: something else for bootable GPT
buf = (unsigned char*)malloc(SecSize * nSecs);
if (buf == NULL) {
uprintf("Could not allocate memory for MBR");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
if (!read_sectors(hPhysicalDrive, SelectedDrive.Geometry.BytesPerSector, 0, nSecs, buf, SecSize)) {
uprintf("Could not read MBR\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
goto out;
}
// DumpBufferHex(buf, 0x200);
switch (ComboBox_GetCurSel(hFileSystem)) {
// TODO: check for 0x06 & 0x0b?
case FS_FAT16:
buf[0x1c2] = 0x0e;
break;
case FS_FAT32:
buf[0x1c2] = 0x0c;
break;
}
if (IsChecked(IDC_DOSSTARTUP)) {
buf[0x1be] = 0x80; // Set first partition bootable
}
if (!write_sectors(hPhysicalDrive, SelectedDrive.Geometry.BytesPerSector, 0, nSecs, buf, SecSize*nSecs)) {
uprintf("Could not write MBR\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
goto out;
}
fake_fd._ptr = (char*)hPhysicalDrive;
fake_fd._bufsiz = SelectedDrive.Geometry.BytesPerSector;
r = write_95b_mbr(&fake_fd);
if (!read_sectors(hPhysicalDrive, SelectedDrive.Geometry.BytesPerSector, 0, nSecs, buf, SecSize)) {
uprintf("Could not re-read MBR\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
goto out;
}
DumpBufferHex(buf, 0x200);
out:
safe_free(buf);
return r;
}
static BOOL ProcessFS_BR(HANDLE hLogicalVolume)
{
BOOL r = FALSE;
unsigned char* buf = NULL;
FILE fake_fd;
size_t SecSize = SelectedDrive.Geometry.BytesPerSector;
size_t nSecs = (0x400 + SecSize -1) / SecSize;
fake_fd._ptr = (char*)hLogicalVolume;
fake_fd._bufsiz = SelectedDrive.Geometry.BytesPerSector;
write_fat_32_br(&fake_fd, 0);
// FormatEx rewrites the MBR and removes the LBA attribute of FAT16
// and FAT32 partitions - we need to correct this in the MBR
// TODO: something else for bootable GPT
buf = (unsigned char*)malloc(SecSize * nSecs);
if (buf == NULL) {
uprintf("Could not allocate memory for FS BR");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
if (!read_sectors(hLogicalVolume, SelectedDrive.Geometry.BytesPerSector, 0, nSecs, buf, SecSize*nSecs)) {
uprintf("Could not read FS BR\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
goto out;
}
uprintf("FS_BR:\n");
DumpBufferHex(buf, 0x400);
out:
safe_free(buf);
return r;
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562%28v=vs.85%29.aspx
Dismounting a volume is useful when a volume needs to disappear for a while. For
example, an application that changes a volume file system from the FAT file system
to the NTFS file system might use the following procedure.
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.
A dismounting operation removes the volume from the FAT file system awareness.
When the operating system mounts the volume, it appears as an NTFS file system volume.
*/
/*
* Standalone thread for the formatting operation
*/
static void __cdecl FormatThread(void* param)
{
DWORD num = (DWORD)(uintptr_t)param;
HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE;
HANDLE hLogicalVolume = INVALID_HANDLE_VALUE;
char drive_name[] = "?:";
int i;
// DWORD size;
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
if (!CreatePartition(hPhysicalDrive)) {
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE;
goto out;
}
// Make sure we can access the volume again before trying to format it
for (i=0; i<10; i++) {
Sleep(500);
hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, TRUE);
if (hLogicalVolume != INVALID_HANDLE_VALUE) {
break;
}
}
if (i >= 10) {
uprintf("Could not access volume after partitioning\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
goto out;
}
// Handle needs to be closed for FormatEx to be happy - we keep a lock though
safe_closehandle(hLogicalVolume);
if (!FormatDrive(drive_name[0])) {
// Error will be set by FormatDrive() in FormatStatus
uprintf("Format error: 0x%08X\n", FormatStatus);
goto out;
}
// TODO: Enable compression on NTFS
// TODO: optionally disable indexing on NTFS
// TODO: use progress bar during MBR/FSBR/MSDOS copy
// Ideally we would lock, FSCTL_DISMOUNT_VOLUME, unlock and close our volume
// handle, but some explorer versions have problems with volumes disappear
// #define VOL_DISMOUNT
#ifdef VOL_DISMOUNT
// Dismount the volume
hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, TRUE);
if (hLogicalVolume == INVALID_HANDLE_VALUE) {
uprintf("Could not open the volume for dismount\n");
goto out;
}
if (!DeviceIoControl(hLogicalVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &size, NULL)) {
uprintf("Could not dismount volume\n");
goto out;
}
#endif
PrintStatus("Writing master boot record...\n");
if (!ProcessMBR(hPhysicalDrive)) {
// Errorcode has already been set
goto out;
}
#ifdef VOL_DISMOUNT
safe_unlockclose(hLogicalVolume);
// Sleep(10000);
hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, FALSE);
if (hLogicalVolume == INVALID_HANDLE_VALUE) {
uprintf("Could not re-mount volume\n");
goto out;
}
#endif
if (IsChecked(IDC_DOSSTARTUP)) {
hLogicalVolume = GetDriveHandle(num, drive_name, TRUE, FALSE);
if (hLogicalVolume == INVALID_HANDLE_VALUE) {
uprintf("Could not re-mount volume\n");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
goto out;
}
PrintStatus("Writing filesystem boot record...\n");
if (!ProcessFS_BR(hLogicalVolume)) {
// Errorcode has already been set
goto out;
}
PrintStatus("Copying MS-DOS files...\n");
if (!ExtractMSDOS(drive_name)) {
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
goto out;
}
}
out:
safe_unlockclose(hLogicalVolume);
safe_unlockclose(hPhysicalDrive);
PostMessage(hMainDialog, UM_FORMAT_COMPLETED, 0, 0);
_endthread();
}
/*
* Refresh the list of USB devices
*/