[core] more drive handling improvements

* Use retries when locking the volume
* Wait for volume availability after partitioning
* Don't halt on errors that can be ignored
* Other small improvements and fixes
* The above should help with addressing #122
This commit is contained in:
Pete Batard 2013-06-25 18:39:07 +01:00
parent cd5665881c
commit 6be21bbdbc
4 changed files with 111 additions and 70 deletions

View file

@ -328,7 +328,6 @@ static DWORD GetFATSizeSectors(DWORD DskSize, DWORD ReservedSecCnt, DWORD SecPer
* Large FAT32 volume formatting from fat32format by Tom Thornhill
* http://www.ridgecrop.demon.co.uk/index.htm?fat32format.htm
*/
// TODO: (v1.3.4) disable slow format for > 32 GB FAT32
static BOOL FormatFAT32(DWORD DriveIndex)
{
BOOL r = FALSE;
@ -369,17 +368,14 @@ static BOOL FormatFAT32(DWORD DriveIndex)
PrintStatus(0, TRUE, "Formatting (Large FAT32)...");
VolumeId = GetVolumeID();
// Open the drive (volume should already be locked)
hLogicalVolume = GetLogicalHandle(DriveIndex, TRUE, FALSE);
// Open the drive and lock it
hLogicalVolume = GetLogicalHandle(DriveIndex, TRUE, TRUE);
if (IS_ERROR(FormatStatus)) goto out;
if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL))
die("Invalid logical volume handle\n", ERROR_INVALID_HANDLE);
// Make sure we get exclusive access
if (!UnmountVolume(hLogicalVolume)) {
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
goto out;
}
// Try to disappear the volume while we're formatting it
UnmountVolume(hLogicalVolume);
// Work out drive params
if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dgDrive,
@ -583,7 +579,7 @@ static BOOL FormatFAT32(DWORD DriveIndex)
// Handle must be closed for SetVolumeLabel to work
safe_closehandle(hLogicalVolume);
PrintStatus(0, TRUE, "Setting Label (This may take while)...");
VolumeName = GetLogicalName(DriveIndex, TRUE);
VolumeName = GetLogicalName(DriveIndex, TRUE, TRUE);
wVolumeName = utf8_to_wchar(VolumeName);
if ((wVolumeName == NULL) || (!SetVolumeLabelW(wVolumeName, wLabel))) {
uprintf("Could not set label: %s\n", WindowsErrorString());
@ -622,7 +618,7 @@ static BOOL FormatDrive(DWORD DriveIndex)
GetWindowTextA(hFileSystem, FSType, ARRAYSIZE(FSType));
safe_sprintf(format_status, ARRAYSIZE(format_status), "Formatting (%s)...", FSType);
PrintStatus(0, TRUE, format_status);
VolumeName = GetLogicalName(DriveIndex, FALSE);
VolumeName = GetLogicalName(DriveIndex, FALSE, TRUE);
wVolumeName = utf8_to_wchar(VolumeName);
if (wVolumeName == NULL) {
uprintf("Could not read volume name\n");
@ -822,6 +818,7 @@ BOOL WriteRufusMBR(FILE *fp)
static BOOL WriteMBR(HANDLE hPhysicalDrive)
{
BOOL r = FALSE;
DWORD size;
int dt, fs;
unsigned char* buf = NULL;
size_t SecSize = SelectedDrive.Geometry.BytesPerSector;
@ -891,6 +888,9 @@ static BOOL WriteMBR(HANDLE hPhysicalDrive)
}
}
// Tell the system we've updated the disk properties
DeviceIoControl(hPhysicalDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL );
out:
safe_free(buf);
return r;
@ -1095,14 +1095,15 @@ out:
*/
static BOOL MountVolume(char* drive_name, char *drive_guid)
{
char mounted_guid[50];
char mounted_guid[52]; // You need at least 51 characters on XP
if (!SetVolumeMountPointA(drive_name, drive_guid)) {
// If the OS was faster than us at remounting the drive, this operation can fail
// with ERROR_DIR_NOT_EMPTY. If that's the case, just check that mountpoints match
if (GetLastError() == ERROR_DIR_NOT_EMPTY) {
if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) {
uprintf("%s already mounted, but volume GUID could not be checked\n", drive_name);
uprintf("%s already mounted, but volume GUID could not be checked: %s\n",
drive_name, WindowsErrorString());
return FALSE;
}
if (safe_strcmp(drive_guid, mounted_guid) != 0) {
@ -1123,7 +1124,7 @@ static BOOL MountVolume(char* drive_name, char *drive_guid)
*/
static BOOL RemountVolume(char* drive_name)
{
char drive_guid[50];
char drive_guid[51];
if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) {
if (DeleteVolumeMountPointA(drive_name)) {
@ -1198,6 +1199,7 @@ DWORD WINAPI CloseFormatPromptThread(LPVOID param) {
* Unlock the volume.
* Close the volume handle.
*/
#define CHECK_FOR_USER_CANCEL if (IS_ERROR(FormatStatus)) goto out
DWORD WINAPI FormatThread(LPVOID param)
{
int r, pt, bt, fs, dt;
@ -1243,7 +1245,6 @@ DWORD WINAPI FormatThread(LPVOID param)
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
// TODO: Use a retry loop for the lock
hLogicalVolume = GetLogicalHandle(DriveIndex, FALSE, TRUE);
if (hLogicalVolume == INVALID_HANDLE_VALUE) {
uprintf("Could not lock volume\n");
@ -1252,11 +1253,10 @@ DWORD WINAPI FormatThread(LPVOID param)
} 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");
} else if (!UnmountVolume(hLogicalVolume)) {
uprintf("Trying to continue regardless...\n");
}
if (FormatStatus) goto out; // Check for user cancel
CHECK_FOR_USER_CANCEL;
PrintStatus(0, TRUE, "Analyzing existing boot records...\n");
AnalyzeMBR(hPhysicalDrive);
@ -1276,7 +1276,7 @@ DWORD WINAPI FormatThread(LPVOID param)
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.
// have localized characters, we use the UTF-8 API.
userdir = getenvU("USERPROFILE");
safe_strcpy(logfile, MAX_PATH, userdir);
safe_free(userdir);
@ -1296,9 +1296,8 @@ DWORD WINAPI FormatThread(LPVOID param)
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);
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_BADBLOCKS_FAILURE);
ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.Geometry.BytesPerSector);
fclose(log_fd);
_unlink(logfile);
@ -1333,8 +1332,7 @@ DWORD WINAPI FormatThread(LPVOID param)
goto out;
}
}
// Close the (unmounted) volume before formatting, but keep the lock
// According to MS this relinquishes the lock, so...
// Close the (unmounted) volume before formatting
if (hLogicalVolume != NULL) {
PrintStatus(0, TRUE, "Closing existing volume...\n");
if (!CloseHandle(hLogicalVolume)) {
@ -1351,12 +1349,12 @@ DWORD WINAPI FormatThread(LPVOID param)
// 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)
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
goto out;
}
if (FormatStatus) goto out; // Check for user cancel
UpdateProgress(OP_ZERO_MBR, -1.0f);
CHECK_FOR_USER_CANCEL;
CreateThread(NULL, 0, CloseFormatPromptThread, NULL, 0, NULL);
if (!CreatePartition(hPhysicalDrive, pt, fs, (pt==PARTITION_STYLE_MBR)&&(bt==BT_UEFI))) {
@ -1365,9 +1363,11 @@ DWORD WINAPI FormatThread(LPVOID param)
}
UpdateProgress(OP_PARTITION, -1.0f);
// Add a delay after partitioning to be safe
// TODO: Use a retry loop and test the lock
Sleep(2000);
// Wait for the logical drive we just created to appear
uprintf("Waiting for logical drive to reappear...\n");
Sleep(200);
WaitForLogical(DriveIndex); // 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.
@ -1379,7 +1379,22 @@ DWORD WINAPI FormatThread(LPVOID param)
goto out;
}
guid_volume = GetLogicalName(DriveIndex, TRUE);
// Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted
if (pt == PARTITION_STYLE_MBR) {
PrintStatus(0, TRUE, "Writing master boot record...");
if (!WriteMBR(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;
@ -1387,22 +1402,12 @@ DWORD WINAPI FormatThread(LPVOID param)
}
uprintf("Found volume GUID %s\n", guid_volume);
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 (FormatStatus) goto out; // Check for user cancel
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;
if (IsChecked(IDC_BOOT)) {
if (bt == BT_UEFI) {
@ -1425,7 +1430,7 @@ DWORD WINAPI FormatThread(LPVOID param)
// [0x00000456] The media in the drive may have changed
PrintStatus(0, TRUE, "Writing partition boot record...");
if (!WritePBR(hLogicalVolume)) {
if (!FormatStatus)
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
goto out;
}
@ -1440,21 +1445,21 @@ DWORD WINAPI FormatThread(LPVOID param)
if (IsChecked(IDC_SET_ICON))
SetAutorun(drive_name);
}
if (FormatStatus) goto out; // Check for user cancel
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;
if (FormatStatus) goto out; // Check for user cancel
CHECK_FOR_USER_CANCEL;
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)
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
goto out;
}
@ -1464,7 +1469,7 @@ DWORD WINAPI FormatThread(LPVOID param)
PrintStatus(0, TRUE, "Copying ISO files...");
drive_name[2] = 0;
if (!ExtractISO(iso_path, drive_name, FALSE)) {
if (!FormatStatus)
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
goto out;
}