2011-12-01 17:54:35 +00:00
|
|
|
/*
|
2011-12-05 11:36:02 +00:00
|
|
|
* Rufus: The Reliable USB Formatting Utility
|
2011-12-01 17:54:35 +00:00
|
|
|
* Drive access function calls
|
2020-04-10 12:16:57 +00:00
|
|
|
* Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
|
2011-12-01 17:54:35 +00:00
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#ifdef _CRTDBG_MAP_ALLOC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <crtdbg.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
2018-06-30 21:45:15 +00:00
|
|
|
#include <assert.h>
|
2018-11-28 10:50:00 +00:00
|
|
|
#if !defined(__MINGW32__)
|
|
|
|
#include <initguid.h>
|
|
|
|
#endif
|
|
|
|
#include <vds.h>
|
2011-12-01 17:54:35 +00:00
|
|
|
|
|
|
|
#include "rufus.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "missing.h"
|
|
|
|
#include "resource.h"
|
2020-06-06 15:34:17 +00:00
|
|
|
#include "settings.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "msapi_utf8.h"
|
|
|
|
#include "localization.h"
|
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
#include "file.h"
|
2014-01-05 01:39:41 +00:00
|
|
|
#include "drive.h"
|
2020-02-06 18:23:19 +00:00
|
|
|
#include "mbr_types.h"
|
|
|
|
#include "gpt_types.h"
|
2014-01-05 01:39:41 +00:00
|
|
|
#include "br.h"
|
|
|
|
#include "fat16.h"
|
|
|
|
#include "fat32.h"
|
|
|
|
#include "ntfs.h"
|
2011-12-01 17:54:35 +00:00
|
|
|
|
2019-04-25 17:58:55 +00:00
|
|
|
#define GLOBALROOT_NAME "\\\\?\\GLOBALROOT"
|
|
|
|
const char* sfd_name = "Super Floppy Disk";
|
|
|
|
const char* groot_name = GLOBALROOT_NAME;
|
|
|
|
const size_t groot_len = sizeof(GLOBALROOT_NAME) - 1;
|
|
|
|
|
2014-08-07 00:45:46 +00:00
|
|
|
|
2018-11-28 10:50:00 +00:00
|
|
|
#if defined(__MINGW32__)
|
|
|
|
const IID CLSID_VdsLoader = { 0x9c38ed61, 0xd565, 0x4728, { 0xae, 0xee, 0xc8, 0x09, 0x52, 0xf0, 0xec, 0xde } };
|
|
|
|
const IID IID_IVdsServiceLoader = { 0xe0393303, 0x90d4, 0x4a97, { 0xab, 0x71, 0xe9, 0xb6, 0x71, 0xee, 0x27, 0x29 } };
|
|
|
|
const IID IID_IVdsProvider = { 0x10c5e575, 0x7984, 0x4e81, { 0xa5, 0x6b, 0x43, 0x1f, 0x5f, 0x92, 0xae, 0x42 } };
|
|
|
|
const IID IID_IVdsSwProvider = { 0x9aa58360, 0xce33, 0x4f92, { 0xb6, 0x58, 0xed, 0x24, 0xb1, 0x44, 0x25, 0xb8 } };
|
|
|
|
const IID IID_IVdsPack = { 0x3b69d7f5, 0x9d94, 0x4648, { 0x91, 0xca, 0x79, 0x93, 0x9b, 0xa2, 0x63, 0xbf } };
|
|
|
|
const IID IID_IVdsDisk = { 0x07e5c822, 0xf00c, 0x47a1, { 0x8f, 0xce, 0xb2, 0x44, 0xda, 0x56, 0xfd, 0x06 } };
|
|
|
|
const IID IID_IVdsAdvancedDisk = { 0x6e6f6b40, 0x977c, 0x4069, { 0xbd, 0xdd, 0xac, 0x71, 0x00, 0x59, 0xf8, 0xc0 } };
|
2019-04-25 17:58:55 +00:00
|
|
|
const IID IID_IVdsVolume = { 0x88306BB2, 0xE71F, 0x478C, { 0x86, 0xA2, 0x79, 0xDA, 0x20, 0x0A, 0x0F, 0x11} };
|
|
|
|
const IID IID_IVdsVolumeMF3 = { 0x6788FAF9, 0x214E, 0x4B85, { 0xBA, 0x59, 0x26, 0x69, 0x53, 0x61, 0x6E, 0x09 } };
|
2018-11-28 10:50:00 +00:00
|
|
|
#endif
|
|
|
|
|
2018-04-02 13:29:02 +00:00
|
|
|
PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryVolumeInformationFile, (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS));
|
|
|
|
|
2011-12-01 17:54:35 +00:00
|
|
|
/*
|
|
|
|
* Globals
|
|
|
|
*/
|
|
|
|
RUFUS_DRIVE_INFO SelectedDrive;
|
2020-06-06 21:19:20 +00:00
|
|
|
extern BOOL installed_uefi_ntfs, write_as_esp;
|
2019-08-18 13:13:09 +00:00
|
|
|
uint64_t partition_offset[3];
|
2019-04-16 19:44:13 +00:00
|
|
|
uint64_t persistence_size = 0;
|
2011-12-01 17:54:35 +00:00
|
|
|
|
2014-11-11 19:17:39 +00:00
|
|
|
/*
|
|
|
|
* The following methods get or set the AutoMount setting (which is different from AutoRun)
|
|
|
|
* Rufus needs AutoMount to be set as the format process may fail for fixed drives otherwise.
|
|
|
|
* See https://github.com/pbatard/rufus/issues/386.
|
|
|
|
*
|
|
|
|
* Reverse engineering diskpart and mountvol indicates that the former uses the IVdsService
|
2015-11-05 22:54:38 +00:00
|
|
|
* ClearFlags()/SetFlags() to set VDS_SVF_AUTO_MOUNT_OFF whereas mountvol on uses
|
2014-11-11 19:17:39 +00:00
|
|
|
* IOCTL_MOUNTMGR_SET_AUTO_MOUNT on "\\\\.\\MountPointManager".
|
|
|
|
* As the latter is MUCH simpler this is what we'll use too
|
|
|
|
*/
|
|
|
|
BOOL SetAutoMount(BOOL enable)
|
|
|
|
{
|
|
|
|
HANDLE hMountMgr;
|
|
|
|
DWORD size;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
|
|
|
|
hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hMountMgr == NULL)
|
|
|
|
return FALSE;
|
|
|
|
ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_SET_AUTO_MOUNT, &enable, sizeof(enable), NULL, 0, &size, NULL);
|
|
|
|
CloseHandle(hMountMgr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL GetAutoMount(BOOL* enabled)
|
|
|
|
{
|
|
|
|
HANDLE hMountMgr;
|
|
|
|
DWORD size;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
|
|
|
|
if (enabled == NULL)
|
|
|
|
return FALSE;
|
|
|
|
hMountMgr = CreateFileA(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hMountMgr == NULL)
|
|
|
|
return FALSE;
|
|
|
|
ret = DeviceIoControl(hMountMgr, IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT, NULL, 0, enabled, sizeof(*enabled), &size, NULL);
|
|
|
|
CloseHandle(hMountMgr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
/*
|
|
|
|
* Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as
|
|
|
|
* index would return a handle to C:, which we might then proceed to unknowingly
|
|
|
|
* clear the MBR of!), so we mitigate the risk by forcing our indexes to belong to
|
|
|
|
* the specific range [DRIVE_INDEX_MIN; DRIVE_INDEX_MAX].
|
|
|
|
*/
|
2018-06-30 21:45:15 +00:00
|
|
|
#define CheckDriveIndex(DriveIndex) do { \
|
2018-11-28 10:50:00 +00:00
|
|
|
if ((int)DriveIndex < 0) goto out; \
|
2018-06-30 21:45:15 +00:00
|
|
|
assert((DriveIndex >= DRIVE_INDEX_MIN) && (DriveIndex <= DRIVE_INDEX_MAX)); \
|
|
|
|
if ((DriveIndex < DRIVE_INDEX_MIN) || (DriveIndex > DRIVE_INDEX_MAX)) goto out; \
|
2013-04-07 23:10:58 +00:00
|
|
|
DriveIndex -= DRIVE_INDEX_MIN; } while (0)
|
|
|
|
|
2011-12-01 17:54:35 +00:00
|
|
|
/*
|
|
|
|
* Open a drive or volume with optional write and lock access
|
2013-04-07 23:10:58 +00:00
|
|
|
* Return INVALID_HANDLE_VALUE (/!\ which is DIFFERENT from NULL /!\) on failure.
|
2011-12-01 17:54:35 +00:00
|
|
|
*/
|
2017-04-08 20:58:10 +00:00
|
|
|
static HANDLE GetHandle(char* Path, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
|
2011-12-01 17:54:35 +00:00
|
|
|
{
|
2013-06-25 17:39:07 +00:00
|
|
|
int i;
|
2017-07-13 20:53:44 +00:00
|
|
|
BYTE access_mask = 0;
|
2017-11-13 14:29:48 +00:00
|
|
|
DWORD size;
|
|
|
|
uint64_t EndTime;
|
2011-12-01 17:54:35 +00:00
|
|
|
HANDLE hDrive = INVALID_HANDLE_VALUE;
|
2017-04-08 20:58:10 +00:00
|
|
|
char DevPath[MAX_PATH];
|
2011-12-01 17:54:35 +00:00
|
|
|
|
2017-04-08 20:58:10 +00:00
|
|
|
if ((safe_strlen(Path) < 5) || (Path[0] != '\\') || (Path[1] != '\\') || (Path[3] != '\\'))
|
2013-04-07 23:10:58 +00:00
|
|
|
goto out;
|
2017-04-08 20:58:10 +00:00
|
|
|
|
2017-04-27 22:06:42 +00:00
|
|
|
// Resolve a device path, so that we can look for that handle in case of access issues.
|
2019-04-25 17:58:55 +00:00
|
|
|
if (safe_strncmp(Path, groot_name, groot_len) == 0)
|
|
|
|
static_strcpy(DevPath, &Path[groot_len]);
|
|
|
|
else if (QueryDosDeviceA(&Path[4], DevPath, sizeof(DevPath)) == 0)
|
2017-04-08 20:58:10 +00:00
|
|
|
strcpy(DevPath, "???");
|
|
|
|
|
2017-02-16 14:13:30 +00:00
|
|
|
for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) {
|
2017-04-08 20:58:10 +00:00
|
|
|
// Try without FILE_SHARE_WRITE (unless specifically requested) so that
|
2017-02-16 14:13:30 +00:00
|
|
|
// we won't be bothered by the OS or other apps when we set up our data.
|
|
|
|
// However this means we might have to wait for an access gap...
|
|
|
|
// We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is
|
|
|
|
// required for enumeration.
|
|
|
|
hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRITE:0),
|
2017-05-01 23:56:07 +00:00
|
|
|
FILE_SHARE_READ|(bWriteShare?FILE_SHARE_WRITE:0),
|
2017-04-08 20:58:10 +00:00
|
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
2017-02-16 14:13:30 +00:00
|
|
|
if (hDrive != INVALID_HANDLE_VALUE)
|
|
|
|
break;
|
|
|
|
if ((GetLastError() != ERROR_SHARING_VIOLATION) && (GetLastError() != ERROR_ACCESS_DENIED))
|
|
|
|
break;
|
2017-04-08 20:58:10 +00:00
|
|
|
if (i == 0) {
|
2017-05-03 13:48:24 +00:00
|
|
|
uprintf("Waiting for access on %s [%s]...", Path, DevPath);
|
2017-05-01 23:56:07 +00:00
|
|
|
} else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES/3)) {
|
2017-05-03 13:48:24 +00:00
|
|
|
// If we can't seem to get a hold of the drive for some time, try to enable FILE_SHARE_WRITE...
|
2017-04-09 20:05:10 +00:00
|
|
|
uprintf("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled...");
|
2017-04-08 20:58:10 +00:00
|
|
|
bWriteShare = TRUE;
|
2017-05-03 13:48:24 +00:00
|
|
|
// Try to report the process that is locking the drive
|
2017-07-13 20:53:44 +00:00
|
|
|
// We also use bit 6 as a flag to indicate that SearchProcess was called.
|
2019-01-31 13:50:03 +00:00
|
|
|
access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40;
|
2017-04-08 20:58:10 +00:00
|
|
|
}
|
2017-02-16 14:13:30 +00:00
|
|
|
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
|
|
|
|
}
|
2013-04-07 23:10:58 +00:00
|
|
|
if (hDrive == INVALID_HANDLE_VALUE) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not open %s: %s", Path, WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
goto out;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
if (bWriteAccess) {
|
2017-05-03 13:48:24 +00:00
|
|
|
uprintf("Opened %s for %s write access", Path, bWriteShare?"shared":"exclusive");
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 17:39:07 +00:00
|
|
|
if (bLockDrive) {
|
2013-11-23 22:39:54 +00:00
|
|
|
if (DeviceIoControl(hDrive, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &size, NULL)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("I/O boundary checks disabled");
|
2013-11-23 22:39:54 +00:00
|
|
|
}
|
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT;
|
2017-05-08 15:58:26 +00:00
|
|
|
do {
|
2013-06-25 17:39:07 +00:00
|
|
|
if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &size, NULL))
|
|
|
|
goto out;
|
|
|
|
if (IS_ERROR(FormatStatus)) // User cancel
|
|
|
|
break;
|
2017-05-08 15:58:26 +00:00
|
|
|
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
|
2017-11-13 14:29:48 +00:00
|
|
|
} while (GetTickCount64() < EndTime);
|
2013-06-25 17:39:07 +00:00
|
|
|
// If we reached this section, either we didn't manage to get a lock or the user cancelled
|
2017-05-03 13:48:24 +00:00
|
|
|
uprintf("Could not lock access to %s: %s", Path, WindowsErrorString());
|
2017-07-13 20:53:44 +00:00
|
|
|
// See if we can report the processes are accessing the drive
|
|
|
|
if (!IS_ERROR(FormatStatus) && (access_mask == 0))
|
2019-01-31 13:50:03 +00:00
|
|
|
access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE);
|
2017-07-13 20:53:44 +00:00
|
|
|
// Try to continue if the only access rights we saw were for read-only
|
|
|
|
if ((access_mask & 0x07) != 0x01)
|
|
|
|
safe_closehandle(hDrive);
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return hDrive;
|
|
|
|
}
|
|
|
|
|
2015-11-05 22:54:38 +00:00
|
|
|
/*
|
2013-04-07 23:10:58 +00:00
|
|
|
* Return the path to access the physical drive, or NULL on error.
|
|
|
|
* The string is allocated and must be freed (to ensure concurrent access)
|
|
|
|
*/
|
|
|
|
char* GetPhysicalName(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
BOOL success = FALSE;
|
|
|
|
char physical_name[24];
|
|
|
|
|
|
|
|
CheckDriveIndex(DriveIndex);
|
2018-11-28 10:50:00 +00:00
|
|
|
static_sprintf(physical_name, "\\\\.\\PhysicalDrive%lu", DriveIndex);
|
2013-04-07 23:10:58 +00:00
|
|
|
success = TRUE;
|
|
|
|
out:
|
|
|
|
return (success)?safe_strdup(physical_name):NULL;
|
|
|
|
}
|
|
|
|
|
2015-11-05 22:54:38 +00:00
|
|
|
/*
|
2013-04-07 23:10:58 +00:00
|
|
|
* Return a handle to the physical drive identified by DriveIndex
|
|
|
|
*/
|
2017-05-01 23:56:07 +00:00
|
|
|
HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
|
2013-04-07 23:10:58 +00:00
|
|
|
{
|
|
|
|
HANDLE hPhysical = INVALID_HANDLE_VALUE;
|
|
|
|
char* PhysicalPath = GetPhysicalName(DriveIndex);
|
2017-05-01 23:56:07 +00:00
|
|
|
hPhysical = GetHandle(PhysicalPath, bLockDrive, bWriteAccess, bWriteShare);
|
2013-04-07 23:10:58 +00:00
|
|
|
safe_free(PhysicalPath);
|
|
|
|
return hPhysical;
|
|
|
|
}
|
|
|
|
|
2013-11-17 01:39:43 +00:00
|
|
|
/*
|
2019-04-25 17:58:55 +00:00
|
|
|
* Return the GUID volume name for the disk and partition specified, or NULL if not found.
|
2013-11-17 01:39:43 +00:00
|
|
|
* See http://msdn.microsoft.com/en-us/library/cc542456.aspx
|
2019-08-18 13:13:09 +00:00
|
|
|
* If PartitionOffset is 0, the offset is ignored and the first partition found is returned.
|
|
|
|
* The returned string is allocated and must be freed.
|
2013-11-17 01:39:43 +00:00
|
|
|
*/
|
2019-08-18 13:13:09 +00:00
|
|
|
char* GetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent)
|
2013-04-07 23:10:58 +00:00
|
|
|
{
|
2019-04-25 17:58:55 +00:00
|
|
|
static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" };
|
|
|
|
static const char* volume_start = "\\\\?\\";
|
|
|
|
char *ret = NULL, volume_name[MAX_PATH], path[MAX_PATH];
|
2019-08-13 08:04:31 +00:00
|
|
|
BOOL bPrintHeader = TRUE;
|
2013-04-07 23:10:58 +00:00
|
|
|
HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE;
|
2017-01-05 15:42:24 +00:00
|
|
|
VOLUME_DISK_EXTENTS_REDEF DiskExtents;
|
2013-04-07 23:10:58 +00:00
|
|
|
DWORD size;
|
2013-11-17 01:39:43 +00:00
|
|
|
UINT drive_type;
|
2019-04-25 17:58:55 +00:00
|
|
|
StrArray found_name;
|
|
|
|
uint64_t found_offset[MAX_PARTITIONS] = { 0 };
|
2019-08-18 13:13:09 +00:00
|
|
|
uint32_t i, j;
|
2019-04-25 17:58:55 +00:00
|
|
|
size_t len;
|
2013-04-07 23:10:58 +00:00
|
|
|
|
2019-04-25 17:58:55 +00:00
|
|
|
StrArrayCreate(&found_name, MAX_PARTITIONS);
|
2013-04-07 23:10:58 +00:00
|
|
|
CheckDriveIndex(DriveIndex);
|
|
|
|
|
2018-11-28 10:50:00 +00:00
|
|
|
for (i = 0; hDrive == INVALID_HANDLE_VALUE; i++) {
|
2013-04-07 23:10:58 +00:00
|
|
|
if (i == 0) {
|
|
|
|
hVolume = FindFirstVolumeA(volume_name, sizeof(volume_name));
|
|
|
|
if (hVolume == INVALID_HANDLE_VALUE) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not access first GUID volume: %s", WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!FindNextVolumeA(hVolume, volume_name, sizeof(volume_name))) {
|
|
|
|
if (GetLastError() != ERROR_NO_MORE_FILES) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not access next GUID volume: %s", WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
2019-04-25 17:58:55 +00:00
|
|
|
break;
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
2013-04-07 23:10:58 +00:00
|
|
|
|
|
|
|
// Sanity checks
|
|
|
|
len = safe_strlen(volume_name);
|
2018-11-28 10:50:00 +00:00
|
|
|
assert(len > 4);
|
|
|
|
assert(safe_strnicmp(volume_name, volume_start, 4) == 0);
|
|
|
|
assert(volume_name[len - 1] == '\\');
|
2013-04-07 23:10:58 +00:00
|
|
|
|
|
|
|
drive_type = GetDriveTypeA(volume_name);
|
2013-11-14 01:21:50 +00:00
|
|
|
if ((drive_type != DRIVE_REMOVABLE) && (drive_type != DRIVE_FIXED))
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
volume_name[len-1] = 0;
|
|
|
|
|
|
|
|
if (QueryDosDeviceA(&volume_name[4], path, sizeof(path)) == 0) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Failed to get device path for GUID volume '%s': %s", volume_name, WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
2013-04-07 23:10:58 +00:00
|
|
|
|
|
|
|
for (j=0; (j<ARRAYSIZE(ignore_device)) &&
|
2014-05-09 21:05:25 +00:00
|
|
|
(_strnicmp(path, ignore_device[j], safe_strlen(ignore_device[j])) != 0); j++);
|
2013-04-07 23:10:58 +00:00
|
|
|
if (j < ARRAYSIZE(ignore_device)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Skipping GUID volume for '%s'", path);
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 19:17:39 +00:00
|
|
|
hDrive = CreateFileA(volume_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
2013-04-07 23:10:58 +00:00
|
|
|
if (hDrive == INVALID_HANDLE_VALUE) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not open GUID volume '%s': %s", volume_name, WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-02-02 19:47:08 +00:00
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
if ((!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
|
|
|
|
&DiskExtents, sizeof(DiskExtents), &size, NULL)) || (size <= 0)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not get Disk Extents: %s", WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
safe_closehandle(hDrive);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
safe_closehandle(hDrive);
|
2019-04-25 17:58:55 +00:00
|
|
|
if (DiskExtents.NumberOfDiskExtents == 0) {
|
|
|
|
suprintf("Ignoring volume '%s' because it has no extents...", volume_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (DiskExtents.NumberOfDiskExtents != 1) {
|
|
|
|
// If we have more than one extent for a volume, it means that someone
|
|
|
|
// is using RAID-1 or something => Stay well away from such a volume!
|
|
|
|
suprintf("Ignoring volume '%s' because it has more than one extent (RAID?)...", volume_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (DiskExtents.Extents[0].DiskNumber != DriveIndex)
|
|
|
|
// Not on our disk
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (found_name.Index == MAX_PARTITIONS) {
|
|
|
|
uprintf("Error: Trying to process a disk with more than %d partitions!", MAX_PARTITIONS);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bKeepTrailingBackslash)
|
|
|
|
volume_name[len - 1] = '\\';
|
|
|
|
found_offset[found_name.Index] = DiskExtents.Extents[0].StartingOffset.QuadPart;
|
|
|
|
StrArrayAdd(&found_name, volume_name, TRUE);
|
2019-08-13 08:04:31 +00:00
|
|
|
if (!bSilent) {
|
|
|
|
if (bPrintHeader) {
|
|
|
|
bPrintHeader = FALSE;
|
|
|
|
uuprintf("Windows volumes from this device:");
|
|
|
|
}
|
|
|
|
uuprintf("● %s @%lld", volume_name, DiskExtents.Extents[0].StartingOffset.QuadPart);
|
|
|
|
}
|
2019-04-25 17:58:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (found_name.Index == 0)
|
|
|
|
goto out;
|
|
|
|
|
2019-08-18 13:13:09 +00:00
|
|
|
// Now process all the volumes we found, and try to match one with our partition offset
|
|
|
|
for (i = 0; (i < found_name.Index) && (PartitionOffset != 0) && (PartitionOffset != found_offset[i]); i++);
|
|
|
|
|
2019-09-14 22:33:46 +00:00
|
|
|
if (i < found_name.Index) {
|
2019-04-25 17:58:55 +00:00
|
|
|
ret = safe_strdup(found_name.String[i]);
|
2019-09-14 22:33:46 +00:00
|
|
|
} else {
|
2019-08-18 13:13:09 +00:00
|
|
|
// NB: We need to re-add DRIVE_INDEX_MIN for this call since CheckDriveIndex() substracted it
|
|
|
|
ret = AltGetLogicalName(DriveIndex + DRIVE_INDEX_MIN, PartitionOffset, bKeepTrailingBackslash, bSilent);
|
2019-09-14 22:33:46 +00:00
|
|
|
if ((ret != NULL) && (strchr(ret, ' ') != NULL))
|
|
|
|
uprintf("Warning: Using physical device to access partition data");
|
|
|
|
}
|
2012-02-02 19:47:08 +00:00
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
out:
|
|
|
|
if (hVolume != INVALID_HANDLE_VALUE)
|
|
|
|
FindVolumeClose(hVolume);
|
2019-04-25 17:58:55 +00:00
|
|
|
StrArrayDestroy(&found_name);
|
|
|
|
return ret;
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
2011-12-01 17:54:35 +00:00
|
|
|
|
2019-08-18 13:13:09 +00:00
|
|
|
/*
|
|
|
|
* Alternative version of the above, needed because some volumes, such as ESPs, are not listed
|
|
|
|
* by Windows, be it with VDS or other APIs.
|
|
|
|
* For these, we return the "\\?\GLOBALROOT\Device\HarddiskVolume#" identifier that matches
|
|
|
|
* our "Harddisk#Partition#", as reported by QueryDosDevice().
|
|
|
|
* The returned string is allocated and must be freed.
|
|
|
|
*/
|
|
|
|
char* AltGetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent)
|
|
|
|
{
|
2019-09-14 22:33:46 +00:00
|
|
|
BOOL matching_drive = (DriveIndex == SelectedDrive.DeviceNumber);
|
2019-08-18 13:13:09 +00:00
|
|
|
DWORD i;
|
2019-09-14 22:33:46 +00:00
|
|
|
char *ret = NULL, volume_name[MAX_PATH], path[64];
|
2019-08-18 13:13:09 +00:00
|
|
|
|
|
|
|
CheckDriveIndex(DriveIndex);
|
|
|
|
|
|
|
|
// Match the offset to a partition index
|
2019-09-14 22:33:46 +00:00
|
|
|
if (PartitionOffset == 0) {
|
|
|
|
i = 0;
|
|
|
|
} else if (matching_drive) {
|
|
|
|
for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.PartitionOffset[i]); i++);
|
|
|
|
if (i >= MAX_PARTITIONS) {
|
|
|
|
suprintf("Error: Could not find a partition at offset %lld on this disk", PartitionOffset);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
suprintf("Error: Searching for a partition on a non matching disk");
|
2019-08-18 13:13:09 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
static_sprintf(path, "Harddisk%luPartition%lu", DriveIndex, i + 1);
|
|
|
|
static_strcpy(volume_name, groot_name);
|
|
|
|
if (!QueryDosDeviceA(path, &volume_name[groot_len], (DWORD)(MAX_PATH - groot_len)) || (strlen(volume_name) < 20)) {
|
2019-09-14 22:33:46 +00:00
|
|
|
suprintf("Could not find a DOS volume name for '%s': %s", path, WindowsErrorString());
|
|
|
|
// If we are on the right drive, we enable a custom access mode through physical + offset
|
|
|
|
if (!matching_drive)
|
|
|
|
goto out;
|
|
|
|
static_sprintf(volume_name, "\\\\.\\PhysicalDrive%lu%s %I64u %I64u", DriveIndex, bKeepTrailingBackslash ? "\\" : "",
|
|
|
|
SelectedDrive.PartitionOffset[i], SelectedDrive.PartitionSize[i]);
|
|
|
|
} else if (bKeepTrailingBackslash) {
|
|
|
|
static_strcat(volume_name, "\\");
|
2019-08-18 13:13:09 +00:00
|
|
|
}
|
2019-09-14 22:33:46 +00:00
|
|
|
ret = safe_strdup(volume_name);
|
2019-08-18 13:13:09 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-05-23 12:09:25 +00:00
|
|
|
/*
|
|
|
|
* Call on VDS to refresh the drive layout
|
|
|
|
*/
|
|
|
|
BOOL RefreshLayout(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
BOOL r = FALSE;
|
|
|
|
HRESULT hr;
|
|
|
|
wchar_t wPhysicalName[24];
|
|
|
|
IVdsServiceLoader *pLoader;
|
|
|
|
IVdsService *pService;
|
|
|
|
IEnumVdsObject *pEnum;
|
|
|
|
|
|
|
|
CheckDriveIndex(DriveIndex);
|
|
|
|
wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex);
|
|
|
|
|
|
|
|
// Initialize COM
|
|
|
|
IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
|
|
|
|
IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
|
|
|
|
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
|
|
|
|
|
|
|
|
// Create a VDS Loader Instance
|
|
|
|
hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
|
|
|
|
&IID_IVdsServiceLoader, (void **)&pLoader);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the VDS Service
|
|
|
|
hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
|
|
|
|
IVdsServiceLoader_Release(pLoader);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not load VDS Service: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the Service to become ready if needed
|
|
|
|
hr = IVdsService_WaitForServiceReady(pService);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("VDS Service is not ready: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query the VDS Service Providers
|
|
|
|
hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS Service Providers: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove mountpoints
|
|
|
|
hr = IVdsService_CleanupObsoleteMountPoints(pService);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not clean up VDS mountpoints: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke layout refresh
|
|
|
|
hr = IVdsService_Refresh(pService);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not refresh VDS layout: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Force re-enum
|
|
|
|
hr = IVdsService_Reenumerate(pService);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not refresh VDS layout: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
r = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-11-28 10:50:00 +00:00
|
|
|
/*
|
|
|
|
* Delete all the partitions from a disk, using VDS
|
|
|
|
* Mostly copied from https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom?forum=vcgeneral
|
|
|
|
*/
|
|
|
|
BOOL DeletePartitions(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
BOOL r = FALSE;
|
|
|
|
HRESULT hr;
|
|
|
|
ULONG ulFetched;
|
|
|
|
wchar_t wPhysicalName[24];
|
|
|
|
IVdsServiceLoader *pLoader;
|
|
|
|
IVdsService *pService;
|
|
|
|
IEnumVdsObject *pEnum;
|
|
|
|
IUnknown *pUnk;
|
|
|
|
|
|
|
|
CheckDriveIndex(DriveIndex);
|
|
|
|
wnsprintf(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex);
|
|
|
|
|
|
|
|
// Initialize COM
|
|
|
|
IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
|
|
|
|
IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
|
|
|
|
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
|
|
|
|
|
|
|
|
// Create a VDS Loader Instance
|
|
|
|
hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
|
|
|
|
&IID_IVdsServiceLoader, (void **)&pLoader);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the VDS Service
|
|
|
|
hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
|
|
|
|
IVdsServiceLoader_Release(pLoader);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not load VDS Service: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-05-23 12:09:25 +00:00
|
|
|
// Wait for the Service to become ready if needed
|
|
|
|
hr = IVdsService_WaitForServiceReady(pService);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("VDS Service is not ready: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-11-28 10:50:00 +00:00
|
|
|
// Query the VDS Service Providers
|
|
|
|
hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS Service Providers: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
|
|
|
|
IVdsProvider *pProvider;
|
|
|
|
IVdsSwProvider *pSwProvider;
|
|
|
|
IEnumVdsObject *pEnumPack;
|
|
|
|
IUnknown *pPackUnk;
|
|
|
|
|
|
|
|
// Get VDS Provider
|
|
|
|
hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider);
|
|
|
|
IUnknown_Release(pUnk);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not get VDS Provider: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get VDS Software Provider
|
|
|
|
hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider);
|
|
|
|
IVdsProvider_Release(pProvider);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not get VDS Software Provider: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get VDS Software Provider Packs
|
|
|
|
hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
|
|
|
|
IVdsSwProvider_Release(pSwProvider);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not get VDS Software Provider Packs: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enumerate Provider Packs
|
|
|
|
while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
|
|
|
|
IVdsPack *pPack;
|
|
|
|
IEnumVdsObject *pEnumDisk;
|
|
|
|
IUnknown *pDiskUnk;
|
|
|
|
|
|
|
|
hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack);
|
|
|
|
IUnknown_Release(pPackUnk);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS Software Provider Pack: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use the pack interface to access the disks
|
|
|
|
hr = IVdsPack_QueryDisks(pPack, &pEnumDisk);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS disks: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// List disks
|
|
|
|
while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFetched) == S_OK) {
|
|
|
|
VDS_DISK_PROP diskprop;
|
|
|
|
VDS_PARTITION_PROP* prop_array;
|
|
|
|
LONG i, prop_array_size;
|
|
|
|
IVdsDisk *pDisk;
|
|
|
|
IVdsAdvancedDisk *pAdvancedDisk;
|
|
|
|
|
|
|
|
// Get the disk interface.
|
|
|
|
hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void **)&pDisk);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS Disk Interface: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the disk properties
|
|
|
|
hr = IVdsDisk_GetProperties(pDisk, &diskprop);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not query VDS Disk Properties: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Isolate the disk we want
|
|
|
|
if (_wcsicmp(wPhysicalName, diskprop.pwszName) != 0) {
|
|
|
|
IVdsDisk_Release(pDisk);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instantiate the AdvanceDisk interface for our disk.
|
|
|
|
hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk, (void **)&pAdvancedDisk);
|
|
|
|
IVdsDisk_Release(pDisk);
|
|
|
|
if (hr != S_OK) {
|
2019-04-25 17:58:55 +00:00
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not access VDS Advanced Disk interface: %s", WindowsErrorString());
|
2018-11-28 10:50:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query the partition data, so we can get the start offset, which we need for deletion
|
|
|
|
hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size);
|
2019-04-25 17:58:55 +00:00
|
|
|
if (hr == S_OK) {
|
|
|
|
uprintf("Deleting ALL partition(s) from disk '%S':", diskprop.pwszName);
|
|
|
|
// Now go through each partition
|
|
|
|
for (i = 0; i < prop_array_size; i++) {
|
|
|
|
uprintf("● Partition %d (offset: %lld, size: %s)", prop_array[i].ulPartitionNumber,
|
|
|
|
prop_array[i].ullOffset, SizeToHumanReadable(prop_array[i].ullSize, FALSE, FALSE));
|
|
|
|
hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
r = FALSE;
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not delete partitions: %s", WindowsErrorString());
|
|
|
|
}
|
2018-11-28 10:50:00 +00:00
|
|
|
}
|
2019-04-25 17:58:55 +00:00
|
|
|
r = TRUE;
|
|
|
|
} else {
|
|
|
|
uprintf("No partition to delete on disk '%S'", diskprop.pwszName);
|
|
|
|
r = TRUE;
|
2018-11-28 10:50:00 +00:00
|
|
|
}
|
|
|
|
CoTaskMemFree(prop_array);
|
|
|
|
|
|
|
|
#if 0
|
2019-04-25 17:58:55 +00:00
|
|
|
// Issue a Clean while we're at it
|
2018-11-28 10:50:00 +00:00
|
|
|
HRESULT hr2 = E_FAIL;
|
2019-04-25 17:58:55 +00:00
|
|
|
ULONG completed;
|
|
|
|
IVdsAsync* pAsync;
|
|
|
|
hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, FALSE, FALSE, &pAsync);
|
|
|
|
while (SUCCEEDED(hr)) {
|
|
|
|
if (IS_ERROR(FormatStatus)) {
|
|
|
|
IVdsAsync_Cancel(pAsync);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed);
|
2018-11-28 10:50:00 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
2019-04-25 17:58:55 +00:00
|
|
|
hr = hr2;
|
|
|
|
if (hr == S_OK)
|
2018-11-28 10:50:00 +00:00
|
|
|
break;
|
2019-04-25 17:58:55 +00:00
|
|
|
if (hr == VDS_E_OPERATION_PENDING)
|
|
|
|
hr = S_OK;
|
2018-11-28 10:50:00 +00:00
|
|
|
}
|
|
|
|
Sleep(500);
|
2019-04-25 17:58:55 +00:00
|
|
|
}
|
|
|
|
if (hr != S_OK) {
|
|
|
|
VDS_SET_ERROR(hr);
|
|
|
|
uprintf("Could not clean disk: %s", WindowsErrorString());
|
|
|
|
}
|
2018-11-28 10:50:00 +00:00
|
|
|
#endif
|
|
|
|
IVdsAdvancedDisk_Release(pAdvancedDisk);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-25 17:39:07 +00:00
|
|
|
/* Wait for a logical drive to reappear - Used when a drive has just been repartitioned */
|
2019-08-18 13:13:09 +00:00
|
|
|
BOOL WaitForLogical(DWORD DriveIndex, uint64_t PartitionOffset)
|
2013-06-25 17:39:07 +00:00
|
|
|
{
|
2017-11-13 14:29:48 +00:00
|
|
|
uint64_t EndTime;
|
2013-06-25 17:39:07 +00:00
|
|
|
char* LogicalPath = NULL;
|
|
|
|
|
2017-04-11 21:15:05 +00:00
|
|
|
// GetLogicalName() calls may be slow, so use the system time to
|
|
|
|
// make sure we don't spend more than DRIVE_ACCESS_TIMEOUT in wait.
|
2017-11-13 14:29:48 +00:00
|
|
|
EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT;
|
2017-04-11 21:15:05 +00:00
|
|
|
do {
|
2019-08-18 13:13:09 +00:00
|
|
|
LogicalPath = GetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE);
|
2019-04-25 17:58:55 +00:00
|
|
|
// Need to filter out GlobalRoot devices as we don't want to wait on those
|
|
|
|
if ((LogicalPath != NULL) && (strncmp(LogicalPath, groot_name, groot_len) != 0)) {
|
2013-06-25 17:39:07 +00:00
|
|
|
free(LogicalPath);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2019-04-25 17:58:55 +00:00
|
|
|
free(LogicalPath);
|
2013-06-25 17:39:07 +00:00
|
|
|
if (IS_ERROR(FormatStatus)) // User cancel
|
|
|
|
return FALSE;
|
2019-08-13 08:04:31 +00:00
|
|
|
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
|
2017-11-13 14:29:48 +00:00
|
|
|
} while (GetTickCount64() < EndTime);
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Timeout while waiting for logical drive");
|
2013-06-25 17:39:07 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-11-05 22:54:38 +00:00
|
|
|
/*
|
2019-04-25 17:58:55 +00:00
|
|
|
* Obtain a handle to the volume identified by DriveIndex + PartitionIndex
|
2013-06-25 01:55:25 +00:00
|
|
|
* Returns INVALID_HANDLE_VALUE on error or NULL if no logical path exists (typical
|
|
|
|
* of unpartitioned drives)
|
2013-04-07 23:10:58 +00:00
|
|
|
*/
|
2019-08-18 13:13:09 +00:00
|
|
|
HANDLE GetLogicalHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
|
2013-04-07 23:10:58 +00:00
|
|
|
{
|
|
|
|
HANDLE hLogical = INVALID_HANDLE_VALUE;
|
2019-08-18 13:13:09 +00:00
|
|
|
char* LogicalPath = GetLogicalName(DriveIndex, PartitionOffset, FALSE, FALSE);
|
2013-06-25 17:39:07 +00:00
|
|
|
|
2013-06-25 01:55:25 +00:00
|
|
|
if (LogicalPath == NULL) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("No logical drive found (unpartitioned?)");
|
2013-06-25 01:55:25 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-04-08 20:58:10 +00:00
|
|
|
hLogical = GetHandle(LogicalPath, bLockDrive, bWriteAccess, bWriteShare);
|
2014-05-09 21:05:25 +00:00
|
|
|
free(LogicalPath);
|
2013-04-07 23:10:58 +00:00
|
|
|
return hLogical;
|
|
|
|
}
|
|
|
|
|
2014-02-27 19:53:53 +00:00
|
|
|
/*
|
|
|
|
* Who would have thought that Microsoft would make it so unbelievably hard to
|
|
|
|
* get the frickin' device number for a drive? You have to use TWO different
|
|
|
|
* methods to have a chance to get it!
|
|
|
|
*/
|
|
|
|
int GetDriveNumber(HANDLE hDrive, char* path)
|
|
|
|
{
|
|
|
|
STORAGE_DEVICE_NUMBER_REDEF DeviceNumber;
|
|
|
|
VOLUME_DISK_EXTENTS_REDEF DiskExtents;
|
|
|
|
DWORD size;
|
|
|
|
int r = -1;
|
|
|
|
|
|
|
|
if (!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
|
|
|
|
&DiskExtents, sizeof(DiskExtents), &size, NULL) || (size <= 0) || (DiskExtents.NumberOfDiskExtents < 1) ) {
|
|
|
|
// DiskExtents are NO_GO (which is the case for external USB HDDs...)
|
|
|
|
if(!DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
|
|
|
|
&DeviceNumber, sizeof(DeviceNumber), &size, NULL ) || (size <= 0)) {
|
|
|
|
uprintf("Could not get device number for device %s: %s", path, WindowsErrorString());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
r = (int)DeviceNumber.DeviceNumber;
|
|
|
|
} else if (DiskExtents.NumberOfDiskExtents >= 2) {
|
|
|
|
uprintf("Ignoring drive '%s' as it spans multiple disks (RAID?)", path);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
r = (int)DiskExtents.Extents[0].DiskNumber;
|
|
|
|
}
|
|
|
|
if (r >= MAX_DRIVES) {
|
|
|
|
uprintf("Device Number for device %s is too big (%d) - ignoring device", path, r);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
/*
|
2014-01-14 20:17:07 +00:00
|
|
|
* Returns the drive letters for all volumes located on the drive identified by DriveIndex,
|
2013-11-17 01:39:43 +00:00
|
|
|
* as well as the drive type. This is used as base for the 2 function calls that follow.
|
2013-04-07 23:10:58 +00:00
|
|
|
*/
|
2014-01-14 20:17:07 +00:00
|
|
|
static BOOL _GetDriveLettersAndType(DWORD DriveIndex, char* drive_letters, UINT* drive_type)
|
2013-04-07 23:10:58 +00:00
|
|
|
{
|
|
|
|
DWORD size;
|
2014-02-27 19:53:53 +00:00
|
|
|
BOOL r = FALSE;
|
2020-04-10 12:16:57 +00:00
|
|
|
HANDLE hDrive = INVALID_HANDLE_VALUE, hPhysical = INVALID_HANDLE_VALUE;
|
2013-11-17 01:39:43 +00:00
|
|
|
UINT _drive_type;
|
2018-04-02 13:29:02 +00:00
|
|
|
IO_STATUS_BLOCK io_status_block;
|
|
|
|
FILE_FS_DEVICE_INFORMATION file_fs_device_info;
|
2020-04-10 12:16:57 +00:00
|
|
|
BYTE geometry[256] = { 0 };
|
|
|
|
PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
|
|
|
|
int i = 0, drives_found = 0, drive_number;
|
2014-09-08 17:23:50 +00:00
|
|
|
char *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */
|
2013-04-07 23:10:58 +00:00
|
|
|
char logical_drive[] = "\\\\.\\#:";
|
2013-11-14 01:21:50 +00:00
|
|
|
|
2018-04-02 13:29:02 +00:00
|
|
|
PF_INIT(NtQueryVolumeInformationFile, Ntdll);
|
|
|
|
|
2014-01-14 20:17:07 +00:00
|
|
|
if (drive_letters != NULL)
|
|
|
|
drive_letters[0] = 0;
|
2013-11-17 01:39:43 +00:00
|
|
|
if (drive_type != NULL)
|
|
|
|
*drive_type = DRIVE_UNKNOWN;
|
2013-04-07 23:10:58 +00:00
|
|
|
CheckDriveIndex(DriveIndex);
|
|
|
|
|
2014-09-08 17:23:50 +00:00
|
|
|
// This call is weird... The buffer needs to have an extra NUL, but you're
|
|
|
|
// supposed to provide the size without the extra NUL. And the returned size
|
|
|
|
// does not include the NUL either *EXCEPT* if your buffer is too small...
|
|
|
|
// But then again, this doesn't hold true if you have a 105 byte buffer and
|
|
|
|
// pass a 4*26=104 size, as the the call will return 105 (i.e. *FAILURE*)
|
|
|
|
// instead of 104 as it should => screw Microsoft: We'll include the NUL
|
|
|
|
// always, as each drive string is at least 4 chars long anyway.
|
2013-04-07 23:10:58 +00:00
|
|
|
size = GetLogicalDriveStringsA(sizeof(drives), drives);
|
|
|
|
if (size == 0) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (size > sizeof(drives)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
|
2013-04-07 23:10:58 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-02-25 19:13:49 +00:00
|
|
|
r = TRUE; // Required to detect drives that don't have volumes assigned
|
2020-04-10 12:16:57 +00:00
|
|
|
for (drive = drives ;*drive; drive += safe_strlen(drive) + 1) {
|
2013-04-07 23:10:58 +00:00
|
|
|
if (!isalpha(*drive))
|
|
|
|
continue;
|
|
|
|
*drive = (char)toupper((int)*drive);
|
|
|
|
|
2016-02-16 17:47:07 +00:00
|
|
|
// IOCTL_STORAGE_GET_DEVICE_NUMBER's STORAGE_DEVICE_NUMBER.DeviceNumber is
|
|
|
|
// not unique! An HDD, a DVD and probably other drives can have the same
|
|
|
|
// value there => Use GetDriveType() to filter out unwanted devices.
|
|
|
|
// See https://github.com/pbatard/rufus/issues/32#issuecomment-3785956
|
2013-11-17 01:39:43 +00:00
|
|
|
_drive_type = GetDriveTypeA(drive);
|
2013-11-14 01:21:50 +00:00
|
|
|
|
2013-11-17 01:39:43 +00:00
|
|
|
if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED))
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
|
|
|
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(logical_drive, "\\\\.\\%c:", drive[0]);
|
2014-11-11 19:17:39 +00:00
|
|
|
hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
2011-12-01 17:54:35 +00:00
|
|
|
if (hDrive == INVALID_HANDLE_VALUE) {
|
2017-09-08 14:38:30 +00:00
|
|
|
// uprintf("Warning: could not open drive %c: %s", drive[0], WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
continue;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
2013-04-07 23:10:58 +00:00
|
|
|
|
2018-04-02 13:29:02 +00:00
|
|
|
// Eliminate floppy drives
|
|
|
|
if ((pfNtQueryVolumeInformationFile != NULL) &&
|
|
|
|
(pfNtQueryVolumeInformationFile(hDrive, &io_status_block, &file_fs_device_info,
|
|
|
|
sizeof(file_fs_device_info), FileFsDeviceInformation) == NO_ERROR) &&
|
|
|
|
(file_fs_device_info.Characteristics & FILE_FLOPPY_DISKETTE) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-02-27 21:40:12 +00:00
|
|
|
drive_number = GetDriveNumber(hDrive, logical_drive);
|
|
|
|
safe_closehandle(hDrive);
|
|
|
|
if (drive_number == DriveIndex) {
|
2014-02-25 19:13:49 +00:00
|
|
|
r = TRUE;
|
2020-04-10 12:16:57 +00:00
|
|
|
drives_found++;
|
2014-01-14 20:17:07 +00:00
|
|
|
if (drive_letters != NULL)
|
|
|
|
drive_letters[i++] = *drive;
|
|
|
|
// The drive type should be the same for all volumes, so we can overwrite
|
2013-11-17 01:39:43 +00:00
|
|
|
if (drive_type != NULL)
|
|
|
|
*drive_type = _drive_type;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-10 12:16:57 +00:00
|
|
|
// Devices that don't have mounted partitions require special
|
|
|
|
// handling to determine if they are fixed or removable.
|
|
|
|
if ((drives_found == 0) && (drive_type != NULL)) {
|
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex + DRIVE_INDEX_MIN, FALSE, FALSE, FALSE);
|
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
|
|
NULL, 0, geometry, sizeof(geometry), &size, NULL);
|
|
|
|
safe_closehandle(hPhysical);
|
|
|
|
if (r && size > 0) {
|
|
|
|
if (DiskGeometry->Geometry.MediaType == FixedMedia)
|
|
|
|
*drive_type = DRIVE_FIXED;
|
|
|
|
else if (DiskGeometry->Geometry.MediaType == RemovableMedia)
|
|
|
|
*drive_type = DRIVE_REMOVABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
out:
|
2014-01-14 20:17:07 +00:00
|
|
|
if (drive_letters != NULL)
|
|
|
|
drive_letters[i] = 0;
|
2013-11-14 01:21:50 +00:00
|
|
|
return r;
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
|
|
|
|
2013-11-17 01:39:43 +00:00
|
|
|
// Could have used a #define, but this is clearer
|
2014-01-14 20:17:07 +00:00
|
|
|
BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters)
|
2013-11-17 01:39:43 +00:00
|
|
|
{
|
2014-01-14 20:17:07 +00:00
|
|
|
return _GetDriveLettersAndType(DriveIndex, drive_letters, NULL);
|
2013-11-17 01:39:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// There's already a GetDriveType in the Windows API
|
|
|
|
UINT GetDriveTypeFromIndex(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
UINT drive_type;
|
2014-01-14 20:17:07 +00:00
|
|
|
_GetDriveLettersAndType(DriveIndex, NULL, &drive_type);
|
2013-11-17 01:39:43 +00:00
|
|
|
return drive_type;
|
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
/*
|
2020-06-06 21:19:20 +00:00
|
|
|
* Return the next unused drive letter from the system or NUL on error.
|
2013-04-07 23:10:58 +00:00
|
|
|
*/
|
|
|
|
char GetUnusedDriveLetter(void)
|
|
|
|
{
|
|
|
|
DWORD size;
|
2020-06-06 15:34:17 +00:00
|
|
|
char drive_letter, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */
|
2013-04-07 23:10:58 +00:00
|
|
|
|
|
|
|
size = GetLogicalDriveStringsA(sizeof(drives), drives);
|
|
|
|
if (size == 0) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
|
2020-06-06 21:19:20 +00:00
|
|
|
return 0;
|
2013-04-07 23:10:58 +00:00
|
|
|
}
|
|
|
|
if (size > sizeof(drives)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
|
2020-06-06 21:19:20 +00:00
|
|
|
return 0;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
|
2017-09-06 13:00:30 +00:00
|
|
|
for (drive_letter = 'C'; drive_letter <= 'Z'; drive_letter++) {
|
2020-06-06 15:34:17 +00:00
|
|
|
for (drive = drives ; *drive; drive += safe_strlen(drive) + 1) {
|
2013-04-07 23:10:58 +00:00
|
|
|
if (!isalpha(*drive))
|
|
|
|
continue;
|
|
|
|
if (drive_letter == (char)toupper((int)*drive))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!*drive)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-06-06 15:34:17 +00:00
|
|
|
return (drive_letter > 'Z') ? 0 : drive_letter;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL IsDriveLetterInUse(const char drive_letter)
|
|
|
|
{
|
|
|
|
DWORD size;
|
|
|
|
char *drive, drives[26 * 4 + 1];
|
|
|
|
|
|
|
|
size = GetLogicalDriveStringsA(sizeof(drives), drives);
|
|
|
|
if (size == 0) {
|
|
|
|
uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString());
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (size > sizeof(drives)) {
|
|
|
|
uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives));
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (drive = drives; *drive; drive += safe_strlen(drive) + 1) {
|
|
|
|
if (drive_letter == (char)toupper((int)*drive))
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the drive letter and volume label
|
2013-04-07 23:10:58 +00:00
|
|
|
* If the drive doesn't have a volume assigned, space is returned for the letter
|
2011-12-01 17:54:35 +00:00
|
|
|
*/
|
2014-01-14 20:17:07 +00:00
|
|
|
BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label)
|
2011-12-01 17:54:35 +00:00
|
|
|
{
|
2013-04-07 23:10:58 +00:00
|
|
|
HANDLE hPhysical;
|
2019-05-03 22:51:05 +00:00
|
|
|
DWORD size, error;
|
2019-07-15 11:35:22 +00:00
|
|
|
static char VolumeLabel[MAX_PATH + 1] = { 0 };
|
2016-06-26 20:24:16 +00:00
|
|
|
char DrivePath[] = "#:\\", AutorunPath[] = "#:\\autorun.inf", *AutorunLabel = NULL;
|
2019-07-15 11:35:22 +00:00
|
|
|
WCHAR VolumeName[MAX_PATH + 1] = { 0 }, FileSystemName[64];
|
|
|
|
DWORD VolumeSerialNumber, MaximumComponentLength, FileSystemFlags;
|
2011-12-01 17:54:35 +00:00
|
|
|
|
|
|
|
*label = STR_NO_LABEL;
|
|
|
|
|
2014-01-14 20:17:07 +00:00
|
|
|
if (!GetDriveLetters(DriveIndex, letters))
|
2013-11-14 01:21:50 +00:00
|
|
|
return FALSE;
|
2014-01-14 20:17:07 +00:00
|
|
|
if (letters[0] == 0) {
|
2019-07-15 11:35:22 +00:00
|
|
|
// Even if we don't have a letter, try to obtain the label of the first partition
|
|
|
|
HANDLE h = GetLogicalHandle(DriveIndex, 0, FALSE, FALSE, FALSE);
|
|
|
|
if (GetVolumeInformationByHandleW(h, VolumeName, 64, &VolumeSerialNumber,
|
|
|
|
&MaximumComponentLength, &FileSystemFlags, FileSystemName, 64)) {
|
|
|
|
wchar_to_utf8_no_alloc(VolumeName, VolumeLabel, sizeof(VolumeLabel));
|
2019-09-14 22:33:46 +00:00
|
|
|
*label = (VolumeLabel[0] != 0) ? VolumeLabel : STR_NO_LABEL;
|
2019-07-15 11:35:22 +00:00
|
|
|
}
|
2020-04-28 16:31:34 +00:00
|
|
|
safe_closehandle(h);
|
2013-11-14 01:21:50 +00:00
|
|
|
// Drive without volume assigned - always enabled
|
|
|
|
return TRUE;
|
2013-02-10 21:54:47 +00:00
|
|
|
}
|
2014-01-14 20:17:07 +00:00
|
|
|
// We only care about an autorun.inf if we have a single volume
|
|
|
|
AutorunPath[0] = letters[0];
|
2016-06-26 20:24:16 +00:00
|
|
|
DrivePath[0] = letters[0];
|
2011-12-01 17:54:35 +00:00
|
|
|
|
2012-03-01 19:19:12 +00:00
|
|
|
// Try to read an extended label from autorun first. Fallback to regular label if not found.
|
2012-03-09 01:38:52 +00:00
|
|
|
// In the case of card readers with no card, users can get an annoying popup asking them
|
|
|
|
// to insert media. Use IOCTL_STORAGE_CHECK_VERIFY to prevent this
|
2017-05-01 23:56:07 +00:00
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
|
2012-03-09 01:38:52 +00:00
|
|
|
if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL))
|
2012-11-12 01:53:34 +00:00
|
|
|
AutorunLabel = get_token_data_file("label", AutorunPath);
|
2012-03-09 01:38:52 +00:00
|
|
|
else if (GetLastError() == ERROR_NOT_READY)
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Ignoring autorun.inf label for drive %c: %s", letters[0],
|
2012-03-09 01:38:52 +00:00
|
|
|
(HRESULT_CODE(GetLastError()) == ERROR_NOT_READY)?"No media":WindowsErrorString());
|
|
|
|
safe_closehandle(hPhysical);
|
2012-03-01 19:19:12 +00:00
|
|
|
if (AutorunLabel != NULL) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Using autorun.inf label for drive %c: '%s'", letters[0], AutorunLabel);
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(VolumeLabel, AutorunLabel);
|
2012-03-01 19:19:12 +00:00
|
|
|
safe_free(AutorunLabel);
|
|
|
|
*label = VolumeLabel;
|
2016-06-26 20:24:16 +00:00
|
|
|
} else if (GetVolumeInformationU(DrivePath, VolumeLabel, ARRAYSIZE(VolumeLabel),
|
|
|
|
NULL, NULL, NULL, NULL, 0) && (VolumeLabel[0] != 0)) {
|
2012-02-21 19:46:28 +00:00
|
|
|
*label = VolumeLabel;
|
2016-06-26 20:24:16 +00:00
|
|
|
} else {
|
2019-05-03 22:51:05 +00:00
|
|
|
// Might be an extfs label
|
|
|
|
error = GetLastError();
|
|
|
|
*label = (char*)GetExtFsLabel(DriveIndex, 0);
|
|
|
|
if (*label == NULL) {
|
|
|
|
SetLastError(error);
|
|
|
|
duprintf("Failed to read label: %s", WindowsErrorString());
|
|
|
|
*label = STR_NO_LABEL;
|
|
|
|
}
|
2011-12-01 17:54:35 +00:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2011-12-11 02:19:38 +00:00
|
|
|
|
2013-11-17 01:39:43 +00:00
|
|
|
/*
|
|
|
|
* Return the drive size
|
|
|
|
*/
|
|
|
|
uint64_t GetDriveSize(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
BOOL r;
|
|
|
|
HANDLE hPhysical;
|
|
|
|
DWORD size;
|
2014-03-29 00:17:41 +00:00
|
|
|
BYTE geometry[256];
|
|
|
|
PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
|
2013-11-17 01:39:43 +00:00
|
|
|
|
2017-05-01 23:56:07 +00:00
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
|
2013-11-17 01:39:43 +00:00
|
|
|
if (hPhysical == INVALID_HANDLE_VALUE)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
|
|
|
NULL, 0, geometry, sizeof(geometry), &size, NULL);
|
|
|
|
safe_closehandle(hPhysical);
|
|
|
|
if (!r || size <= 0)
|
|
|
|
return 0;
|
|
|
|
return DiskGeometry->DiskSize.QuadPart;
|
|
|
|
}
|
|
|
|
|
2013-12-20 18:32:10 +00:00
|
|
|
/*
|
|
|
|
* GET_DRIVE_GEOMETRY is used to tell if there is an actual media
|
|
|
|
*/
|
|
|
|
BOOL IsMediaPresent(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
BOOL r;
|
|
|
|
HANDLE hPhysical;
|
|
|
|
DWORD size;
|
|
|
|
BYTE geometry[128];
|
|
|
|
|
2017-05-01 23:56:07 +00:00
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
|
2013-12-20 18:32:10 +00:00
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
2015-09-19 16:00:51 +00:00
|
|
|
NULL, 0, geometry, sizeof(geometry), &size, NULL) && (size > 0);
|
2013-12-20 18:32:10 +00:00
|
|
|
safe_closehandle(hPhysical);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-05-09 20:57:27 +00:00
|
|
|
const struct {int (*fn)(FILE *fp); char* str;} known_mbr[] = {
|
|
|
|
{ is_dos_mbr, "DOS/NT/95A" },
|
|
|
|
{ is_dos_f2_mbr, "DOS/NT/95A (F2)" },
|
|
|
|
{ is_95b_mbr, "Windows 95B/98/98SE/ME" },
|
|
|
|
{ is_2000_mbr, "Windows 2000/XP/2003" },
|
|
|
|
{ is_vista_mbr, "Windows Vista" },
|
|
|
|
{ is_win7_mbr, "Windows 7" },
|
|
|
|
{ is_rufus_mbr, "Rufus" },
|
|
|
|
{ is_syslinux_mbr, "Syslinux" },
|
2014-05-15 20:17:12 +00:00
|
|
|
{ is_reactos_mbr, "ReactOS" },
|
2016-01-07 15:49:58 +00:00
|
|
|
{ is_kolibrios_mbr, "KolibriOS" },
|
|
|
|
{ is_grub4dos_mbr, "Grub4DOS" },
|
2014-11-14 23:40:00 +00:00
|
|
|
{ is_grub2_mbr, "Grub 2.0" },
|
2016-01-07 15:49:58 +00:00
|
|
|
{ is_zero_mbr_not_including_disk_signature_or_copy_protect, "Zeroed" },
|
2014-02-09 02:54:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Returns TRUE if the drive seems bootable, FALSE otherwise
|
2018-10-03 17:14:40 +00:00
|
|
|
BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent)
|
2014-01-05 01:39:41 +00:00
|
|
|
{
|
2014-02-09 02:54:07 +00:00
|
|
|
const char* mbr_name = "Master Boot Record";
|
2015-08-10 22:19:57 +00:00
|
|
|
FAKE_FD fake_fd = { 0 };
|
|
|
|
FILE* fp = (FILE*)&fake_fd;
|
2014-02-09 02:54:07 +00:00
|
|
|
int i;
|
2014-01-05 01:39:41 +00:00
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
fake_fd._handle = (char*)hPhysicalDrive;
|
2016-05-23 11:19:11 +00:00
|
|
|
set_bytes_per_sector(SelectedDrive.SectorSize);
|
2014-01-05 01:39:41 +00:00
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
if (!is_br(fp)) {
|
2018-10-03 17:14:40 +00:00
|
|
|
suprintf("%s does not have an x86 %s", TargetName, mbr_name);
|
2014-01-05 01:39:41 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-02-09 02:54:07 +00:00
|
|
|
for (i=0; i<ARRAYSIZE(known_mbr); i++) {
|
2015-08-10 22:19:57 +00:00
|
|
|
if (known_mbr[i].fn(fp)) {
|
2018-10-03 17:14:40 +00:00
|
|
|
suprintf("%s has a %s %s", TargetName, known_mbr[i].str, mbr_name);
|
2014-05-09 20:57:27 +00:00
|
|
|
return TRUE;
|
2014-02-09 02:54:07 +00:00
|
|
|
}
|
2014-01-05 01:39:41 +00:00
|
|
|
}
|
2014-02-09 02:54:07 +00:00
|
|
|
|
2018-10-03 17:14:40 +00:00
|
|
|
suprintf("%s has an unknown %s", TargetName, mbr_name);
|
2014-01-05 01:39:41 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-05-09 20:57:27 +00:00
|
|
|
const struct {int (*fn)(FILE *fp); char* str;} known_pbr[] = {
|
|
|
|
{ entire_fat_16_br_matches, "FAT16 DOS" },
|
|
|
|
{ entire_fat_16_fd_br_matches, "FAT16 FreeDOS" },
|
|
|
|
{ entire_fat_16_ros_br_matches, "FAT16 ReactOS" },
|
|
|
|
{ entire_fat_32_br_matches, "FAT32 DOS" },
|
|
|
|
{ entire_fat_32_nt_br_matches, "FAT32 NT" },
|
|
|
|
{ entire_fat_32_fd_br_matches, "FAT32 FreeDOS" },
|
|
|
|
{ entire_fat_32_ros_br_matches, "FAT32 ReactOS" },
|
2014-05-15 20:17:12 +00:00
|
|
|
{ entire_fat_32_kos_br_matches, "FAT32 KolibriOS" },
|
2014-05-09 20:57:27 +00:00
|
|
|
};
|
|
|
|
|
2014-01-05 01:39:41 +00:00
|
|
|
BOOL AnalyzePBR(HANDLE hLogicalVolume)
|
|
|
|
{
|
2014-05-09 20:57:27 +00:00
|
|
|
const char* pbr_name = "Partition Boot Record";
|
2015-08-10 22:19:57 +00:00
|
|
|
FAKE_FD fake_fd = { 0 };
|
|
|
|
FILE* fp = (FILE*)&fake_fd;
|
2014-05-09 20:57:27 +00:00
|
|
|
int i;
|
2014-01-05 01:39:41 +00:00
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
fake_fd._handle = (char*)hLogicalVolume;
|
2016-05-23 11:19:11 +00:00
|
|
|
set_bytes_per_sector(SelectedDrive.SectorSize);
|
2014-01-05 01:39:41 +00:00
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
if (!is_br(fp)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Volume does not have an x86 %s", pbr_name);
|
2014-01-05 01:39:41 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-09 20:57:27 +00:00
|
|
|
|
2015-08-10 22:19:57 +00:00
|
|
|
if (is_fat_16_br(fp) || is_fat_32_br(fp)) {
|
2014-05-09 20:57:27 +00:00
|
|
|
for (i=0; i<ARRAYSIZE(known_pbr); i++) {
|
2015-08-10 22:19:57 +00:00
|
|
|
if (known_pbr[i].fn(fp)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Drive has a %s %s", known_pbr[i].str, pbr_name);
|
2014-05-09 20:57:27 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2014-01-05 01:39:41 +00:00
|
|
|
}
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Volume has an unknown FAT16 or FAT32 %s", pbr_name);
|
2014-01-05 01:39:41 +00:00
|
|
|
} else {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Volume has an unknown %s", pbr_name);
|
2014-01-05 01:39:41 +00:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-06-06 15:34:17 +00:00
|
|
|
static BOOL StoreEspInfo(GUID* guid)
|
|
|
|
{
|
|
|
|
uint8_t j;
|
|
|
|
char key_name[2][16], *str;
|
|
|
|
// Look for an empty slot and use that if available
|
|
|
|
for (j = 1; j <= MAX_ESP_TOGGLE; j++) {
|
|
|
|
static_sprintf(key_name[0], "ToggleEsp%02u", j);
|
|
|
|
str = ReadSettingStr(key_name[0]);
|
|
|
|
if ((str == NULL) || (str[0] == 0))
|
|
|
|
return WriteSettingStr(key_name[0], GuidToString(guid));
|
|
|
|
}
|
|
|
|
// All slots are used => Move every key down and add to last slot
|
|
|
|
// NB: No, we don't care that the slot we remove may not be the oldest.
|
|
|
|
for (j = 1; j < MAX_ESP_TOGGLE; j++) {
|
|
|
|
static_sprintf(key_name[0], "ToggleEsp%02u", j);
|
|
|
|
static_sprintf(key_name[1], "ToggleEsp%02u", j + 1);
|
|
|
|
WriteSettingStr(key_name[0], ReadSettingStr(key_name[1]));
|
|
|
|
}
|
|
|
|
return WriteSettingStr(key_name[1], GuidToString(guid));
|
|
|
|
}
|
|
|
|
|
|
|
|
static GUID* GetEspGuid(uint8_t index)
|
|
|
|
{
|
|
|
|
char key_name[16];
|
|
|
|
|
|
|
|
static_sprintf(key_name, "ToggleEsp%02u", index);
|
|
|
|
return StringToGuid(ReadSettingStr(key_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL ClearEspInfo(uint8_t index)
|
|
|
|
{
|
|
|
|
char key_name[16];
|
|
|
|
static_sprintf(key_name, "ToggleEsp%02u", index);
|
|
|
|
return WriteSettingStr(key_name, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This calls changes the type of a GPT ESP back and forth to Basic Data.
|
|
|
|
* Needed because Windows 10 doesn't mount ESPs by default, and also
|
|
|
|
* doesn't let usermode apps (such as File Explorer) access mounted ESPs.
|
|
|
|
*/
|
|
|
|
BOOL ToggleEsp(DWORD DriveIndex)
|
|
|
|
{
|
|
|
|
char *volume_name, mount_point[] = DEFAULT_ESP_MOUNT_POINT;
|
|
|
|
BOOL r, ret = FALSE, found = FALSE;
|
|
|
|
HANDLE hPhysical;
|
|
|
|
DWORD size, i, j, esp_index = 0;
|
|
|
|
BYTE layout[4096] = { 0 };
|
|
|
|
GUID* guid;
|
|
|
|
PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout;
|
|
|
|
|
|
|
|
if (nWindowsVersion < WINDOWS_10) {
|
|
|
|
uprintf("ESP toggling is only available for Windows 10 or later");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex, FALSE, TRUE, TRUE);
|
|
|
|
if (hPhysical == INVALID_HANDLE_VALUE)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
|
|
NULL, 0, layout, sizeof(layout), &size, NULL);
|
|
|
|
if (!r || size <= 0) {
|
|
|
|
uprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (DriveLayout->PartitionStyle != PARTITION_STYLE_GPT) {
|
|
|
|
uprintf("ESP toggling is only available for GPT drives");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if the current drive contains an ESP
|
|
|
|
for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) {
|
|
|
|
if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) {
|
|
|
|
esp_index = i;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j > 1) {
|
|
|
|
uprintf("ESP toggling is not available for drives with more than one ESP");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (j == 1) {
|
|
|
|
// ESP -> Basic Data
|
|
|
|
i = esp_index;
|
|
|
|
uprintf("ESP name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name);
|
|
|
|
if (!StoreEspInfo(&DriveLayout->PartitionEntry[i].Gpt.PartitionId)) {
|
|
|
|
uprintf("ESP toggling data could not be stored");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_MICROSOFT_DATA;
|
|
|
|
} else {
|
|
|
|
// Basic Data -> ESP
|
|
|
|
for (j = 1; j <= MAX_ESP_TOGGLE; j++) {
|
|
|
|
guid = GetEspGuid((uint8_t)j);
|
|
|
|
if (guid != NULL) {
|
|
|
|
for (i = 0; i < DriveLayout->PartitionCount; i++) {
|
|
|
|
if (CompareGUID(guid, &DriveLayout->PartitionEntry[i].Gpt.PartitionId)) {
|
|
|
|
uprintf("BD name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name);
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j > MAX_ESP_TOGGLE)
|
|
|
|
goto out;
|
|
|
|
DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP;
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveLayout->PartitionEntry[i].RewritePartition = TRUE; // Just in case
|
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)DriveLayout, size, NULL, 0, &size, NULL);
|
|
|
|
if (!r) {
|
|
|
|
uprintf("Could not set drive layout: %s", WindowsErrorString());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
RefreshDriveLayout(hPhysical);
|
|
|
|
if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) {
|
|
|
|
// We successfully reverted ESP from Basic Data -> Delete stored ESP info
|
|
|
|
ClearEspInfo((uint8_t)j);
|
|
|
|
} else if (!IsDriveLetterInUse(*mount_point)) {
|
|
|
|
// We succesfully switched ESP to Basic Data -> Try to mount it
|
|
|
|
volume_name = GetLogicalName(DriveIndex, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart, TRUE, FALSE);
|
|
|
|
MountVolume(mount_point, volume_name);
|
|
|
|
free(volume_name);
|
|
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
safe_closehandle(hPhysical);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-01-24 21:30:11 +00:00
|
|
|
/*
|
|
|
|
* Fill the drive properties (size, FS, etc)
|
2014-08-07 00:45:46 +00:00
|
|
|
* Returns TRUE if the drive has a partition that can be mounted in Windows, FALSE otherwise
|
2013-01-24 21:30:11 +00:00
|
|
|
*/
|
2014-08-07 00:45:46 +00:00
|
|
|
BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSystemNameSize, BOOL bSilent)
|
2013-01-24 21:30:11 +00:00
|
|
|
{
|
2014-08-07 00:45:46 +00:00
|
|
|
// MBR partition types that can be mounted in Windows
|
|
|
|
const uint8_t mbr_mountable[] = { 0x01, 0x04, 0x06, 0x07, 0x0b, 0x0c, 0x0e, 0xef };
|
2018-05-08 14:25:53 +00:00
|
|
|
BOOL r, ret = FALSE, isUefiNtfs;
|
2013-04-07 23:10:58 +00:00
|
|
|
HANDLE hPhysical;
|
2018-05-08 14:25:53 +00:00
|
|
|
DWORD size, i, j, super_floppy_disk = FALSE;
|
2015-01-28 23:22:11 +00:00
|
|
|
BYTE geometry[256] = {0}, layout[4096] = {0}, part_type;
|
2014-03-29 00:17:41 +00:00
|
|
|
PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
|
|
|
|
PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout;
|
2019-01-03 12:29:28 +00:00
|
|
|
char *volume_name, *buf;
|
2013-01-24 21:30:11 +00:00
|
|
|
|
2015-01-28 23:22:11 +00:00
|
|
|
if (FileSystemName == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
2014-08-07 00:45:46 +00:00
|
|
|
SelectedDrive.nPartitions = 0;
|
2019-08-18 13:13:09 +00:00
|
|
|
memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
|
2019-09-14 22:33:46 +00:00
|
|
|
memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));
|
2013-07-08 23:14:29 +00:00
|
|
|
// Populate the filesystem data
|
|
|
|
FileSystemName[0] = 0;
|
2019-04-25 17:58:55 +00:00
|
|
|
volume_name = GetLogicalName(DriveIndex, 0, TRUE, FALSE);
|
2013-07-08 23:14:29 +00:00
|
|
|
if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("No volume information for drive 0x%02x", DriveIndex);
|
2013-07-08 23:14:29 +00:00
|
|
|
}
|
|
|
|
safe_free(volume_name);
|
|
|
|
|
2017-05-01 23:56:07 +00:00
|
|
|
hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE);
|
2013-04-07 23:10:58 +00:00
|
|
|
if (hPhysical == INVALID_HANDLE_VALUE)
|
2020-06-06 15:34:17 +00:00
|
|
|
return FALSE;
|
2013-01-24 21:30:11 +00:00
|
|
|
|
2013-11-17 01:39:43 +00:00
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
2013-01-24 21:30:11 +00:00
|
|
|
NULL, 0, geometry, sizeof(geometry), &size, NULL);
|
|
|
|
if (!r || size <= 0) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not get geometry for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
|
2013-04-07 23:10:58 +00:00
|
|
|
safe_closehandle(hPhysical);
|
2020-06-06 15:34:17 +00:00
|
|
|
return FALSE;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
2016-05-23 11:19:11 +00:00
|
|
|
SelectedDrive.DiskSize = DiskGeometry->DiskSize.QuadPart;
|
|
|
|
SelectedDrive.SectorSize = DiskGeometry->Geometry.BytesPerSector;
|
2016-07-07 17:13:01 +00:00
|
|
|
SelectedDrive.FirstDataSector = MAXDWORD;
|
2016-05-23 11:19:11 +00:00
|
|
|
if (SelectedDrive.SectorSize < 512) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Warning: Drive 0x%02x reports a sector size of %d - Correcting to 512 bytes.",
|
2016-05-23 11:19:11 +00:00
|
|
|
DriveIndex, SelectedDrive.SectorSize);
|
|
|
|
SelectedDrive.SectorSize = 512;
|
2014-08-03 18:34:01 +00:00
|
|
|
}
|
2016-05-23 11:19:11 +00:00
|
|
|
SelectedDrive.SectorsPerTrack = DiskGeometry->Geometry.SectorsPerTrack;
|
|
|
|
SelectedDrive.MediaType = DiskGeometry->Geometry.MediaType;
|
|
|
|
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Disk type: %s, Disk size: %s, Sector size: %d bytes", (SelectedDrive.MediaType == FixedMedia)?"FIXED":"Removable",
|
|
|
|
SizeToHumanReadable(SelectedDrive.DiskSize, FALSE, TRUE), SelectedDrive.SectorSize);
|
|
|
|
suprintf("Cylinders: %" PRIi64 ", Tracks per cylinder: %d, Sectors per track: %d",
|
2013-01-24 21:30:11 +00:00
|
|
|
DiskGeometry->Geometry.Cylinders, DiskGeometry->Geometry.TracksPerCylinder, DiskGeometry->Geometry.SectorsPerTrack);
|
|
|
|
|
2015-11-05 22:54:38 +00:00
|
|
|
r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
2013-01-24 21:30:11 +00:00
|
|
|
NULL, 0, layout, sizeof(layout), &size, NULL );
|
|
|
|
if (!r || size <= 0) {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString());
|
2014-05-09 21:05:25 +00:00
|
|
|
safe_closehandle(hPhysical);
|
2020-06-06 15:34:17 +00:00
|
|
|
return FALSE;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
|
|
|
|
2014-12-20 00:22:00 +00:00
|
|
|
#if defined(__GNUC__)
|
2015-09-19 16:00:51 +00:00
|
|
|
// GCC 4.9 bugs us about the fact that MS defined an expandable array as array[1]
|
2014-12-20 00:22:00 +00:00
|
|
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
|
|
|
#endif
|
2013-01-24 21:30:11 +00:00
|
|
|
switch (DriveLayout->PartitionStyle) {
|
|
|
|
case PARTITION_STYLE_MBR:
|
2018-03-26 17:58:22 +00:00
|
|
|
SelectedDrive.PartitionStyle = PARTITION_STYLE_MBR;
|
2020-02-20 11:56:15 +00:00
|
|
|
for (i = 0; i < DriveLayout->PartitionCount; i++) {
|
2013-01-24 21:30:11 +00:00
|
|
|
if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) {
|
2014-08-07 00:45:46 +00:00
|
|
|
SelectedDrive.nPartitions++;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-21 12:06:15 +00:00
|
|
|
// Detect drives that are using the whole disk as a single partition
|
|
|
|
if ((DriveLayout->PartitionEntry[0].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) &&
|
|
|
|
(DriveLayout->PartitionEntry[0].StartingOffset.QuadPart == 0LL)) {
|
2020-02-20 11:56:15 +00:00
|
|
|
suprintf("Partition type: SFD (%s) or unpartitioned", sfd_name);
|
2017-08-12 14:12:00 +00:00
|
|
|
super_floppy_disk = TRUE;
|
2016-08-21 12:06:15 +00:00
|
|
|
} else {
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Partition type: MBR, NB Partitions: %d", SelectedDrive.nPartitions);
|
2016-08-21 12:06:15 +00:00
|
|
|
SelectedDrive.has_mbr_uefi_marker = (DriveLayout->Mbr.Signature == MBR_UEFI_MARKER);
|
2018-05-08 14:25:53 +00:00
|
|
|
suprintf("Disk ID: 0x%08X %s", DriveLayout->Mbr.Signature, SelectedDrive.has_mbr_uefi_marker?"(UEFI target)":"");
|
2018-10-03 17:14:40 +00:00
|
|
|
AnalyzeMBR(hPhysical, "Drive", bSilent);
|
2016-08-21 12:06:15 +00:00
|
|
|
}
|
2020-02-20 11:56:15 +00:00
|
|
|
for (i = 0; i < DriveLayout->PartitionCount; i++) {
|
2018-05-08 14:25:53 +00:00
|
|
|
isUefiNtfs = FALSE;
|
2013-01-24 21:30:11 +00:00
|
|
|
if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) {
|
|
|
|
part_type = DriveLayout->PartitionEntry[i].Mbr.PartitionType;
|
2020-02-20 11:56:15 +00:00
|
|
|
// Microsoft will have to explain why they completely ignore the actual MBR partition
|
|
|
|
// type for zeroed drive (which *IS* 0x00) and fill in Small FAT16 instead (0x04).
|
|
|
|
// This means that if we detect a Small FAT16 "partition", that "starts" at offset 0
|
|
|
|
// and that is larger than 16 MB, our drive is actually unpartitioned.
|
|
|
|
if (part_type == 0x04 && super_floppy_disk && SelectedDrive.DiskSize > 16 * MB)
|
|
|
|
break;
|
2018-05-08 14:25:53 +00:00
|
|
|
if (part_type == 0xef) {
|
|
|
|
// Check the FAT label to see if we're dealing with an UEFI_NTFS partition
|
|
|
|
buf = calloc(SelectedDrive.SectorSize, 1);
|
|
|
|
if (buf != NULL) {
|
2018-05-15 09:23:45 +00:00
|
|
|
if (SetFilePointerEx(hPhysical, DriveLayout->PartitionEntry[i].StartingOffset, NULL, FILE_BEGIN) &&
|
|
|
|
ReadFile(hPhysical, buf, SelectedDrive.SectorSize, &size, NULL)) {
|
|
|
|
isUefiNtfs = (strncmp(&buf[0x2B], "UEFI_NTFS", 9) == 0);
|
|
|
|
}
|
2018-05-08 14:25:53 +00:00
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
}
|
2020-02-20 11:56:15 +00:00
|
|
|
suprintf("Partition %d%s:", i + (super_floppy_disk ? 0 : 1), isUefiNtfs ? " (UEFI:NTFS)" : "");
|
|
|
|
for (j = 0; j < ARRAYSIZE(mbr_mountable); j++) {
|
2014-08-07 00:45:46 +00:00
|
|
|
if (part_type == mbr_mountable[j]) {
|
|
|
|
ret = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-09-14 22:33:46 +00:00
|
|
|
if (i < MAX_PARTITIONS) {
|
2019-08-18 13:13:09 +00:00
|
|
|
SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
|
2019-09-14 22:33:46 +00:00
|
|
|
SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
|
|
|
|
}
|
2015-01-28 23:22:11 +00:00
|
|
|
// NB: MinGW's gcc 4.9.2 broke "%lld" printout on XP so we use the inttypes.h "PRI##" qualifiers
|
2016-08-21 12:06:15 +00:00
|
|
|
suprintf(" Type: %s (0x%02x)\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %" PRIi64 ", Boot: %s",
|
2020-02-20 11:56:15 +00:00
|
|
|
((part_type == 0x07 || super_floppy_disk) && (FileSystemName[0] != 0)) ?
|
|
|
|
FileSystemName : GetMBRPartitionType(part_type), super_floppy_disk ? 0: part_type,
|
2014-03-01 00:09:40 +00:00
|
|
|
SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE),
|
2016-07-07 17:13:01 +00:00
|
|
|
DriveLayout->PartitionEntry[i].PartitionLength.QuadPart,
|
|
|
|
DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize,
|
2016-08-21 12:06:15 +00:00
|
|
|
DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No");
|
2018-05-08 14:25:53 +00:00
|
|
|
// suprintf(" GUID: %s", GuidToString(&DriveLayout->PartitionEntry[i].Mbr.PartitionId));
|
2016-07-07 17:13:01 +00:00
|
|
|
SelectedDrive.FirstDataSector = min(SelectedDrive.FirstDataSector,
|
|
|
|
(DWORD)(DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize));
|
2015-03-16 20:34:04 +00:00
|
|
|
if ((part_type == RUFUS_EXTRA_PARTITION_TYPE) || (isUefiNtfs))
|
2014-12-20 00:22:00 +00:00
|
|
|
// This is a partition Rufus created => we can safely ignore it
|
2015-01-20 21:50:24 +00:00
|
|
|
--SelectedDrive.nPartitions;
|
2013-01-24 21:30:11 +00:00
|
|
|
if (part_type == 0xee) // Flag a protective MBR for non GPT platforms (XP)
|
|
|
|
SelectedDrive.has_protective_mbr = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PARTITION_STYLE_GPT:
|
2018-03-26 17:58:22 +00:00
|
|
|
SelectedDrive.PartitionStyle = PARTITION_STYLE_GPT;
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Partition type: GPT, NB Partitions: %d", DriveLayout->PartitionCount);
|
|
|
|
suprintf("Disk GUID: %s", GuidToString(&DriveLayout->Gpt.DiskId));
|
|
|
|
suprintf("Max parts: %d, Start Offset: %" PRIi64 ", Usable = %" PRIi64 " bytes",
|
2013-01-24 21:30:11 +00:00
|
|
|
DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart);
|
2020-02-20 11:56:15 +00:00
|
|
|
for (i = 0; i < DriveLayout->PartitionCount; i++) {
|
2019-09-14 22:33:46 +00:00
|
|
|
if (i < MAX_PARTITIONS) {
|
2019-08-18 13:13:09 +00:00
|
|
|
SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
|
2019-09-14 22:33:46 +00:00
|
|
|
SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
|
|
|
|
}
|
2014-08-07 00:45:46 +00:00
|
|
|
SelectedDrive.nPartitions++;
|
2019-01-03 12:29:28 +00:00
|
|
|
isUefiNtfs = (wcscmp(DriveLayout->PartitionEntry[i].Gpt.Name, L"UEFI:NTFS") == 0);
|
2020-06-06 15:34:17 +00:00
|
|
|
suprintf("Partition %d%s:\r\n Type: %s", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "",
|
|
|
|
GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType));
|
|
|
|
if (DriveLayout->PartitionEntry[i].Gpt.Name[0] != 0)
|
|
|
|
suprintf(" Name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name);
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf(" ID: %s\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %" PRIi64 ", Attributes: 0x%016" PRIX64,
|
2016-07-07 17:13:01 +00:00
|
|
|
GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId),
|
|
|
|
SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE),
|
|
|
|
DriveLayout->PartitionEntry[i].PartitionLength,
|
|
|
|
DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize,
|
2013-01-24 21:30:11 +00:00
|
|
|
DriveLayout->PartitionEntry[i].Gpt.Attributes);
|
2016-07-07 17:13:01 +00:00
|
|
|
SelectedDrive.FirstDataSector = min(SelectedDrive.FirstDataSector,
|
|
|
|
(DWORD)(DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / SelectedDrive.SectorSize));
|
2015-01-20 21:50:24 +00:00
|
|
|
// Don't register the partitions that we don't care about destroying
|
2018-05-08 14:25:53 +00:00
|
|
|
if ( isUefiNtfs ||
|
2020-02-06 18:23:19 +00:00
|
|
|
(CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MICROSOFT_RESERVED)) ||
|
|
|
|
(CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) )
|
2015-01-20 21:50:24 +00:00
|
|
|
--SelectedDrive.nPartitions;
|
2020-02-06 18:23:19 +00:00
|
|
|
if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MICROSOFT_DATA))
|
2014-08-07 00:45:46 +00:00
|
|
|
ret = TRUE;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2018-03-26 17:58:22 +00:00
|
|
|
SelectedDrive.PartitionStyle = PARTITION_STYLE_MBR;
|
2017-09-08 14:38:30 +00:00
|
|
|
suprintf("Partition type: RAW");
|
2013-01-24 21:30:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-12-20 00:22:00 +00:00
|
|
|
#if defined(__GNUC__)
|
|
|
|
#pragma GCC diagnostic warning "-Warray-bounds"
|
|
|
|
#endif
|
2013-04-07 23:10:58 +00:00
|
|
|
safe_closehandle(hPhysical);
|
2013-01-24 21:30:11 +00:00
|
|
|
|
2014-08-07 00:45:46 +00:00
|
|
|
return ret;
|
2013-07-08 23:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Flush file data
|
|
|
|
*/
|
|
|
|
static BOOL FlushDrive(char drive_letter)
|
|
|
|
{
|
|
|
|
HANDLE hDrive = INVALID_HANDLE_VALUE;
|
|
|
|
BOOL r = FALSE;
|
|
|
|
char logical_drive[] = "\\\\.\\#:";
|
|
|
|
|
|
|
|
logical_drive[4] = drive_letter;
|
|
|
|
hDrive = CreateFileA(logical_drive, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
2014-11-11 19:17:39 +00:00
|
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
2013-07-08 23:14:29 +00:00
|
|
|
if (hDrive == INVALID_HANDLE_VALUE) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Failed to open %c: for flushing: %s", drive_letter, WindowsErrorString());
|
2013-07-08 23:14:29 +00:00
|
|
|
goto out;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
2013-07-08 23:14:29 +00:00
|
|
|
r = FlushFileBuffers(hDrive);
|
|
|
|
if (r == FALSE)
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Failed to flush %c: %s", drive_letter, WindowsErrorString());
|
2013-01-24 21:30:11 +00:00
|
|
|
|
2013-07-08 23:14:29 +00:00
|
|
|
out:
|
|
|
|
safe_closehandle(hDrive);
|
|
|
|
return r;
|
2013-01-24 21:30:11 +00:00
|
|
|
}
|
|
|
|
|
2013-04-07 23:10:58 +00:00
|
|
|
/*
|
|
|
|
* Unmount of volume using the DISMOUNT_VOLUME ioctl
|
|
|
|
*/
|
|
|
|
BOOL UnmountVolume(HANDLE hDrive)
|
2011-12-11 02:19:38 +00:00
|
|
|
{
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
if (!DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &size, NULL)) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not unmount drive: %s", WindowsErrorString());
|
2011-12-11 02:19:38 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-01-22 02:40:43 +00:00
|
|
|
|
2013-07-08 23:14:29 +00:00
|
|
|
/*
|
2019-04-08 15:23:54 +00:00
|
|
|
* Mount the volume identified by drive_guid to mountpoint drive_name.
|
2019-04-25 17:58:55 +00:00
|
|
|
* If volume_name is already mounted, but with a different letter than the
|
|
|
|
* one requested then drive_name is updated to use that letter.
|
2013-07-08 23:14:29 +00:00
|
|
|
*/
|
2019-04-25 17:58:55 +00:00
|
|
|
BOOL MountVolume(char* drive_name, char *volume_name)
|
2013-07-08 23:14:29 +00:00
|
|
|
{
|
2019-04-08 15:23:54 +00:00
|
|
|
char mounted_guid[52];
|
|
|
|
char mounted_letter[27] = { 0 };
|
2013-12-10 19:30:12 +00:00
|
|
|
DWORD size;
|
|
|
|
|
2019-04-25 17:58:55 +00:00
|
|
|
if ((drive_name == NULL) || (volume_name == NULL) || (drive_name[0] == '?') ||
|
|
|
|
(strncmp(volume_name, groot_name, groot_len) == 0))
|
2015-06-24 19:00:20 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2019-04-08 15:23:54 +00:00
|
|
|
// Windows may already have the volume mounted but under a different letter.
|
|
|
|
// If that is the case, update drive_name to that letter.
|
2019-04-25 17:58:55 +00:00
|
|
|
if ( (GetVolumePathNamesForVolumeNameA(volume_name, mounted_letter, sizeof(mounted_letter), &size))
|
2014-01-11 00:19:03 +00:00
|
|
|
&& (size > 1) && (mounted_letter[0] != drive_name[0]) ) {
|
2019-04-19 12:24:20 +00:00
|
|
|
uprintf("%s is already mounted as %C: instead of %C: - Will now use this target instead...",
|
2019-04-25 17:58:55 +00:00
|
|
|
volume_name, mounted_letter[0], drive_name[0]);
|
2019-04-08 15:23:54 +00:00
|
|
|
drive_name[0] = mounted_letter[0];
|
|
|
|
return TRUE;
|
2013-12-10 19:30:12 +00:00
|
|
|
}
|
2013-07-08 23:14:29 +00:00
|
|
|
|
2019-04-25 17:58:55 +00:00
|
|
|
if (!SetVolumeMountPointA(drive_name, volume_name)) {
|
2013-07-08 23:14:29 +00:00
|
|
|
if (GetLastError() == ERROR_DIR_NOT_EMPTY) {
|
|
|
|
if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) {
|
2019-04-08 15:23:54 +00:00
|
|
|
uprintf("%s is already mounted, but volume GUID could not be checked: %s",
|
2013-07-08 23:14:29 +00:00
|
|
|
drive_name, WindowsErrorString());
|
2019-04-25 17:58:55 +00:00
|
|
|
} else if (safe_strcmp(volume_name, mounted_guid) != 0) {
|
2019-04-08 15:23:54 +00:00
|
|
|
uprintf("%s is mounted, but volume GUID doesn't match:\r\n expected %s, got %s",
|
2019-04-25 17:58:55 +00:00
|
|
|
drive_name, volume_name, mounted_guid);
|
2019-04-16 19:44:13 +00:00
|
|
|
} else {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("%s is already mounted as %C:", volume_name, drive_name[0]);
|
2019-04-16 19:44:13 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
uprintf("Retrying after dismount...");
|
|
|
|
if (!DeleteVolumeMountPointA(drive_name))
|
|
|
|
uprintf("Warning: Could not delete volume mountpoint: %s", WindowsErrorString());
|
2019-04-25 17:58:55 +00:00
|
|
|
if (SetVolumeMountPointA(drive_name, volume_name))
|
2019-04-16 19:44:13 +00:00
|
|
|
return TRUE;
|
|
|
|
if ((GetLastError() == ERROR_DIR_NOT_EMPTY) &&
|
|
|
|
GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid)) &&
|
2019-04-25 17:58:55 +00:00
|
|
|
(safe_strcmp(volume_name, mounted_guid) == 0)) {
|
|
|
|
uprintf("%s was remounted as %C: (second time lucky!)", volume_name, drive_name[0]);
|
2019-04-16 19:44:13 +00:00
|
|
|
return TRUE;
|
2013-07-08 23:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-16 19:44:13 +00:00
|
|
|
return FALSE;
|
2013-07-08 23:14:29 +00:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-01-20 02:56:17 +00:00
|
|
|
/*
|
2019-04-25 17:58:55 +00:00
|
|
|
* Alternate version of MountVolume required for ESP's, since Windows (including VDS) does
|
|
|
|
* *NOT* provide any means of mounting these volume but through DefineDosDevice(). Also
|
2019-08-18 13:13:09 +00:00
|
|
|
* note that bcdboot is very finicky about what it may or may not handle, even if the
|
2019-04-25 17:58:55 +00:00
|
|
|
* mount was successful (e.g. '\Device\HarddiskVolume###' vs 'Device\HarddiskVolume###').
|
2019-08-18 13:13:09 +00:00
|
|
|
* Returned string is static (no concurrency) and must not be freed.
|
2015-01-20 02:56:17 +00:00
|
|
|
*/
|
2019-08-18 13:13:09 +00:00
|
|
|
char* AltMountVolume(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bSilent)
|
2015-01-20 02:56:17 +00:00
|
|
|
{
|
2019-04-25 17:58:55 +00:00
|
|
|
char* ret = NULL, *volume_name = NULL;
|
2015-01-20 02:56:17 +00:00
|
|
|
static char mounted_drive[] = "?:";
|
|
|
|
|
|
|
|
mounted_drive[0] = GetUnusedDriveLetter();
|
|
|
|
if (mounted_drive[0] == 0) {
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Could not find an unused drive letter");
|
2015-01-20 02:56:17 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2019-08-18 13:13:09 +00:00
|
|
|
// Can't use a regular volume GUID for ESPs...
|
|
|
|
volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE);
|
2019-04-25 17:58:55 +00:00
|
|
|
if ((volume_name == NULL) || (strncmp(volume_name, groot_name, groot_len) != 0)) {
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Unexpected volume name: '%s'", volume_name);
|
2015-01-20 02:56:17 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Mounting '%s' as '%s'", &volume_name[14], mounted_drive);
|
|
|
|
// bcdboot doesn't like it if you forget the starting '\'
|
2019-04-25 17:58:55 +00:00
|
|
|
if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, mounted_drive, &volume_name[14])) {
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Mount operation failed: %s", WindowsErrorString());
|
2015-01-20 02:56:17 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = mounted_drive;
|
|
|
|
|
|
|
|
out:
|
2019-04-25 17:58:55 +00:00
|
|
|
free(volume_name);
|
2015-01-20 02:56:17 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unmount a volume that was mounted by AltmountVolume()
|
|
|
|
*/
|
2019-08-18 13:13:09 +00:00
|
|
|
BOOL AltUnmountVolume(const char* drive_name, BOOL bSilent)
|
2015-01-20 02:56:17 +00:00
|
|
|
{
|
2015-01-20 21:50:24 +00:00
|
|
|
if (drive_name == NULL)
|
|
|
|
return FALSE;
|
2015-01-20 02:56:17 +00:00
|
|
|
if (!DefineDosDeviceA(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, drive_name, NULL)) {
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Could not unmount '%s': %s", drive_name, WindowsErrorString());
|
2015-01-20 02:56:17 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2019-08-18 13:13:09 +00:00
|
|
|
suprintf("Successfully unmounted '%s'", drive_name);
|
2015-01-20 02:56:17 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-07-08 23:14:29 +00:00
|
|
|
/*
|
2019-04-08 15:23:54 +00:00
|
|
|
* Issue a complete remount of the volume.
|
|
|
|
* Note that drive_name *may* be altered when the volume gets remounted.
|
2013-07-08 23:14:29 +00:00
|
|
|
*/
|
|
|
|
BOOL RemountVolume(char* drive_name)
|
|
|
|
{
|
2019-04-25 17:58:55 +00:00
|
|
|
char volume_name[51];
|
2013-07-08 23:14:29 +00:00
|
|
|
|
|
|
|
// UDF requires a sync/flush, and it's also a good idea for other FS's
|
|
|
|
FlushDrive(drive_name[0]);
|
2019-04-25 17:58:55 +00:00
|
|
|
if (GetVolumeNameForVolumeMountPointA(drive_name, volume_name, sizeof(volume_name))) {
|
2013-07-08 23:14:29 +00:00
|
|
|
if (DeleteVolumeMountPointA(drive_name)) {
|
|
|
|
Sleep(200);
|
2019-04-25 17:58:55 +00:00
|
|
|
if (MountVolume(drive_name, volume_name)) {
|
|
|
|
uprintf("Successfully remounted %s as %C:", volume_name, drive_name[0]);
|
2013-07-08 23:14:29 +00:00
|
|
|
} else {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("Failed to remount %s as %C:", volume_name, drive_name[0]);
|
2013-07-08 23:14:29 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
} else {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("Could not remount %s as %C: %s", volume_name, drive_name[0], WindowsErrorString());
|
2013-07-08 23:14:29 +00:00
|
|
|
// Try to continue regardless
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-04-27 15:04:47 +00:00
|
|
|
/*
|
|
|
|
* Zero the first 'size' bytes of a partition. This is needed because we haven't found a way to
|
|
|
|
* properly reset Windows's cached view of a drive partitioning short of cycling the USB port
|
|
|
|
* (especially IOCTL_DISK_UPDATE_PROPERTIES is *USELESS*), and therefore the OS will try to
|
|
|
|
* read the file system data at an old location, even if the partition has just been deleted.
|
2019-09-06 10:32:37 +00:00
|
|
|
* TODO: We should do something like this in DeletePartitions() too.
|
2019-04-27 15:04:47 +00:00
|
|
|
*/
|
|
|
|
static BOOL ClearPartition(HANDLE hDrive, LARGE_INTEGER offset, DWORD size)
|
|
|
|
{
|
|
|
|
BOOL r = FALSE;
|
|
|
|
uint8_t* buffer = calloc(size, 1);
|
|
|
|
|
|
|
|
if (buffer == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
2019-05-02 14:41:42 +00:00
|
|
|
if (!SetFilePointerEx(hDrive, offset, NULL, FILE_BEGIN)) {
|
|
|
|
free(buffer);
|
2019-04-27 15:04:47 +00:00
|
|
|
return FALSE;
|
2019-05-02 14:41:42 +00:00
|
|
|
}
|
2019-04-27 15:04:47 +00:00
|
|
|
|
|
|
|
r = WriteFileWithRetry(hDrive, buffer, size, &size, WRITE_RETRIES);
|
|
|
|
free(buffer);
|
|
|
|
return r;
|
|
|
|
}
|
2013-01-22 02:40:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a partition table
|
2013-01-27 20:56:57 +00:00
|
|
|
* See http://technet.microsoft.com/en-us/library/cc739412.aspx for some background info
|
|
|
|
* NB: if you modify the MBR outside of using the Windows API, Windows still uses the cached
|
2013-06-25 01:55:25 +00:00
|
|
|
* copy it got from the last IOCTL, and ignores your changes until you replug the drive
|
|
|
|
* or issue an IOCTL_DISK_UPDATE_PROPERTIES.
|
|
|
|
*/
|
2015-01-20 02:56:17 +00:00
|
|
|
BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL mbr_uefi_marker, uint8_t extra_partitions)
|
2013-01-22 02:40:43 +00:00
|
|
|
{
|
2017-08-12 14:12:00 +00:00
|
|
|
const char* PartitionTypeName[] = { "MBR", "GPT", "SFD" };
|
2020-06-06 21:19:20 +00:00
|
|
|
const wchar_t *extra_part_name = L"", *main_part_name = write_as_esp ? L"EFI System Partition" : L"Main Data Partition";
|
|
|
|
const LONGLONG main_part_size = write_as_esp ? MAX_ISO_TO_ESP_SIZE * MB : SelectedDrive.DiskSize;
|
2019-04-27 15:04:47 +00:00
|
|
|
const LONGLONG bytes_per_track = ((LONGLONG)SelectedDrive.SectorsPerTrack) * SelectedDrive.SectorSize;
|
|
|
|
const DWORD size_to_clear = MAX_SECTORS_TO_CLEAR * SelectedDrive.SectorSize;
|
|
|
|
uint8_t* buffer;
|
2016-04-28 08:09:43 +00:00
|
|
|
size_t uefi_ntfs_size = 0;
|
2013-01-22 02:40:43 +00:00
|
|
|
CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}};
|
|
|
|
DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0};
|
|
|
|
BOOL r;
|
2015-01-19 01:23:32 +00:00
|
|
|
DWORD i, size, bufsize, pn = 0;
|
2020-06-06 21:19:20 +00:00
|
|
|
LONGLONG main_part_size_in_sectors, extra_part_size_in_tracks = 0, esp_size;
|
2013-01-22 02:40:43 +00:00
|
|
|
|
2015-01-01 23:39:28 +00:00
|
|
|
PrintInfoDebug(0, MSG_238, PartitionTypeName[partition_style]);
|
2015-01-19 01:23:32 +00:00
|
|
|
|
2017-08-12 14:12:00 +00:00
|
|
|
if (partition_style == PARTITION_STYLE_SFD)
|
|
|
|
// Nothing to do
|
|
|
|
return TRUE;
|
|
|
|
|
2016-04-28 08:09:43 +00:00
|
|
|
if (extra_partitions & XP_UEFI_NTFS) {
|
2015-03-16 20:34:04 +00:00
|
|
|
uefi_ntfs_size = GetResourceSize(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img");
|
|
|
|
if (uefi_ntfs_size == 0)
|
2015-01-22 22:31:34 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2019-08-18 13:13:09 +00:00
|
|
|
memset(partition_offset, 0, sizeof(partition_offset));
|
|
|
|
memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
|
2019-09-14 22:33:46 +00:00
|
|
|
memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));
|
2013-01-22 02:40:43 +00:00
|
|
|
|
2015-01-19 01:23:32 +00:00
|
|
|
// Compute the start offset of our first partition
|
2018-03-22 23:14:20 +00:00
|
|
|
if ((partition_style == PARTITION_STYLE_GPT) || (!IsChecked(IDC_OLD_BIOS_FIXES))) {
|
2013-01-22 02:40:43 +00:00
|
|
|
// Go with the MS 1 MB wastage at the beginning...
|
2016-02-20 22:52:32 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = MB;
|
2013-01-22 02:40:43 +00:00
|
|
|
} else {
|
|
|
|
// Align on Cylinder
|
2015-01-20 21:50:24 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = bytes_per_track;
|
2013-01-22 02:40:43 +00:00
|
|
|
}
|
2015-01-19 01:23:32 +00:00
|
|
|
|
|
|
|
// If required, set the MSR partition (GPT only - must be created before the data part)
|
2019-04-27 15:04:47 +00:00
|
|
|
if (extra_partitions & XP_MSR) {
|
2020-04-11 14:02:08 +00:00
|
|
|
assert(partition_style == PARTITION_STYLE_GPT);
|
2019-04-27 15:04:47 +00:00
|
|
|
extra_part_name = L"Microsoft Reserved Partition";
|
2016-02-20 22:52:32 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = 128*MB;
|
2020-02-06 18:23:19 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_MICROSOFT_RESERVED;
|
2019-08-13 08:04:31 +00:00
|
|
|
uprintf("● Creating %S (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
|
2019-04-27 15:04:47 +00:00
|
|
|
SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
|
2015-01-19 01:23:32 +00:00
|
|
|
IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
|
2019-04-27 15:04:47 +00:00
|
|
|
wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, extra_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
|
|
|
|
// Zero the first sectors from this partition to avoid file system caching issues
|
|
|
|
if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
|
|
|
|
uprintf("Could not zero %S: %s", extra_part_name, WindowsErrorString());
|
2019-08-18 13:13:09 +00:00
|
|
|
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
|
2019-09-14 22:33:46 +00:00
|
|
|
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
|
2015-01-26 23:20:39 +00:00
|
|
|
pn++;
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart +
|
|
|
|
DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart;
|
2019-08-13 08:04:31 +00:00
|
|
|
// Clear the extra partition we processed
|
|
|
|
extra_partitions &= ~(XP_MSR);
|
2015-01-19 01:23:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set our main data partition
|
2020-06-06 21:19:20 +00:00
|
|
|
if (write_as_esp) {
|
|
|
|
// Align ESP to 64 MB while leaving at least 32 MB free space
|
|
|
|
esp_size = (((img_report.projected_size / MB) + 96) / 64) * 64 * MB;
|
|
|
|
main_part_size_in_sectors = (esp_size - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) /
|
|
|
|
SelectedDrive.SectorSize;
|
|
|
|
} else {
|
|
|
|
main_part_size_in_sectors = (main_part_size - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) /
|
|
|
|
// Need 33 sectors at the end for secondary GPT
|
|
|
|
SelectedDrive.SectorSize - ((partition_style == PARTITION_STYLE_GPT) ? 33 : 0);
|
|
|
|
}
|
2019-04-27 15:04:47 +00:00
|
|
|
if (extra_partitions) {
|
2019-04-25 17:58:55 +00:00
|
|
|
// Adjust the size according to extra partitions (which we always align to a track)
|
2019-08-13 08:04:31 +00:00
|
|
|
if (extra_partitions & XP_ESP) {
|
|
|
|
extra_part_name = L"EFI System";
|
|
|
|
// The size of the ESP depends on the minimum size we're able to format in FAT32, which
|
|
|
|
// in turn depends on the cluster size used, which in turn depends on the disk sector size.
|
|
|
|
// Plus some people are complaining that the *OFFICIAL MINIMUM SIZE* as documented by Microsoft at
|
|
|
|
// https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions
|
|
|
|
// is too small. See: https://github.com/pbatard/rufus/issues/979
|
|
|
|
if (SelectedDrive.SectorSize <= 4096)
|
2020-06-06 21:19:20 +00:00
|
|
|
esp_size = 300 * MB;
|
2019-08-13 08:04:31 +00:00
|
|
|
else
|
2020-06-06 21:19:20 +00:00
|
|
|
esp_size = 1200 * MB; // That'll teach you to have a nonstandard disk!
|
|
|
|
extra_part_size_in_tracks = (esp_size + bytes_per_track - 1) / bytes_per_track;
|
2019-08-13 08:04:31 +00:00
|
|
|
} else if (extra_partitions & XP_UEFI_NTFS) {
|
2019-04-25 17:58:55 +00:00
|
|
|
extra_part_name = L"UEFI:NTFS";
|
2016-06-16 16:29:15 +00:00
|
|
|
extra_part_size_in_tracks = (max(MIN_EXTRA_PART_SIZE, uefi_ntfs_size) + bytes_per_track - 1) / bytes_per_track;
|
2019-04-16 19:44:13 +00:00
|
|
|
} else if ((extra_partitions & XP_CASPER)) {
|
|
|
|
assert(persistence_size != 0);
|
2019-04-25 17:58:55 +00:00
|
|
|
extra_part_name = L"Linux Persistence";
|
2019-04-16 19:44:13 +00:00
|
|
|
extra_part_size_in_tracks = persistence_size / bytes_per_track;
|
2019-04-25 17:58:55 +00:00
|
|
|
} else if (extra_partitions & XP_COMPAT) {
|
|
|
|
extra_part_name = L"BIOS Compatibility";
|
|
|
|
extra_part_size_in_tracks = 1; // One track for the extra partition
|
2019-04-27 15:04:47 +00:00
|
|
|
} else {
|
|
|
|
assert(FALSE);
|
2019-04-16 19:44:13 +00:00
|
|
|
}
|
2016-05-23 11:19:11 +00:00
|
|
|
main_part_size_in_sectors = ((main_part_size_in_sectors / SelectedDrive.SectorsPerTrack) -
|
|
|
|
extra_part_size_in_tracks) * SelectedDrive.SectorsPerTrack;
|
2014-12-20 00:22:00 +00:00
|
|
|
}
|
2019-04-25 17:58:55 +00:00
|
|
|
if (main_part_size_in_sectors <= 0) {
|
2019-04-27 15:04:47 +00:00
|
|
|
uprintf("Error: Invalid %S size", main_part_name);
|
2019-04-25 17:58:55 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2019-04-27 15:04:47 +00:00
|
|
|
uprintf("● Creating %S (offset: %lld, size: %s)", main_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
|
|
|
|
SizeToHumanReadable(main_part_size_in_sectors * SelectedDrive.SectorSize, TRUE, FALSE));
|
|
|
|
// Zero the beginning of this partition to avoid conflicting leftovers
|
|
|
|
if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
|
|
|
|
uprintf("Could not zero %S: %s", main_part_name, WindowsErrorString());
|
2019-04-25 17:58:55 +00:00
|
|
|
|
2016-05-23 11:19:11 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = main_part_size_in_sectors * SelectedDrive.SectorSize;
|
2015-01-19 01:23:32 +00:00
|
|
|
if (partition_style == PARTITION_STYLE_MBR) {
|
2019-04-13 14:29:27 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = (boot_type != BT_NON_BOOTABLE);
|
2015-01-19 01:23:32 +00:00
|
|
|
switch (file_system) {
|
|
|
|
case FS_FAT16:
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0e; // FAT16 LBA
|
|
|
|
break;
|
|
|
|
case FS_NTFS:
|
|
|
|
case FS_EXFAT:
|
|
|
|
case FS_UDF:
|
|
|
|
case FS_REFS:
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
|
|
|
|
break;
|
2019-04-25 17:58:55 +00:00
|
|
|
case FS_EXT2:
|
|
|
|
case FS_EXT3:
|
|
|
|
case FS_EXT4:
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83;
|
|
|
|
break;
|
2015-01-19 01:23:32 +00:00
|
|
|
case FS_FAT32:
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0c; // FAT32 LBA
|
|
|
|
break;
|
|
|
|
default:
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Unsupported file system");
|
2015-01-19 01:23:32 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-06 21:19:20 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = write_as_esp ? PARTITION_GENERIC_ESP : PARTITION_MICROSOFT_DATA;
|
2015-01-19 01:23:32 +00:00
|
|
|
IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
|
2019-04-27 15:04:47 +00:00
|
|
|
wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, main_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
|
2015-01-19 01:23:32 +00:00
|
|
|
}
|
2019-08-18 13:13:09 +00:00
|
|
|
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
|
2019-09-14 22:33:46 +00:00
|
|
|
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
|
2019-08-18 13:13:09 +00:00
|
|
|
partition_offset[PI_MAIN] = SelectedDrive.PartitionOffset[pn];
|
2015-01-19 01:23:32 +00:00
|
|
|
pn++;
|
|
|
|
|
|
|
|
// Set the optional extra partition
|
2015-01-20 02:56:17 +00:00
|
|
|
if (extra_partitions) {
|
2015-01-19 01:23:32 +00:00
|
|
|
// Should end on a track boundary
|
|
|
|
DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart +
|
2015-01-20 02:56:17 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart;
|
2015-03-16 20:34:04 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = (extra_partitions & XP_UEFI_NTFS)?uefi_ntfs_size:
|
2019-04-19 12:24:20 +00:00
|
|
|
extra_part_size_in_tracks * bytes_per_track;
|
2019-04-27 15:04:47 +00:00
|
|
|
uprintf("● Creating %S Partition (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
|
|
|
|
SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
|
2019-08-18 13:13:09 +00:00
|
|
|
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
|
2019-09-14 22:33:46 +00:00
|
|
|
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
|
2019-08-18 13:13:09 +00:00
|
|
|
if (extra_partitions & XP_CASPER)
|
|
|
|
partition_offset[PI_CASPER] = SelectedDrive.PartitionOffset[pn];
|
|
|
|
else if (extra_partitions & XP_ESP)
|
|
|
|
partition_offset[PI_ESP] = SelectedDrive.PartitionOffset[pn];
|
|
|
|
|
2015-01-19 01:23:32 +00:00
|
|
|
if (partition_style == PARTITION_STYLE_GPT) {
|
2020-02-06 18:23:19 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = (extra_partitions & XP_ESP) ? PARTITION_GENERIC_ESP : PARTITION_MICROSOFT_DATA;
|
2015-01-19 01:23:32 +00:00
|
|
|
IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId));
|
2019-08-13 08:04:31 +00:00
|
|
|
wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, (extra_partitions & XP_ESP) ? L"EFI System Partition" : extra_part_name,
|
|
|
|
ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
|
2015-01-19 01:23:32 +00:00
|
|
|
} else {
|
2019-08-13 08:04:31 +00:00
|
|
|
if (extra_partitions & (XP_UEFI_NTFS | XP_ESP)) {
|
2019-04-25 17:58:55 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;
|
2019-04-16 19:44:13 +00:00
|
|
|
} else if (extra_partitions & XP_CASPER) {
|
2019-04-25 17:58:55 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x83;
|
2020-04-11 14:02:08 +00:00
|
|
|
} else if (extra_partitions & XP_COMPAT) {
|
2019-04-25 17:58:55 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = RUFUS_EXTRA_PARTITION_TYPE;
|
2015-01-20 21:50:24 +00:00
|
|
|
// Set the one track compatibility partition to be all hidden sectors
|
2016-05-23 11:19:11 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[pn].Mbr.HiddenSectors = SelectedDrive.SectorsPerTrack;
|
2019-04-16 19:44:13 +00:00
|
|
|
} else {
|
|
|
|
assert(FALSE);
|
|
|
|
}
|
2015-01-19 01:23:32 +00:00
|
|
|
}
|
|
|
|
|
2015-03-16 20:34:04 +00:00
|
|
|
// We need to write the UEFI:NTFS partition before we refresh the disk
|
|
|
|
if (extra_partitions & XP_UEFI_NTFS) {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("Writing %S data...", extra_part_name);
|
2015-01-19 01:23:32 +00:00
|
|
|
if (!SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("Could not set position");
|
2015-01-19 01:23:32 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2015-03-16 20:34:04 +00:00
|
|
|
buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img", &bufsize, FALSE);
|
2015-01-19 01:23:32 +00:00
|
|
|
if (buffer == NULL) {
|
2019-04-25 17:58:55 +00:00
|
|
|
uprintf("Could not access source image");
|
2015-01-19 01:23:32 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2016-01-14 17:43:02 +00:00
|
|
|
if(!WriteFileWithRetry(hDrive, buffer, bufsize, &size, WRITE_RETRIES)) {
|
|
|
|
uprintf("Write error: %s", WindowsErrorString());
|
2015-01-19 01:23:32 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2018-11-20 12:28:20 +00:00
|
|
|
installed_uefi_ntfs = TRUE;
|
2015-01-19 01:23:32 +00:00
|
|
|
}
|
|
|
|
pn++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the remaining partition data
|
|
|
|
for (i = 0; i < pn; i++) {
|
2019-04-25 17:58:55 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
|
2015-01-19 01:23:32 +00:00
|
|
|
DriveLayoutEx.PartitionEntry[i].PartitionStyle = partition_style;
|
|
|
|
DriveLayoutEx.PartitionEntry[i].RewritePartition = TRUE;
|
|
|
|
}
|
2013-01-22 02:40:43 +00:00
|
|
|
|
|
|
|
switch (partition_style) {
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
|
|
CreateDisk.PartitionStyle = PARTITION_STYLE_MBR;
|
2013-01-27 20:56:57 +00:00
|
|
|
// If MBR+UEFI is selected, write an UEFI marker in lieu of the regular MBR signature.
|
|
|
|
// This helps us reselect the partition scheme option that was used when creating the
|
|
|
|
// drive in Rufus. As far as I can tell, Windows doesn't care much if this signature
|
|
|
|
// isn't unique for USB drives.
|
2017-11-13 14:29:48 +00:00
|
|
|
CreateDisk.Mbr.Signature = mbr_uefi_marker?MBR_UEFI_MARKER:(DWORD)GetTickCount64();
|
2013-01-22 02:40:43 +00:00
|
|
|
|
|
|
|
DriveLayoutEx.PartitionStyle = PARTITION_STYLE_MBR;
|
|
|
|
DriveLayoutEx.PartitionCount = 4; // Must be multiple of 4 for MBR
|
|
|
|
DriveLayoutEx.Type.Mbr.Signature = CreateDisk.Mbr.Signature;
|
|
|
|
// TODO: CHS fixup (32 sectors/track) through a cheat mode, if requested
|
|
|
|
// NB: disk geometry is computed by BIOS & co. by finding a match between LBA and CHS value of first partition
|
|
|
|
// ms-sys's write_partition_number_of_heads() and write_partition_start_sector_number() can be used if needed
|
|
|
|
break;
|
|
|
|
case PARTITION_STYLE_GPT:
|
2019-08-13 08:04:31 +00:00
|
|
|
// TODO: (HOW?!?!?) As per MSDN: "When specifying a GUID partition table (GPT) as the PARTITION_STYLE of the CREATE_DISK
|
2013-07-05 21:20:46 +00:00
|
|
|
// structure, an application should wait for the MSR partition arrival before sending the IOCTL_DISK_SET_DRIVE_LAYOUT_EX
|
|
|
|
// control code. For more information about device notification, see RegisterDeviceNotification."
|
|
|
|
|
2013-01-22 02:40:43 +00:00
|
|
|
CreateDisk.PartitionStyle = PARTITION_STYLE_GPT;
|
|
|
|
IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId));
|
2019-04-25 17:58:55 +00:00
|
|
|
CreateDisk.Gpt.MaxPartitionCount = MAX_PARTITIONS;
|
2013-01-22 02:40:43 +00:00
|
|
|
|
|
|
|
DriveLayoutEx.PartitionStyle = PARTITION_STYLE_GPT;
|
2015-01-19 01:23:32 +00:00
|
|
|
DriveLayoutEx.PartitionCount = pn;
|
2014-10-28 19:16:35 +00:00
|
|
|
// At the very least, a GPT disk has 34 reserved sectors at the beginning and 33 at the end.
|
2016-05-23 11:19:11 +00:00
|
|
|
DriveLayoutEx.Type.Gpt.StartingUsableOffset.QuadPart = 34 * SelectedDrive.SectorSize;
|
|
|
|
DriveLayoutEx.Type.Gpt.UsableLength.QuadPart = SelectedDrive.DiskSize - (34+33) * SelectedDrive.SectorSize;
|
2019-04-25 17:58:55 +00:00
|
|
|
DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_PARTITIONS;
|
2013-01-22 02:40:43 +00:00
|
|
|
DriveLayoutEx.Type.Gpt.DiskId = CreateDisk.Gpt.DiskId;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-13 08:04:31 +00:00
|
|
|
// If you don't call IOCTL_DISK_CREATE_DISK, the IOCTL_DISK_SET_DRIVE_LAYOUT_EX call will fail
|
2013-01-22 02:40:43 +00:00
|
|
|
size = sizeof(CreateDisk);
|
2019-08-13 08:04:31 +00:00
|
|
|
r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL);
|
2013-01-22 02:40:43 +00:00
|
|
|
if (!r) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not reset disk: %s", WindowsErrorString());
|
2013-01-22 02:40:43 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-08-13 08:04:31 +00:00
|
|
|
// "The goggles, they do nothing!"
|
|
|
|
RefreshDriveLayout(hDrive);
|
|
|
|
|
2015-01-19 01:23:32 +00:00
|
|
|
size = sizeof(DriveLayoutEx) - ((partition_style == PARTITION_STYLE_GPT)?((4-pn)*sizeof(PARTITION_INFORMATION_EX)):0);
|
2019-08-13 08:04:31 +00:00
|
|
|
r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL);
|
2013-01-22 02:40:43 +00:00
|
|
|
if (!r) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not set drive layout: %s", WindowsErrorString());
|
2013-01-22 02:40:43 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-08-07 00:45:46 +00:00
|
|
|
|
2015-01-20 21:50:24 +00:00
|
|
|
if (!RefreshDriveLayout(hDrive))
|
2013-06-25 01:55:25 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-08-07 00:45:46 +00:00
|
|
|
BOOL RefreshDriveLayout(HANDLE hDrive)
|
|
|
|
{
|
|
|
|
BOOL r;
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
// Diskpart does call the following IOCTL this after updating the partition table, so we do too
|
|
|
|
r = DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL );
|
|
|
|
if (!r)
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not refresh drive layout: %s", WindowsErrorString());
|
2014-08-07 00:45:46 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-05-21 15:34:34 +00:00
|
|
|
/* Initialize disk for partitioning */
|
|
|
|
BOOL InitializeDisk(HANDLE hDrive)
|
2013-07-05 21:20:46 +00:00
|
|
|
{
|
|
|
|
BOOL r;
|
|
|
|
DWORD size;
|
|
|
|
CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}};
|
|
|
|
|
2020-06-03 15:03:20 +00:00
|
|
|
uprintf("Initializing disk...");
|
2013-07-05 21:20:46 +00:00
|
|
|
|
|
|
|
size = sizeof(CreateDisk);
|
|
|
|
r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK,
|
|
|
|
(BYTE*)&CreateDisk, size, NULL, 0, &size, NULL );
|
|
|
|
if (!r) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not delete drive layout: %s", WindowsErrorString());
|
2013-07-05 21:20:46 +00:00
|
|
|
safe_closehandle(hDrive);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL );
|
|
|
|
if (!r) {
|
2017-09-08 14:38:30 +00:00
|
|
|
uprintf("Could not refresh drive layout: %s", WindowsErrorString());
|
2013-07-05 21:20:46 +00:00
|
|
|
safe_closehandle(hDrive);
|
|
|
|
return FALSE;
|
2017-02-14 12:44:25 +00:00
|
|
|
}
|
2013-07-05 21:20:46 +00:00
|
|
|
|
2017-02-14 12:44:25 +00:00
|
|
|
return TRUE;
|
2013-07-05 21:20:46 +00:00
|
|
|
}
|
|
|
|
|
2013-01-22 02:40:43 +00:00
|
|
|
/*
|
2020-02-06 18:23:19 +00:00
|
|
|
* Convert MBR or GPT partition types to their human readable forms
|
2013-01-22 02:40:43 +00:00
|
|
|
*/
|
2020-02-06 18:23:19 +00:00
|
|
|
const char* GetMBRPartitionType(const uint8_t type)
|
2013-01-22 02:40:43 +00:00
|
|
|
{
|
|
|
|
int i;
|
2020-02-06 18:23:19 +00:00
|
|
|
for (i = 0; (i < ARRAYSIZE(mbr_type)) && (mbr_type[i].type != type); i++);
|
|
|
|
return (i < ARRAYSIZE(mbr_type)) ? mbr_type[i].name : "Unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* GetGPTPartitionType(const GUID* guid)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; (i < ARRAYSIZE(gpt_type)) && !CompareGUID(guid, gpt_type[i].guid); i++);
|
|
|
|
return (i < ARRAYSIZE(gpt_type)) ? gpt_type[i].name : GuidToString(guid);
|
2013-01-22 02:40:43 +00:00
|
|
|
}
|