unsigned char* GetResource(HMODULE module, char* name, char* type, const char* desc, DWORD* len, BOOL duplicate) { HGLOBAL res_handle; HRSRC res; unsigned char* p = NULL; res = FindResourceA(module, name, type); if (res == NULL) { uprintf("Could not locate resource '%s': %s\n", desc, WindowsErrorString()); goto out; } res_handle = LoadResource(module, res); if (res_handle == NULL) { uprintf("Could not load resource '%s': %s\n", desc, WindowsErrorString()); goto out; } *len = SizeofResource(module, res); if (duplicate) { p = (unsigned char*)malloc(*len); if (p == NULL) { uprintf("Coult not allocate resource '%s'\n", desc); goto out; } memcpy(p, LockResource(res_handle), *len); } else { p = (unsigned char*)LockResource(res_handle); } out: return p; }
/* * Open a drive or volume with optional write and lock access * Return INVALID_HANDLE_VALUE (/!\ which is DIFFERENT from NULL /!\) on failure. */ static HANDLE GetHandle(char* Path, BOOL bWriteAccess, BOOL bLockDrive) { int i; DWORD size; HANDLE hDrive = INVALID_HANDLE_VALUE; if (Path == NULL) goto out; hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRITE:0), FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (hDrive == INVALID_HANDLE_VALUE) { uprintf("Could not open drive %s: %s\n", Path, WindowsErrorString()); goto out; } if (bWriteAccess) { uprintf("Caution: Opened drive %s for write access\n", Path); } if (bLockDrive) { for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) { if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &size, NULL)) goto out; if (IS_ERROR(FormatStatus)) // User cancel break; Sleep(DRIVE_ACCESS_TIMEOUT/DRIVE_ACCESS_RETRIES); } // If we reached this section, either we didn't manage to get a lock or the user cancelled uprintf("Could not get exclusive access to device %s: %s\n", Path, WindowsErrorString()); safe_closehandle(hDrive); } out: return hDrive; }
/* Delete the disk partition table */ BOOL DeletePartitions(HANDLE hDrive) { BOOL r; DWORD size; CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}}; PrintInfoDebug(0, MSG_239); size = sizeof(CreateDisk); r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL ); if (!r) { uprintf("Could not delete drive layout: %s\n", WindowsErrorString()); safe_closehandle(hDrive); return FALSE; } r = DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL ); if (!r) { uprintf("Could not refresh drive layout: %s\n", WindowsErrorString()); safe_closehandle(hDrive); return FALSE; } return TRUE; }
/* * Open a drive or volume with optional write and lock access * Return INVALID_HANDLE_VALUE (/!\ which is DIFFERENT from NULL /!\) on failure. */ static HANDLE GetHandle(char* Path, BOOL bWriteAccess, BOOL bLockDrive) { DWORD size; HANDLE hDrive = INVALID_HANDLE_VALUE; if (Path == NULL) goto out; hDrive = CreateFileA(Path, GENERIC_READ|(bWriteAccess?GENERIC_WRITE:0), FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (hDrive == INVALID_HANDLE_VALUE) { uprintf("Could not open drive %s: %s\n", Path, WindowsErrorString()); goto out; } if (bWriteAccess) { uprintf("Caution: Opened drive %s for write access\n", Path); } if ((bLockDrive) && (!DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &size, NULL))) { uprintf("Could not get exclusive access to device %s: %s\n", Path, WindowsErrorString()); safe_closehandle(hDrive); goto out; } out: return hDrive; }
/* * Get the VID, PID and current device speed */ static void GetUSBProperties(char* parent_path, char* device_id, usb_device_props* props) { HANDLE handle = INVALID_HANDLE_VALUE; DWORD size; DEVINST device_inst; USB_NODE_CONNECTION_INFORMATION_EX conn_info; USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; PF_INIT(CM_Get_DevNode_Registry_PropertyA, Cfgmgr32); if ((parent_path == NULL) || (device_id == NULL) || (props == NULL)) { return; } props->port = 0; size = sizeof(props->port); if ( (pfCM_Get_DevNode_Registry_PropertyA != NULL) && (CM_Locate_DevNodeA(&device_inst, device_id, 0) == CR_SUCCESS) ) { pfCM_Get_DevNode_Registry_PropertyA(device_inst, CM_DRP_ADDRESS, NULL, (PVOID)&props->port, &size, 0); } handle = CreateFileA(parent_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { uprintf("Could not open hub %s: %s", parent_path, WindowsErrorString()); goto out; } memset(&conn_info, 0, sizeof(conn_info)); size = sizeof(conn_info); conn_info.ConnectionIndex = (ULONG)props->port; // coverity[tainted_data_argument] if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size, &conn_info, size, &size, NULL)) { uprintf("Could not get node connection information for '%s': %s", device_id, WindowsErrorString()); goto out; } props->vid = conn_info.DeviceDescriptor.idVendor; props->pid = conn_info.DeviceDescriptor.idProduct; props->speed = conn_info.Speed + 1; // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8 if (nWindowsVersion >= WINDOWS_8) { memset(&conn_info_v2, 0, sizeof(conn_info_v2)); size = sizeof(conn_info_v2); conn_info_v2.ConnectionIndex = (ULONG)props->port; conn_info_v2.Length = size; conn_info_v2.SupportedUsbProtocols.Usb300 = 1; if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) { uprintf("Could not get node connection information (V2) for device '%s': %s", device_id, WindowsErrorString()); } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) { props->speed = USB_SPEED_SUPER_OR_LATER; } else if (conn_info_v2.Flags.DeviceIsSuperSpeedCapableOrHigher) { props->is_LowerSpeed = TRUE; } } out: safe_closehandle(handle); }
char* MountISO(const char* path) { VIRTUAL_STORAGE_TYPE vtype = { 1, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT }; ATTACH_VIRTUAL_DISK_PARAMETERS vparams = {0}; DWORD r; wchar_t wtmp[128]; ULONG size = ARRAYSIZE(wtmp); wconvert(path); char* ret = NULL; PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk); PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk); PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk); if ((mounted_handle != NULL) && (mounted_handle != INVALID_HANDLE_VALUE)) UnMountISO(); r = pfOpenVirtualDisk(&vtype, wpath, VIRTUAL_DISK_ACCESS_READ | VIRTUAL_DISK_ACCESS_GET_INFO, OPEN_VIRTUAL_DISK_FLAG_NONE, NULL, &mounted_handle); if (r != ERROR_SUCCESS) { SetLastError(r); uprintf("Could not open ISO '%s': %s", path, WindowsErrorString()); goto out; } vparams.Version = ATTACH_VIRTUAL_DISK_VERSION_1; r = pfAttachVirtualDisk(mounted_handle, NULL, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 0, &vparams, NULL); if (r != ERROR_SUCCESS) { SetLastError(r); uprintf("Could not mount ISO '%s': %s", path, WindowsErrorString()); goto out; } r = pfGetVirtualDiskPhysicalPath(mounted_handle, &size, wtmp); if (r != ERROR_SUCCESS) { SetLastError(r); uprintf("Could not obtain physical path for mounted ISO '%s': %s", path, WindowsErrorString()); goto out; } wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path)); ret = physical_path; out: if (ret == NULL) UnMountISO(); wfree(path); return ret; }
/** * Search all the processes and list the ones that have a specific handle open. * * \param HandleName The name of the handle to look for. * \param dwTimeOut The maximum amounf of time (ms) that may be spent searching * \param bPartialMatch Whether partial matches should be allowed. * \param bIgnoreSelf Whether the current process should be listed. * \param bQuiet Prints minimal output. * * \return a byte containing the cummulated access rights (f----xwr) from all the handles found * with bit 7 ('f') also set if at least one process was found. */ BYTE SearchProcess(char* HandleName, DWORD dwTimeOut, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL bQuiet) { HANDLE handle; DWORD res = 0; _HandleName = HandleName; _bPartialMatch = bPartialMatch; _bIgnoreSelf = bIgnoreSelf; _bQuiet = bQuiet; access_mask = 0; handle = CreateThread(NULL, 0, SearchProcessThread, NULL, 0, NULL); if (handle == NULL) { uprintf("Warning: Unable to create conflicting process search thread"); return 0x00; } res = WaitForSingleObjectWithMessages(handle, dwTimeOut); if (res == WAIT_TIMEOUT) { // Timeout - kill the thread TerminateThread(handle, 0); uprintf("Warning: Conflicting process search failed to complete due to timeout"); } else if (res != WAIT_OBJECT_0) { TerminateThread(handle, 0); uprintf("Warning: Failed to wait for conflicting process search thread: %s", WindowsErrorString()); } return access_mask; }
/* * Return the next unused drive letter from the system */ char GetUnusedDriveLetter(void) { DWORD size; char drive_letter = 'Z'+1, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)\n", size, sizeof(drives)); goto out; } for (drive_letter = 'C'; drive_letter < 'Z'; drive_letter++) { for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; if (drive_letter == (char)toupper((int)*drive)) break; } if (!*drive) break; } out: return (drive_letter>'Z')?0:drive_letter; }
/* Returns the number of bytes read or -1 on error */ int64_t read_sectors(HANDLE hDrive, uint64_t SectorSize, uint64_t StartSector, uint64_t nSectors, void *pBuf) { LARGE_INTEGER ptr; DWORD Size; if((nSectors*SectorSize) > 0xFFFFFFFFUL) { uprintf("read_sectors: nSectors x SectorSize is too big\n"); return -1; } Size = (DWORD)(nSectors*SectorSize); ptr.QuadPart = StartSector*SectorSize; if(!SetFilePointerEx(hDrive, ptr, NULL, FILE_BEGIN)) { uprintf("read_sectors: Could not access sector 0x%08" PRIx64 " - %s\n", StartSector, WindowsErrorString()); return -1; } if((!ReadFile(hDrive, pBuf, Size, &Size, NULL)) || (Size != nSectors*SectorSize)) { uprintf("read_sectors: Read error %s\n", (GetLastError()!=ERROR_SUCCESS)?WindowsErrorString():""); uprintf(" Read: %d, Expected: %" PRIu64 "\n", Size, nSectors*SectorSize); uprintf(" StartSector: 0x%08" PRIx64 ", nSectors: 0x%" PRIx64 ", SectorSize: 0x%" PRIx64 "\n", StartSector, nSectors, SectorSize); } return (int64_t)Size; }
/* * Mount the volume identified by drive_guid to mountpoint drive_name */ BOOL MountVolume(char* drive_name, char *drive_guid) { char mounted_guid[52]; // You need at least 51 characters on XP if (!SetVolumeMountPointA(drive_name, drive_guid)) { // If the OS was faster than us at remounting the drive, this operation can fail // with ERROR_DIR_NOT_EMPTY. If that's the case, just check that mountpoints match if (GetLastError() == ERROR_DIR_NOT_EMPTY) { if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) { uprintf("%s already mounted, but volume GUID could not be checked: %s\n", drive_name, WindowsErrorString()); return FALSE; } if (safe_strcmp(drive_guid, mounted_guid) != 0) { uprintf("%s already mounted, but volume GUID doesn't match:\r\n expected %s, got %s\n", drive_name, drive_guid, mounted_guid); return FALSE; } uprintf("%s was already mounted as %s\n", drive_guid, drive_name); } else { return FALSE; } } return TRUE; }
/* * 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; }
/* * Retrieve the SID of the current user. The returned PSID must be freed by the caller using LocalFree() */ static PSID GetSid(void) { TOKEN_USER* tu = NULL; DWORD len; HANDLE token; PSID ret = NULL; char* psid_string = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { dprintf("OpenProcessToken failed: %s", WindowsErrorString()); return NULL; } if (!GetTokenInformation(token, TokenUser, tu, 0, &len)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { dprintf("GetTokenInformation (pre) failed: %s", WindowsErrorString()); return NULL; } tu = (TOKEN_USER*)calloc(1, len); } if (tu == NULL) { return NULL; } if (GetTokenInformation(token, TokenUser, tu, len, &len)) { /* * now of course, the interesting thing is that if you return tu->User.Sid * but free tu, the PSID pointer becomes invalid after a while. * The workaround? Convert to string then back to PSID */ if (!ConvertSidToStringSidA(tu->User.Sid, &psid_string)) { dprintf("unable to convert SID to string: %s", WindowsErrorString()); ret = NULL; } else { if (!ConvertStringSidToSidA(psid_string, &ret)) { dprintf("unable to convert string back to SID: %s", WindowsErrorString()); ret = NULL; } // MUST use LocalFree() LocalFree(psid_string); } } else { ret = NULL; dprintf("GetTokenInformation (real) failed: %s", WindowsErrorString()); } free(tu); return ret; }
// Helper function to restore the timestamp on a directory static void __inline set_directory_timestamp(char* path, LPFILETIME creation, LPFILETIME last_access, LPFILETIME modify) { HANDLE dir_handle = CreateFileU(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if ((dir_handle == INVALID_HANDLE_VALUE) || (!SetFileTime(dir_handle, creation, last_access, modify))) uprintf(" Could not set timestamp for directory '%s': %s", path, WindowsErrorString()); safe_closehandle(dir_handle); }
/* * Cycle port (reset) the selected device */ BOOL ResetDevice(int index) { static uint64_t LastReset = 0; BOOL r = FALSE; HANDLE handle = INVALID_HANDLE_VALUE; DWORD size; USB_CYCLE_PORT_PARAMS cycle_port; // Wait at least 10 secs between resets if (GetTickCount64() < LastReset + 10000ULL) { uprintf("You must wait at least 10 seconds before trying to reset a device"); return FALSE; } if (DriveHub.String[index] == NULL) { uprintf("The device you are trying to reset does not appear to be a USB device..."); return FALSE; } LastReset = GetTickCount64(); handle = CreateFileA(DriveHub.String[index], GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { uprintf("Could not open %s: %s", DriveHub.String[index], WindowsErrorString()); goto out; } size = sizeof(cycle_port); memset(&cycle_port, 0, size); cycle_port.ConnectionIndex = DrivePort[index]; uprintf("Cycling port %d (reset) on %s", DrivePort[index], DriveHub.String[index]); // As per https://msdn.microsoft.com/en-us/library/windows/hardware/ff537340.aspx // IOCTL_USB_HUB_CYCLE_PORT is not supported on Windows 7, Windows Vista, and Windows Server 2008 if (!DeviceIoControl(handle, IOCTL_USB_HUB_CYCLE_PORT, &cycle_port, size, &cycle_port, size, &size, NULL)) { uprintf(" Failed to cycle port: %s", WindowsErrorString()); goto out; } uprintf("Please wait for the device to re-appear..."); r = TRUE; out: safe_closehandle(handle); return r; }
/* * Unmount of volume using the DISMOUNT_VOLUME ioctl */ BOOL UnmountVolume(HANDLE hDrive) { DWORD size; if (!DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &size, NULL)) { uprintf("Could not unmount drive: %s\n", WindowsErrorString()); return FALSE; } return TRUE; }
/* * Return the drive letter and volume label * If the drive doesn't have a volume assigned, space is returned for the letter */ BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label) { HANDLE hPhysical; DWORD size; static char VolumeLabel[MAX_PATH + 1]; char DrivePath[] = "#:\\", AutorunPath[] = "#:\\autorun.inf", *AutorunLabel = NULL; *label = STR_NO_LABEL; if (!GetDriveLetters(DriveIndex, letters)) return FALSE; if (letters[0] == 0) { // Drive without volume assigned - always enabled return TRUE; } // We only care about an autorun.inf if we have a single volume AutorunPath[0] = letters[0]; DrivePath[0] = letters[0]; // Try to read an extended label from autorun first. Fallback to regular label if not found. // 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 hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE); if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL)) AutorunLabel = get_token_data_file("label", AutorunPath); else if (GetLastError() == ERROR_NOT_READY) uprintf("Ignoring autorun.inf label for drive %c: %s\n", letters[0], (HRESULT_CODE(GetLastError()) == ERROR_NOT_READY)?"No media":WindowsErrorString()); safe_closehandle(hPhysical); if (AutorunLabel != NULL) { uprintf("Using autorun.inf label for drive %c: '%s'\n", letters[0], AutorunLabel); safe_strcpy(VolumeLabel, sizeof(VolumeLabel), AutorunLabel); safe_free(AutorunLabel); *label = VolumeLabel; } else if (GetVolumeInformationU(DrivePath, VolumeLabel, ARRAYSIZE(VolumeLabel), NULL, NULL, NULL, NULL, 0) && (VolumeLabel[0] != 0)) { *label = VolumeLabel; } else { duprintf("Failed to read label: %s", WindowsErrorString()); } return TRUE; }
/* * Mount the volume identified by drive_guid to mountpoint drive_name */ BOOL MountVolume(char* drive_name, char *drive_guid) { char mounted_guid[52]; // You need at least 51 characters on XP char mounted_letter[16] = {0}; DWORD size; if (drive_name[0] == '?') return FALSE; // For fixed disks, Windows may already have remounted the volume, but with a different letter // than the one we want. If that's the case, we need to unmount first. if ( (GetVolumePathNamesForVolumeNameA(drive_guid, mounted_letter, sizeof(mounted_letter), &size)) && (size > 1) && (mounted_letter[0] != drive_name[0]) ) { uprintf("Volume is already mounted, but as %c: instead of %c: - Unmounting...\n", mounted_letter[0], drive_name[0]); if (!DeleteVolumeMountPointA(mounted_letter)) uprintf("Failed to unmount volume: %s", WindowsErrorString()); // Also delete the destination mountpoint if needed (Don't care about errors) DeleteVolumeMountPointA(drive_name); Sleep(200); } if (!SetVolumeMountPointA(drive_name, drive_guid)) { // If the OS was faster than us at remounting the drive, this operation can fail // with ERROR_DIR_NOT_EMPTY. If that's the case, just check that mountpoints match if (GetLastError() == ERROR_DIR_NOT_EMPTY) { if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) { uprintf("%s already mounted, but volume GUID could not be checked: %s\n", drive_name, WindowsErrorString()); return FALSE; } if (safe_strcmp(drive_guid, mounted_guid) != 0) { uprintf("%s already mounted, but volume GUID doesn't match:\r\n expected %s, got %s\n", drive_name, drive_guid, mounted_guid); return FALSE; } uprintf("%s was already mounted as %s\n", drive_guid, drive_name); } else { return FALSE; } } return TRUE; }
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) uprintf("Could not refresh drive layout: %s\n", WindowsErrorString()); return r; }
/* * Unmount a volume that was mounted by AltmountVolume() */ BOOL AltUnmountVolume(const char* drive_name) { if (drive_name == NULL) return FALSE; if (!DefineDosDeviceA(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, drive_name, NULL)) { uprintf("Could not unmount '%s': %s", drive_name, WindowsErrorString()); return FALSE; } uprintf("Successfully unmounted '%s'", drive_name); return TRUE; }
/* * 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, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { uprintf("Failed to open %c: for flushing: %s\n", drive_letter, WindowsErrorString()); goto out; } r = FlushFileBuffers(hDrive); if (r == FALSE) uprintf("Failed to flush %c: %s\n", drive_letter, WindowsErrorString()); out: safe_closehandle(hDrive); return r; }
BOOL SmartGetVersion(HANDLE hdevice) { MY_GETVERSIONINPARAMS vers; DWORD size = sizeof(MY_GETVERSIONINPARAMS); BOOL r; memset(&vers, 0, sizeof(vers)); r = DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &size, NULL); if ( (!r) || (size != sizeof(MY_GETVERSIONINPARAMS)) ) { uprintf("SmartGetVersion failed: %s\n", r?"unexpected size":WindowsErrorString()); return FALSE; } uprintf("Smart Version: %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n", vers.bVersion, vers.bRevision, (unsigned)vers.fCapabilities, vers.bIDEDeviceMap); return vers.bIDEDeviceMap; }
/* * Return the drive letter and volume label * If the drive doesn't have a volume assigned, space is returned for the letter */ BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label) { HANDLE hPhysical; DWORD size; char AutorunPath[] = "#:\\autorun.inf", *AutorunLabel = NULL; wchar_t wDrivePath[] = L"#:\\"; wchar_t wVolumeLabel[MAX_PATH+1]; static char VolumeLabel[MAX_PATH+1]; *label = STR_NO_LABEL; if (!GetDriveLetter(DriveIndex, letter)) return FALSE; if (*letter == ' ') { // Drive without volume assigned - always enabled return TRUE; } AutorunPath[0] = *letter; wDrivePath[0] = *letter; // Try to read an extended label from autorun first. Fallback to regular label if not found. // 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 hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE); if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL)) AutorunLabel = get_token_data_file("label", AutorunPath); else if (GetLastError() == ERROR_NOT_READY) uprintf("Ignoring autorun.inf label for drive %c: %s\n", *letter, (HRESULT_CODE(GetLastError()) == ERROR_NOT_READY)?"No media":WindowsErrorString()); safe_closehandle(hPhysical); if (AutorunLabel != NULL) { uprintf("Using autorun.inf label for drive %c: '%s'\n", *letter, AutorunLabel); strncpy(VolumeLabel, AutorunLabel, sizeof(VolumeLabel)); safe_free(AutorunLabel); *label = VolumeLabel; } else if (GetVolumeInformationW(wDrivePath, wVolumeLabel, ARRAYSIZE(wVolumeLabel), NULL, NULL, NULL, NULL, 0) && *wVolumeLabel) { wchar_to_utf8_no_alloc(wVolumeLabel, VolumeLabel, sizeof(VolumeLabel)); *label = VolumeLabel; } return TRUE; }
BOOL WriteFileWithRetry(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, DWORD nNumRetries) { DWORD nTry; BOOL readFilePointer; LARGE_INTEGER liFilePointer, liZero = { { 0,0 } }; static char* retry_msg = " - retrying..."; // Need to get the current file pointer in case we need to retry readFilePointer = SetFilePointerEx(hFile, liZero, &liFilePointer, FILE_CURRENT); if (!readFilePointer) uprintf(" Warning - Could not read file pointer: %s", WindowsErrorString()); if (nNumRetries == 0) nNumRetries = 1; for (nTry = 1; nTry <= nNumRetries; nTry++) { // Need to rewind our file position on retry - if we can't even do that, just give up if ((nTry > 1) && (!SetFilePointerEx(hFile, liFilePointer, NULL, FILE_BEGIN))) { uprintf(" Could not set file pointer - aborting"); break; } if (WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, NULL)) { if (nNumberOfBytesToWrite == *lpNumberOfBytesWritten) return TRUE; // Some large drives return 0, even though all the data was written - See github #787 */ if (large_drive && (*lpNumberOfBytesWritten == 0)) { uprintf("Warning: Possible short write"); return TRUE; } uprintf(" Wrote %d bytes but requested %d%s", *lpNumberOfBytesWritten, nNumberOfBytesToWrite, nTry < nNumRetries ? retry_msg : ""); } else { uprintf(" Write error [0x%08X]%s", GetLastError(), nTry < nNumRetries ? retry_msg : ""); } // If we can't reposition for the next run, just abort if (!readFilePointer) break; Sleep(200); } if (SCODE_CODE(GetLastError()) == ERROR_SUCCESS) SetLastError(ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT); return FALSE; }
/* * Issue a complete remount of the volume */ BOOL RemountVolume(char* drive_name) { char drive_guid[51]; // UDF requires a sync/flush, and it's also a good idea for other FS's FlushDrive(drive_name[0]); if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) { if (DeleteVolumeMountPointA(drive_name)) { Sleep(200); if (MountVolume(drive_name, drive_guid)) { uprintf("Successfully remounted %s on %s\n", &drive_guid[4], drive_name); } else { uprintf("Failed to remount %s on %s\n", &drive_guid[4], drive_name); // 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 { uprintf("Could not remount %s %s\n", drive_name, WindowsErrorString()); // Try to continue regardless } } return TRUE; }
/* * Return the first GUID volume name for the associated drive or NULL if not found * See http://msdn.microsoft.com/en-us/library/cc542456.aspx * The returned string is allocated and must be freed */ char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent) { BOOL success = FALSE; char volume_name[MAX_PATH]; HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE; size_t len; char path[MAX_PATH]; VOLUME_DISK_EXTENTS DiskExtents; DWORD size; UINT drive_type; int i, j; static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" }; static const char* volume_start = "\\\\?\\"; CheckDriveIndex(DriveIndex); for (i=0; hDrive == INVALID_HANDLE_VALUE; i++) { if (i == 0) { hVolume = FindFirstVolumeA(volume_name, sizeof(volume_name)); if (hVolume == INVALID_HANDLE_VALUE) { suprintf("Could not access first GUID volume: %s\n", WindowsErrorString()); goto out; } } else { if (!FindNextVolumeA(hVolume, volume_name, sizeof(volume_name))) { if (GetLastError() != ERROR_NO_MORE_FILES) { suprintf("Could not access next GUID volume: %s\n", WindowsErrorString()); } goto out; } } // Sanity checks len = safe_strlen(volume_name); if ((len <= 1) || (safe_strnicmp(volume_name, volume_start, 4) != 0) || (volume_name[len-1] != '\\')) { suprintf("'%s' is not a GUID volume name\n", volume_name); continue; } drive_type = GetDriveTypeA(volume_name); if ((drive_type != DRIVE_REMOVABLE) && (drive_type != DRIVE_FIXED)) continue; volume_name[len-1] = 0; if (QueryDosDeviceA(&volume_name[4], path, sizeof(path)) == 0) { suprintf("Failed to get device path for GUID volume '%s': %s\n", volume_name, WindowsErrorString()); continue; } for (j=0; (j<ARRAYSIZE(ignore_device)) && (_strnicmp(path, ignore_device[j], safe_strlen(ignore_device[j])) != 0); j++); if (j < ARRAYSIZE(ignore_device)) { suprintf("Skipping GUID volume for '%s'\n", path); continue; } // If we can't have FILE_SHARE_WRITE, forget it hDrive = CreateFileA(volume_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { suprintf("Could not open GUID volume '%s': %s\n", volume_name, WindowsErrorString()); continue; } if ((!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &DiskExtents, sizeof(DiskExtents), &size, NULL)) || (size <= 0)) { suprintf("Could not get Disk Extents: %s\n", WindowsErrorString()); safe_closehandle(hDrive); continue; } safe_closehandle(hDrive); if ((DiskExtents.NumberOfDiskExtents >= 1) && (DiskExtents.Extents[0].DiskNumber == DriveIndex)) { if (bKeepTrailingBackslash) volume_name[len-1] = '\\'; success = TRUE; break; } } out: if (hVolume != INVALID_HANDLE_VALUE) FindVolumeClose(hVolume); return (success)?safe_strdup(volume_name):NULL; }
/* * Refresh the list of USB devices */ BOOL GetUSBDevices(DWORD devnum) { // The first two are standard Microsoft drivers (including the Windows 8 UASP one). // The rest are the vendor UASP drivers I know of so far - list may be incomplete! const char* storage_name[] = { "USBSTOR", "UASPSTOR", "VUSBSTOR", "ETRONSTOR", "ASUSSTPT" }; const char* scsi_name = "SCSI"; const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0" }; // Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path htab_table htab_devid = HTAB_EMPTY; StrArray dev_if_path; char letter_name[] = " (?:)"; char uefi_togo_check[] = "?:\\EFI\\Rufus\\ntfs_x64.efi"; BOOL r = FALSE, found = FALSE, is_SCSI; HDEVINFO dev_info = NULL; SP_DEVINFO_DATA dev_info_data; SP_DEVICE_INTERFACE_DATA devint_data; PSP_DEVICE_INTERFACE_DETAIL_DATA_A devint_detail_data; DEVINST parent_inst, grandparent_inst, device_inst; DWORD size, i, j, k, l, datatype, drive_index; ULONG list_size[ARRAYSIZE(storage_name)] = { 0 }, list_start[ARRAYSIZE(storage_name)] = { 0 }, full_list_size, ulFlags; HANDLE hDrive; LONG maxwidth = 0; int s, score, drive_number; char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128]; char *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str; usb_device_props props; IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList)); StrArrayClear(&DriveID); StrArrayClear(&DriveLabel); StrArrayCreate(&dev_if_path, 128); // Add a dummy for string index zero, as this is what non matching hashes will point to StrArrayAdd(&dev_if_path, ""); device_id = (char*)malloc(MAX_PATH); if (device_id == NULL) goto out; // Build a hash table associating a CM Device ID of an USB device with the SetupDI Device Interface Path // of its parent hub - this is needed to retrieve the device speed dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info != INVALID_HANDLE_VALUE) { if (htab_create(DEVID_HTAB_SIZE, &htab_devid)) { dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { if (usb_debug) uprintf("Processing Hub %d:", i + 1); devint_detail_data = NULL; devint_data.cbSize = sizeof(devint_data); // Only care about the first interface (MemberIndex 0) if ( (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_USB_HUB, 0, &devint_data)) && (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && ((devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size)) != NULL) ) { devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { // Find the Device IDs for all the children of this hub if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { device_id[0] = 0; s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath); if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } if (usb_debug) uprintf(" Found ID[%03d]: %s", k, device_id); while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { device_id[0] = 0; if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) { if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } if (usb_debug) uprintf(" Found ID[%03d]: %s", k, device_id); } } } } } free(devint_detail_data); } } } SetupDiDestroyDeviceInfoList(dev_info); } free(device_id); // Build a single list of Device IDs from all the storage enumerators we know of full_list_size = 0; ulFlags = CM_GETIDLIST_FILTER_SERVICE; if (nWindowsVersion >= WINDOWS_7) ulFlags |= CM_GETIDLIST_FILTER_PRESENT; for (s=0; s<ARRAYSIZE(storage_name); s++) { // Get a list of device IDs for all USB storage devices // This will be used to find if a device is UASP if (CM_Get_Device_ID_List_SizeA(&list_size[s], storage_name[s], ulFlags) != CR_SUCCESS) list_size[s] = 0; if (list_size[s] != 0) full_list_size += list_size[s]-1; // remove extra NUL terminator } devid_list = NULL; if (full_list_size != 0) { full_list_size += 1; // add extra NUL terminator devid_list = (char*)malloc(full_list_size); if (devid_list == NULL) { uprintf("Could not allocate Device ID list\n"); return FALSE; } for (s=0, i=0; s<ARRAYSIZE(storage_name); s++) { list_start[s] = i; if (list_size[s] > 1) { if (CM_Get_Device_ID_ListA(storage_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS) continue; if (usb_debug) { uprintf("Processing IDs belonging to %s:", storage_name[s]); for (device_id = &devid_list[i]; *device_id != 0; device_id += strlen(device_id) + 1) uprintf(" %s", device_id); } // The list_size is sometimes larger than required thus we need to find the real end for (i += list_size[s]; i > 2; i--) { if ((devid_list[i-2] != '\0') && (devid_list[i-1] == '\0') && (devid_list[i] == '\0')) break; } } } } // Now use SetupDi to enumerate all our storage devices dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info == INVALID_HANDLE_VALUE) { uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString()); goto out; } dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { memset(buffer, 0, sizeof(buffer)); method_str = ""; if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME, &datatype, (LPBYTE)buffer, sizeof(buffer), &size)) { uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString()); continue; } // UASP drives are listed under SCSI (along with regular SYSTEM drives => "DANGER, WILL ROBINSON!!!") is_SCSI = (safe_stricmp(buffer, scsi_name) == 0); if ((safe_stricmp(buffer, storage_name[0]) != 0) && (!is_SCSI)) continue; // We can't use the friendly name to find if a drive is a VHD, as friendly name string gets translated // according to your locale, so we poke the Hardware ID memset(&props, 0, sizeof(props)); memset(buffer, 0, sizeof(buffer)); props.is_VHD = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_HARDWAREID, &datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsVHD(buffer); if (usb_debug) uprintf("Processing Device: '%s'", buffer); memset(buffer, 0, sizeof(buffer)); if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_FRIENDLYNAME, &datatype, (LPBYTE)buffer, sizeof(buffer), &size)) { uprintf("SetupDiGetDeviceRegistryProperty (Friendly Name) failed: %s\n", WindowsErrorString()); // We can afford a failure on this call - just replace the name with "USB Storage Device (Generic)" safe_strcpy(buffer, sizeof(buffer), lmprintf(MSG_045)); } else if ((!props.is_VHD) && (devid_list != NULL)) { // Get the properties of the device. We could avoid doing this lookup every time by keeping // a lookup table, but there shouldn't be that many USB storage devices connected... // NB: Each of these Device IDs have an _only_ child, from which we get the Device Instance match. for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) { if ( (CM_Locate_DevNodeA(&parent_inst, device_id, 0) == CR_SUCCESS) && (CM_Get_Child(&device_inst, parent_inst, 0) == CR_SUCCESS) && (device_inst == dev_info_data.DevInst) ) { // If we're not dealing with the USBSTOR part of our list, then this is an UASP device props.is_UASP = ((((uintptr_t)device_id)+2) >= ((uintptr_t)devid_list)+list_start[1]); // Now get the properties of the device, and its Device ID, which we need to populate the properties j = htab_hash(device_id, &htab_devid); if (usb_debug) uprintf(" Matched with ID[%03d]: %s", j, device_id); // If the hash didn't match a populated string in dev_if_path[] (htab_devid.table[j].data > 0), // we might have an extra vendor driver in between (e.g. "ASUS USB 3.0 Boost Storage Driver" // for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods) // so try to see if we can match the grandparent. if ( ((uint32_t)htab_devid.table[j].data == 0) && (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS) && (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) { device_id = str; method_str = "[GP]"; j = htab_hash(device_id, &htab_devid); if (usb_debug) uprintf(" Matched with (GP) ID[%03d]: %s", j, device_id); } if ((uint32_t)htab_devid.table[j].data > 0) GetUSBProperties(dev_if_path.String[(uint32_t)htab_devid.table[j].data], device_id, &props); if (usb_debug) uprintf(" Props VID:PID = %04X:%04X", props.vid, props.pid); // If previous calls still didn't succeed, try reading the VID:PID from the device_id if ((props.vid == 0) && (props.pid == 0)) { BOOL post_backslash = FALSE; method_str = "[ID]"; for (j=0, k=0; (j<strlen(device_id))&&(k<2); j++) { // The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\... if (device_id[j] == '\\') post_backslash = TRUE; if (!post_backslash) continue; if (device_id[j] == '_') { props.pid = (uint16_t)strtoul(&device_id[j+1], NULL, 16); if (k++==0) props.vid = props.pid; } } } } } } if (props.is_VHD) { uprintf("Found VHD device '%s'", buffer); } else { if ((props.vid == 0) && (props.pid == 0)) { if (is_SCSI) { // If we have an SCSI drive and couldn't get a VID:PID, we are most likely // dealing with a system drive => eliminate it! if (usb_debug) uprintf(" Non USB => Eliminated"); continue; } safe_strcpy(str, sizeof(str), "????:????"); // Couldn't figure VID:PID } else { static_sprintf(str, "%04X:%04X", props.vid, props.pid); } if (props.speed >= USB_SPEED_MAX) props.speed = 0; uprintf("Found %s%s%s device '%s' (%s) %s\n", props.is_UASP?"UAS (":"", usb_speed_name[props.speed], props.is_UASP?")":"", buffer, str, method_str); if (props.is_LowerSpeed) uprintf("NOTE: This device is an USB 3.0 device operating at lower speed..."); } devint_data.cbSize = sizeof(devint_data); hDrive = INVALID_HANDLE_VALUE; devint_detail_data = NULL; for (j=0; ;j++) { safe_closehandle(hDrive); safe_free(devint_detail_data); if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_DISK, j, &devint_data)) { if(GetLastError() != ERROR_NO_MORE_ITEMS) { uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString()); } else { uprintf("A device was eliminated because it didn't report itself as a disk\n"); } break; } if (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size); if (devint_detail_data == NULL) { uprintf("Unable to allocate data for SP_DEVICE_INTERFACE_DETAIL_DATA\n"); continue; } devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); } else { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) failed: %s\n", WindowsErrorString()); continue; } } if (devint_detail_data == NULL) { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) - no data was allocated\n"); continue; } if(!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { uprintf("SetupDiGetDeviceInterfaceDetail (actual) failed: %s\n", WindowsErrorString()); continue; } hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDrive == INVALID_HANDLE_VALUE) { uprintf("Could not open '%s': %s\n", devint_detail_data->DevicePath, WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, devint_detail_data->DevicePath); if (drive_number < 0) continue; drive_index = drive_number + DRIVE_INDEX_MIN; if (!IsMediaPresent(drive_index)) { uprintf("Device eliminated because it appears to contain no media\n"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } if (GetDriveLabel(drive_index, drive_letters, &label)) { if ((!enable_HDDs) && (!props.is_VHD) && ((score = IsHDD(drive_index, (uint16_t)props.vid, (uint16_t)props.pid, buffer)) > 0)) { uprintf("Device eliminated because it was detected as an USB Hard Drive (score %d > 0)\n", score); uprintf("If this device is not an USB Hard Drive, please e-mail the author of this application\n"); uprintf("NOTE: You can enable the listing of USB Hard Drives in 'Advanced Options' (after clicking the white triangle)"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } // The empty string is returned for drives that don't have any volumes assigned if (drive_letters[0] == 0) { entry = lmprintf(MSG_046, label, drive_number, SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units)); } else { // Find the UEFI:TOGO partition(s) (and eliminate them form our listing) for (k=0; drive_letters[k]; k++) { uefi_togo_check[0] = drive_letters[k]; if (PathFileExistsA(uefi_togo_check)) { for (l=k; drive_letters[l]; l++) drive_letters[l] = drive_letters[l+1]; k--; } } // We have multiple volumes assigned to the same device (multiple partitions) // If that is the case, use "Multiple Volumes" instead of the label safe_strcpy(entry_msg, sizeof(entry_msg), ((drive_letters[0] != 0) && (drive_letters[1] != 0))? lmprintf(MSG_047):label); for (k=0; drive_letters[k]; k++) { // Append all the drive letters we detected letter_name[2] = drive_letters[k]; if (right_to_left_mode) safe_strcat(entry_msg, sizeof(entry_msg), RIGHT_TO_LEFT_MARK); safe_strcat(entry_msg, sizeof(entry_msg), letter_name); if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) break; } // Repeat as we need to break the outside loop if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) { uprintf("Removing %c: from the list: This is the disk from which " APPLICATION_NAME " is running!\n", app_dir[0]); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } safe_sprintf(&entry_msg[strlen(entry_msg)], sizeof(entry_msg) - strlen(entry_msg), "%s [%s]", (right_to_left_mode)?RIGHT_TO_LEFT_MARK:"", SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units)); entry = entry_msg; } // Must ensure that the combo box is UNSORTED for indexes to be the same StrArrayAdd(&DriveID, buffer); StrArrayAdd(&DriveLabel, label); IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index)); maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry)); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } } } SetupDiDestroyDeviceInfoList(dev_info); // Adjust the Dropdown width to the maximum text size SendMessage(hDeviceList, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0); if (devnum >= DRIVE_INDEX_MIN) { for (i=0; i<ComboBox_GetCount(hDeviceList); i++) { if ((DWORD)ComboBox_GetItemData(hDeviceList, i) == devnum) { found = TRUE; break; } } } if (!found) i = 0; IGNORE_RETVAL(ComboBox_SetCurSel(hDeviceList, i)); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_DEVICE, 0); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_FILESYSTEM, ComboBox_GetCurSel(hFileSystem)); r = TRUE; out: // Set 'Start' as the selected button, so that tab selection works SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDC_START), TRUE); safe_free(devid_list); StrArrayDestroy(&dev_if_path); htab_destroy(&htab_devid); return r; }
/* * Mount partition #part_nr, residing on the same disk as drive_name to an available * drive letter. Returns the newly allocated drive string. * We need to do this because, for instance, EFI System partitions are not assigned * Volume GUIDs by the OS, and we need to have a letter assigned, for when we invoke * bcdtool for Windows To Go. All in all, the process looks like this: * 1. F: = \Device\HarddiskVolume9 (SINGLE LOOKUP) * 2. Harddisk5Partition1 = \Device\HarddiskVolume9 (FULL LOOKUP) * 3. Harddisk5Partition2 = \Device\HarddiskVolume10 (SINGLE LOOKUP) * 4. DefineDosDevice(letter, \Device\HarddiskVolume10) */ char* AltMountVolume(const char* drive_name, uint8_t part_nr) { static char mounted_drive[] = "?:"; const DWORD bufsize = 65536; char *buffer = NULL, *p, target[2][MAX_PATH], *ret = NULL; size_t i; mounted_drive[0] = GetUnusedDriveLetter(); if (mounted_drive[0] == 0) { uprintf("Could not find an unused drive letter"); goto out; } target[0][0] = 0; // Convert our drive letter to something like "\Device\HarddiskVolume9" if (!QueryDosDeviceA(drive_name, target[0], MAX_PATH) || (strlen(target[0]) == 0)) { uprintf("Could not get the DOS volume name for '%s': %s", drive_name, WindowsErrorString()); goto out; } // Now parse the whole DOS device list to find the 'Harddisk#Partition#' that matches the above // TODO: realloc if someone ever manages to burst through 64K of DOS devices buffer = malloc(bufsize); if (buffer == NULL) goto out; buffer[0] = 0; if (!QueryDosDeviceA(NULL, buffer, bufsize)) { uprintf("Could not get the DOS device list: %s", WindowsErrorString()); goto out; } p = buffer; while (strlen(p) != 0) { if ((strncmp("Harddisk", p, 8) == 0) && (strstr(&p[9], "Partition") != NULL)) { target[1][0] = 0; if (QueryDosDeviceA(p, target[1], MAX_PATH) && (strlen(target[1]) != 0)) if ((strcmp(target[1], target[0]) == 0) && (p[1] != ':')) break; } p += strlen(p) + 1; } i = strlen(p); if (i == 0) { uprintf("Could not find partition mapping for %s", target[0]); goto out; } while ((--i > 0) && (isdigit(p[i]))); p[++i] = '0' + part_nr; p[++i] = 0; target[0][0] = 0; if (!QueryDosDeviceA(p, target[0], MAX_PATH) || (strlen(target[0]) == 0)) { uprintf("Could not find the DOS volume name for partition '%s': %s", p, WindowsErrorString()); goto out; } if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, mounted_drive, target[0])) { uprintf("Could not mount '%s' to '%s': %s", target[0], mounted_drive, WindowsErrorString()); goto out; } uprintf("Successfully mounted '%s' (USB partition %d) as '%s'", target[0], part_nr, mounted_drive); ret = mounted_drive; out: safe_free(buffer); return ret; }
/* * Create a partition table * 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 * copy it got from the last IOCTL, and ignores your changes until you replug the drive * or issue an IOCTL_DISK_UPDATE_PROPERTIES. */ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL mbr_uefi_marker, uint8_t extra_partitions) { const char* PartitionTypeName[2] = { "MBR", "GPT" }; unsigned char* buffer; size_t uefi_ntfs_size = 0; CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}}; DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0}; BOOL r; DWORD i, size, bufsize, pn = 0; LONGLONG main_part_size_in_sectors, extra_part_size_in_tracks = 0, ms_efi_size; const LONGLONG bytes_per_track = ((LONGLONG)SelectedDrive.Geometry.SectorsPerTrack) * SelectedDrive.Geometry.BytesPerSector; PrintInfoDebug(0, MSG_238, PartitionTypeName[partition_style]); if (extra_partitions & XP_UEFI_NTFS) { uefi_ntfs_size = GetResourceSize(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img"); if (uefi_ntfs_size == 0) return FALSE; } // Compute the start offset of our first partition if ((partition_style == PARTITION_STYLE_GPT) || (!IsChecked(IDC_EXTRA_PARTITION))) { // Go with the MS 1 MB wastage at the beginning... DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = MB; } else { // Align on Cylinder DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = bytes_per_track; } // If required, set the MSR partition (GPT only - must be created before the data part) if ((partition_style == PARTITION_STYLE_GPT) && (extra_partitions & XP_MSR)) { uprintf("Adding MSR partition"); DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = 128*MB; DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_MSFT_RESERVED_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); // coverity[strcpy_overrun] wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Microsoft reserved partition"); // We must zero the beginning of this partition, else we get FAT leftovers and stuff if (SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) { bufsize = 65536; // 64K should be enough for everyone buffer = calloc(bufsize, 1); if (buffer != NULL) { if (!WriteFileWithRetry(hDrive, buffer, bufsize, &size, WRITE_RETRIES)) uprintf(" Could not zero MSR: %s", WindowsErrorString()); free(buffer); } } pn++; DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart + DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart; } // Set our main data partition main_part_size_in_sectors = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart) / // Need 33 sectors at the end for secondary GPT SelectedDrive.Geometry.BytesPerSector - ((partition_style == PARTITION_STYLE_GPT)?33:0); if (main_part_size_in_sectors <= 0) return FALSE; // Adjust the size according to extra partitions (which we always align to a track) if (extra_partitions) { uprintf("Adding extra partition"); if (extra_partitions & XP_EFI) { // The size of the EFI partition 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. if (SelectedDrive.Geometry.BytesPerSector <= 1024) ms_efi_size = 100*MB; else if (SelectedDrive.Geometry.BytesPerSector <= 4096) ms_efi_size = 300*MB; else ms_efi_size = 1200*MB; // That'll teach you to have a nonstandard disk! extra_part_size_in_tracks = (ms_efi_size + bytes_per_track - 1) / bytes_per_track; } else if (extra_partitions & XP_UEFI_NTFS) extra_part_size_in_tracks = (MIN_EXTRA_PART_SIZE + bytes_per_track - 1) / bytes_per_track; else if (extra_partitions & XP_COMPAT) extra_part_size_in_tracks = 1; // One track for the extra partition uprintf("Reserved %" PRIi64" tracks (%s) for extra partition", extra_part_size_in_tracks, SizeToHumanReadable(extra_part_size_in_tracks * bytes_per_track, TRUE, FALSE)); main_part_size_in_sectors = ((main_part_size_in_sectors / SelectedDrive.Geometry.SectorsPerTrack) - extra_part_size_in_tracks) * SelectedDrive.Geometry.SectorsPerTrack; if (main_part_size_in_sectors <= 0) return FALSE; } DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = main_part_size_in_sectors * SelectedDrive.Geometry.BytesPerSector; if (partition_style == PARTITION_STYLE_MBR) { DriveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = IsChecked(IDC_BOOT); 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; case FS_FAT32: DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x0c; // FAT32 LBA break; default: uprintf("Unsupported file system\n"); return FALSE; } } else { DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, L"Microsoft Basic Data"); } pn++; // Set the optional extra partition if (extra_partitions) { // Should end on a track boundary DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart + DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart; DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart = (extra_partitions & XP_UEFI_NTFS)?uefi_ntfs_size: extra_part_size_in_tracks * SelectedDrive.Geometry.SectorsPerTrack * SelectedDrive.Geometry.BytesPerSector; if (partition_style == PARTITION_STYLE_GPT) { DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionType = (extra_partitions & XP_UEFI_NTFS)? PARTITION_BASIC_DATA_GUID:PARTITION_SYSTEM_GUID; IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[pn].Gpt.PartitionId)); wcscpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, (extra_partitions & XP_UEFI_NTFS)?L"UEFI:NTFS":L"EFI system partition"); } else { DriveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = (extra_partitions & XP_UEFI_NTFS)?0xef:RUFUS_EXTRA_PARTITION_TYPE; if (extra_partitions & XP_COMPAT) // Set the one track compatibility partition to be all hidden sectors DriveLayoutEx.PartitionEntry[pn].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack; } // We need to write the UEFI:NTFS partition before we refresh the disk if (extra_partitions & XP_UEFI_NTFS) { uprintf("Writing UEFI:NTFS partition..."); if (!SetFilePointerEx(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, NULL, FILE_BEGIN)) { uprintf("Unable to set position"); return FALSE; } buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_UEFI_NTFS), _RT_RCDATA, "uefi-ntfs.img", &bufsize, FALSE); if (buffer == NULL) { uprintf("Could not access uefi-ntfs.img"); return FALSE; } if(!WriteFileWithRetry(hDrive, buffer, bufsize, &size, WRITE_RETRIES)) { uprintf("Write error: %s", WindowsErrorString()); return FALSE; } } pn++; } // Initialize the remaining partition data for (i = 0; i < pn; i++) { DriveLayoutEx.PartitionEntry[i].PartitionNumber = i+1; DriveLayoutEx.PartitionEntry[i].PartitionStyle = partition_style; DriveLayoutEx.PartitionEntry[i].RewritePartition = TRUE; } switch (partition_style) { case PARTITION_STYLE_MBR: CreateDisk.PartitionStyle = PARTITION_STYLE_MBR; // 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. CreateDisk.Mbr.Signature = mbr_uefi_marker?MBR_UEFI_MARKER:(DWORD)_GetTickCount64(); 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: // TODO: (?) As per MSDN: "When specifying a GUID partition table (GPT) as the PARTITION_STYLE of the CREATE_DISK // 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." CreateDisk.PartitionStyle = PARTITION_STYLE_GPT; IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId)); CreateDisk.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; DriveLayoutEx.PartitionStyle = PARTITION_STYLE_GPT; DriveLayoutEx.PartitionCount = pn; // At the very least, a GPT disk has 34 reserved sectors at the beginning and 33 at the end. DriveLayoutEx.Type.Gpt.StartingUsableOffset.QuadPart = 34 * SelectedDrive.Geometry.BytesPerSector; DriveLayoutEx.Type.Gpt.UsableLength.QuadPart = SelectedDrive.DiskSize - (34+33) * SelectedDrive.Geometry.BytesPerSector; DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; DriveLayoutEx.Type.Gpt.DiskId = CreateDisk.Gpt.DiskId; break; } // If you don't call IOCTL_DISK_CREATE_DISK, the next call will fail size = sizeof(CreateDisk); r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL ); if (!r) { uprintf("Could not reset disk: %s\n", WindowsErrorString()); return FALSE; } size = sizeof(DriveLayoutEx) - ((partition_style == PARTITION_STYLE_GPT)?((4-pn)*sizeof(PARTITION_INFORMATION_EX)):0); r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL ); if (!r) { uprintf("Could not set drive layout: %s\n", WindowsErrorString()); return FALSE; } if (!RefreshDriveLayout(hDrive)) return FALSE; return TRUE; }
/* * Fill the drive properties (size, FS, etc) * Returns TRUE if the drive has a partition that can be mounted in Windows, FALSE otherwise */ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSystemNameSize, BOOL bSilent) { // MBR partition types that can be mounted in Windows const uint8_t mbr_mountable[] = { 0x01, 0x04, 0x06, 0x07, 0x0b, 0x0c, 0x0e, 0xef }; BOOL r, ret = FALSE, isUefiNtfs = FALSE; HANDLE hPhysical; DWORD size; BYTE geometry[256] = {0}, layout[4096] = {0}, part_type; PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; char* volume_name; char tmp[256]; DWORD i, j; if (FileSystemName == NULL) return FALSE; SelectedDrive.nPartitions = 0; // Populate the filesystem data FileSystemName[0] = 0; volume_name = GetLogicalName(DriveIndex, TRUE, FALSE); if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) { suprintf("No volume information for drive 0x%02x\n", DriveIndex); } safe_free(volume_name); hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE); if (hPhysical == INVALID_HANDLE_VALUE) return 0; r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); if (!r || size <= 0) { suprintf("Could not get geometry for drive 0x%02x: %s\n", DriveIndex, WindowsErrorString()); safe_closehandle(hPhysical); return 0; } if (DiskGeometry->Geometry.BytesPerSector < 512) { suprintf("Warning: Drive 0x%02x reports a sector size of %d - Correcting to 512 bytes.\n", DriveIndex, DiskGeometry->Geometry.BytesPerSector); DiskGeometry->Geometry.BytesPerSector = 512; } SelectedDrive.DiskSize = DiskGeometry->DiskSize.QuadPart; memcpy(&SelectedDrive.Geometry, &DiskGeometry->Geometry, sizeof(DISK_GEOMETRY)); suprintf("Disk type: %s, Sector Size: %d bytes\n", (DiskGeometry->Geometry.MediaType == FixedMedia)?"Fixed":"Removable", DiskGeometry->Geometry.BytesPerSector); suprintf("Cylinders: %" PRIi64 ", TracksPerCylinder: %d, SectorsPerTrack: %d\n", DiskGeometry->Geometry.Cylinders, DiskGeometry->Geometry.TracksPerCylinder, DiskGeometry->Geometry.SectorsPerTrack); r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL ); if (!r || size <= 0) { suprintf("Could not get layout for drive 0x%02x: %s\n", DriveIndex, WindowsErrorString()); safe_closehandle(hPhysical); return 0; } #if defined(__GNUC__) // GCC 4.9 bugs us about the fact that MS defined an expandable array as array[1] #pragma GCC diagnostic ignored "-Warray-bounds" #endif switch (DriveLayout->PartitionStyle) { case PARTITION_STYLE_MBR: SelectedDrive.PartitionType = PARTITION_STYLE_MBR; for (i=0; i<DriveLayout->PartitionCount; i++) { if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { SelectedDrive.nPartitions++; } } suprintf("Partition type: MBR, NB Partitions: %d\n", SelectedDrive.nPartitions); SelectedDrive.has_mbr_uefi_marker = (DriveLayout->Mbr.Signature == MBR_UEFI_MARKER); suprintf("Disk ID: 0x%08X %s\n", DriveLayout->Mbr.Signature, SelectedDrive.has_mbr_uefi_marker?"(UEFI target)":""); AnalyzeMBR(hPhysical, "Drive"); for (i=0; i<DriveLayout->PartitionCount; i++) { if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { part_type = DriveLayout->PartitionEntry[i].Mbr.PartitionType; isUefiNtfs = (i == 1) && (part_type == 0xef) && (DriveLayout->PartitionEntry[i].PartitionLength.QuadPart <= 1*MB); suprintf("Partition %d%s:\n", i+1, isUefiNtfs?" (UEFI:NTFS)":""); for (j=0; j<ARRAYSIZE(mbr_mountable); j++) { if (part_type == mbr_mountable[j]) { ret = TRUE; break; } } // NB: MinGW's gcc 4.9.2 broke "%lld" printout on XP so we use the inttypes.h "PRI##" qualifiers suprintf(" Type: %s (0x%02x)\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %d, Boot: %s, Recognized: %s\n", ((part_type==0x07)&&(FileSystemName[0]!=0))?FileSystemName:GetPartitionType(part_type), part_type, SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE), DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, DriveLayout->PartitionEntry[i].Mbr.HiddenSectors, DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No", DriveLayout->PartitionEntry[i].Mbr.RecognizedPartition?"Yes":"No"); if ((part_type == RUFUS_EXTRA_PARTITION_TYPE) || (isUefiNtfs)) // This is a partition Rufus created => we can safely ignore it --SelectedDrive.nPartitions; if (part_type == 0xee) // Flag a protective MBR for non GPT platforms (XP) SelectedDrive.has_protective_mbr = TRUE; } } break; case PARTITION_STYLE_GPT: SelectedDrive.PartitionType = PARTITION_STYLE_GPT; suprintf("Partition type: GPT, NB Partitions: %d\n", DriveLayout->PartitionCount); suprintf("Disk GUID: %s\n", GuidToString(&DriveLayout->Gpt.DiskId)); suprintf("Max parts: %d, Start Offset: %" PRIi64 ", Usable = %" PRIi64 " bytes\n", DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart); for (i=0; i<DriveLayout->PartitionCount; i++) { SelectedDrive.nPartitions++; tmp[0] = 0; wchar_to_utf8_no_alloc(DriveLayout->PartitionEntry[i].Gpt.Name, tmp, sizeof(tmp)); suprintf("Partition %d:\r\n Type: %s\r\n Name: '%s'\n", i+1, GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionType), tmp); suprintf(" ID: %s\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %" PRIi64 ", Attributes: 0x%016" PRIX64 "\n", GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId), SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE), DriveLayout->PartitionEntry[i].PartitionLength, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / DiskGeometry->Geometry.BytesPerSector, DriveLayout->PartitionEntry[i].Gpt.Attributes); // Don't register the partitions that we don't care about destroying if ( (strcmp(tmp, "UEFI:NTFS") == 0) || (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MSFT_RESERVED_GUID)) || (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_SYSTEM_GUID)) ) --SelectedDrive.nPartitions; if ( (memcmp(&PARTITION_BASIC_DATA_GUID, &DriveLayout->PartitionEntry[i].Gpt.PartitionType, sizeof(GUID)) == 0) && (nWindowsVersion >= WINDOWS_VISTA) ) ret = TRUE; } break; default: SelectedDrive.PartitionType = PARTITION_STYLE_MBR; suprintf("Partition type: RAW\n"); break; } #if defined(__GNUC__) #pragma GCC diagnostic warning "-Warray-bounds" #endif safe_closehandle(hPhysical); return ret; }
/* * Returns the drive letters for all volumes located on the drive identified by DriveIndex, * as well as the drive type. This is used as base for the 2 function calls that follow. */ static BOOL _GetDriveLettersAndType(DWORD DriveIndex, char* drive_letters, UINT* drive_type) { DWORD size; BOOL r = FALSE; HANDLE hDrive = INVALID_HANDLE_VALUE; UINT _drive_type; int i = 0, drive_number; char *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ char logical_drive[] = "\\\\.\\#:"; if (drive_letters != NULL) drive_letters[0] = 0; if (drive_type != NULL) *drive_type = DRIVE_UNKNOWN; CheckDriveIndex(DriveIndex); // 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. size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)\n", size, sizeof(drives)); goto out; } r = TRUE; // Required to detect drives that don't have volumes assigned for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; *drive = (char)toupper((int)*drive); if (*drive < 'C') { continue; } // 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 _drive_type = GetDriveTypeA(drive); if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED)) continue; safe_sprintf(logical_drive, sizeof(logical_drive), "\\\\.\\%c:", drive[0]); hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { // uprintf("Warning: could not open drive %c: %s\n", drive[0], WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, logical_drive); safe_closehandle(hDrive); if (drive_number == DriveIndex) { r = TRUE; if (drive_letters != NULL) drive_letters[i++] = *drive; // The drive type should be the same for all volumes, so we can overwrite if (drive_type != NULL) *drive_type = _drive_type; } } out: if (drive_letters != NULL) drive_letters[i] = 0; return r; }