From b2b621cec799b8c28c7a0e094a06a46f276c6fc5 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Mon, 26 Oct 2020 11:48:33 +0000 Subject: [PATCH] [core] improve mounting/unmounting of volumes * Factorize drive letter removal into a RemoveDriveLetters() call. * Improve MountVolume() and RemountVolume() calls. * Also bump Rufus version to 3.13 --- configure | 20 ++++----- configure.ac | 2 +- res/appstore/AppxManifest.xml | 2 +- res/appstore/packme.cmd | 2 +- src/drive.c | 81 +++++++++++++++++++++++++++++------ src/drive.h | 1 + src/format.c | 40 ++++++----------- src/rufus.rc | 12 +++--- 8 files changed, 99 insertions(+), 61 deletions(-) diff --git a/configure b/configure index 343544f5..440e596d 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for rufus 3.12. +# Generated by GNU Autoconf 2.69 for rufus 3.13. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='rufus' PACKAGE_TARNAME='rufus' -PACKAGE_VERSION='3.12' -PACKAGE_STRING='rufus 3.12' +PACKAGE_VERSION='3.13' +PACKAGE_STRING='rufus 3.13' PACKAGE_BUGREPORT='https://github.com/pbatard/rufus/issues' PACKAGE_URL='https://rufus.ie' @@ -1228,7 +1228,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures rufus 3.12 to adapt to many kinds of systems. +\`configure' configures rufus 3.13 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1294,7 +1294,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of rufus 3.12:";; + short | recursive ) echo "Configuration of rufus 3.13:";; esac cat <<\_ACEOF @@ -1385,7 +1385,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -rufus configure 3.12 +rufus configure 3.13 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1440,7 +1440,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by rufus $as_me 3.12, which was +It was created by rufus $as_me 3.13, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2303,7 +2303,7 @@ fi # Define the identity of the package. PACKAGE='rufus' - VERSION='3.12' + VERSION='3.13' cat >>confdefs.h <<_ACEOF @@ -4484,7 +4484,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by rufus $as_me 3.12, which was +This file was extended by rufus $as_me 3.13, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4538,7 +4538,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -rufus config.status 3.12 +rufus config.status 3.13 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 9d8f44e0..7da31e48 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([rufus], [3.12], [https://github.com/pbatard/rufus/issues], [rufus], [https://rufus.ie]) +AC_INIT([rufus], [3.13], [https://github.com/pbatard/rufus/issues], [rufus], [https://rufus.ie]) AM_INIT_AUTOMAKE([-Wno-portability foreign no-dist no-dependencies]) AC_CONFIG_SRCDIR([src/rufus.c]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/res/appstore/AppxManifest.xml b/res/appstore/AppxManifest.xml index 122b2857..32a20026 100644 --- a/res/appstore/AppxManifest.xml +++ b/res/appstore/AppxManifest.xml @@ -8,7 +8,7 @@ for an interesting struggle, when you also happen to have a comma in one of the fields... --> diff --git a/res/appstore/packme.cmd b/res/appstore/packme.cmd index 72a12567..e82e4e9f 100644 --- a/res/appstore/packme.cmd +++ b/res/appstore/packme.cmd @@ -1,6 +1,6 @@ @echo off setlocal EnableExtensions DisableDelayedExpansion -set VERSION=3.12 +set VERSION=3.13 del /q *.appx >NUL 2>&1 del /q *.appxbundle >NUL 2>&1 diff --git a/src/drive.c b/src/drive.c index 7c333f02..ddcf2f9c 100644 --- a/src/drive.c +++ b/src/drive.c @@ -1099,6 +1099,47 @@ UINT GetDriveTypeFromIndex(DWORD DriveIndex) return drive_type; } + +// Removes all drive letters associated with the specific drive, and return +// either the first or last letter that was removed, according to bReturnLast. +char RemoveDriveLetters(DWORD DriveIndex, BOOL bReturnLast, BOOL bSilent) +{ + int i, len; + char drive_letters[27] = { 0 }, drive_name[4] = "#:\\"; + + if (!GetDriveLetters(DriveIndex, drive_letters)) { + suprintf("Failed to get a drive letter"); + return 0; + } + if (drive_letters[0] == 0) { + suprintf("No drive letter was assigned..."); + return GetUnusedDriveLetter(); + } + len = (int)strlen(drive_letters); + if (len == 0) + return 0; + + // Unmount all mounted volumes that belong to this drive + for (i = 0; i < len; i++) { + // Check that the current image isn't located on a drive we are trying to dismount + if ((boot_type == BT_IMAGE) && (drive_letters[i] == (PathGetDriveNumberU(image_path) + 'A'))) { + if ((PathGetDriveNumberU(image_path) + 'A') == drive_letters[i]) { + suprintf("ABORTED: Cannot use an image that is located on the target drive!"); + return 0; + } + } + drive_name[0] = drive_letters[i]; + // DefineDosDevice() cannot have a trailing backslash... + drive_name[2] = 0; + DefineDosDeviceA(DDD_REMOVE_DEFINITION, drive_name, NULL); + // ... but DeleteVolumeMountPoint() requires one. Go figure... + drive_name[2] = '\\'; + if (!DeleteVolumeMountPointA(drive_name)) + suprintf("Failed to delete mountpoint %s: %s", drive_name, WindowsErrorString()); + } + return drive_letters[bReturnLast ? (len - 1) : 0]; +} + /* * Return the next unused drive letter from the system or NUL on error. */ @@ -1719,15 +1760,33 @@ BOOL UnmountVolume(HANDLE hDrive) */ BOOL MountVolume(char* drive_name, char *volume_name) { - char mounted_guid[52]; + char mounted_guid[52], dos_name[] = "?:"; #if defined(WINDOWS_IS_NOT_BUGGY) char mounted_letter[27] = { 0 }; DWORD size; #endif - if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '?') || - (strncmp(volume_name, groot_name, groot_len) == 0)) + if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '?')) { + SetLastError(ERROR_INVALID_PARAMETER); return FALSE; + } + + // If we are working with a "\\?\GLOBALROOT" device, SetVolumeMountPoint() + // is useless, so try with DefineDosDevice() instead. + if (_strnicmp(volume_name, groot_name, groot_len) == 0) { + dos_name[0] = drive_name[0]; + // Microsoft will also have to explain why "In no case is a trailing backslash allowed" [1] in + // DefineDosDevice(), instead of just checking if the driver parameter is "X:\" and remove the + // backslash from a copy of the parameter in the bloody API call. *THIS* really tells a lot + // about the level of thought and foresight that actually goes into the Windows APIs... + // [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-definedosdevicew + if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, dos_name, &volume_name[14])) { + uprintf("Could not mount %s as %C:", volume_name, drive_name[0]); + return FALSE; + } + uprintf("%s was successfully mounted as %C:", volume_name, drive_name[0]); + return TRUE; + } // Great: Windows has a *MAJOR BUG* whereas, in some circumstances, GetVolumePathNamesForVolumeName() // can return the *WRONG* drive letter. And yes, we validated that this is *NOT* an issue like stack @@ -1837,19 +1896,13 @@ BOOL RemountVolume(char* drive_name) // UDF requires a sync/flush, and it's also a good idea for other FS's FlushDrive(drive_name[0]); if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(volume_name))) { - if (DeleteVolumeMountPointA(drive_name)) { - Sleep(200); - if (MountVolume(drive_name, volume_name)) { - uprintf("Successfully remounted %s as %C:", volume_name, drive_name[0]); - } else { - uprintf("Failed to remount %s as %C:", volume_name, drive_name[0]); - // This will leave the drive inaccessible and must be flagged as an error - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); - return FALSE; - } + if (MountVolume(drive_name, volume_name)) { + uprintf("Successfully remounted %s as %C:", volume_name, drive_name[0]); } else { uprintf("Could not remount %s as %C: %s", volume_name, drive_name[0], WindowsErrorString()); - // Try to continue regardless + // This will leave the drive inaccessible and must be flagged as an error + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); + return FALSE; } } return TRUE; diff --git a/src/drive.h b/src/drive.h index 9298ced0..a1c8748f 100644 --- a/src/drive.h +++ b/src/drive.h @@ -384,6 +384,7 @@ BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters); UINT GetDriveTypeFromIndex(DWORD DriveIndex); char GetUnusedDriveLetter(void); BOOL IsDriveLetterInUse(const char drive_letter); +char RemoveDriveLetters(DWORD DriveIndex, BOOL bUseLast, BOOL bSilent); BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label); uint64_t GetDriveSize(DWORD DriveIndex); BOOL IsMediaPresent(DWORD DriveIndex); diff --git a/src/format.c b/src/format.c index 5ef19f2b..4a808e20 100644 --- a/src/format.c +++ b/src/format.c @@ -1660,7 +1660,7 @@ out: */ DWORD WINAPI FormatThread(void* param) { - int i, r; + int r; BOOL ret, use_large_fat32, windows_to_go, actual_lock_drive = lock_drive; DWORD cr, DriveIndex = (DWORD)(uintptr_t)param, ClusterSize, Flags; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; @@ -1714,32 +1714,13 @@ DWORD WINAPI FormatThread(void* param) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); goto out; } - if (drive_letters[0] == 0) { - uprintf("No drive letter was assigned..."); - drive_name[0] = GetUnusedDriveLetter(); - if (drive_name[0] == 0) { - uprintf("Could not find a suitable drive letter"); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER); - goto out; - } - } else { - // Unmount all mounted volumes that belong to this drive - // Do it in reverse so that we always end on the first volume letter - for (i = (int)safe_strlen(drive_letters); i > 0; i--) { - drive_name[0] = drive_letters[i-1]; - if (boot_type == BT_IMAGE) { - // If we are using an image, check that it isn't located on the drive we are trying to format - if ((PathGetDriveNumberU(image_path) + 'A') == drive_letters[i-1]) { - uprintf("ABORTED: Cannot use an image that is located on the target drive!"); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED; - goto out; - } - } - if (!DeleteVolumeMountPointA(drive_name)) { - uprintf("Failed to delete mountpoint %s: %s", drive_name, WindowsErrorString()); - // Try to continue. We will bail out if this causes an issue. - } - } + + // Unassign all drives letters + drive_name[0] = RemoveDriveLetters(DriveIndex, TRUE, FALSE); + if (drive_name[0] == 0) { + uprintf("Unable to find a drive letter to use"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER); + goto out; } uprintf("Will use '%C:' as volume mountpoint", drive_name[0]); @@ -2012,6 +1993,9 @@ DWORD WINAPI FormatThread(void* param) } uprintf("Found volume %s", volume_name); + // Windows is really finicky with regards to reassigning drive letters even after + // we forcibly removed them, so add yet another explicit call to RemoveDriveLetters() + RemoveDriveLetters(DriveIndex, FALSE, TRUE); if (!MountVolume(drive_name, volume_name)) { uprintf("Could not remount %s as %C: %s\n", volume_name, drive_name[0], WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_MOUNT_VOLUME); @@ -2080,7 +2064,7 @@ DWORD WINAPI FormatThread(void* param) } CHECK_FOR_USER_CANCEL; - // We issue a complete remount of the filesystem at on account of: + // We issue a complete remount of the filesystem 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)) diff --git a/src/rufus.rc b/src/rufus.rc index 4fbf7cfe..9f1c7ea4 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.12.1713" +CAPTION "Rufus 3.13.1714" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,12,1713,0 - PRODUCTVERSION 3,12,1713,0 + FILEVERSION 3,13,1714,0 + PRODUCTVERSION 3,13,1714,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.12.1713" + VALUE "FileVersion", "3.13.1714" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" - VALUE "OriginalFilename", "rufus-3.12.exe" + VALUE "OriginalFilename", "rufus-3.13.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.12.1713" + VALUE "ProductVersion", "3.13.1714" END END BLOCK "VarFileInfo"