[core] report detected USB device speed

* Yes, it takes about all of the changes below, to basically add 3 extra characters in the log...
  That's Microsoft Windows for you, baby!
* Closes #314
This commit is contained in:
Pete Batard 2014-05-18 00:37:01 +01:00
parent 4026083da8
commit c85670f7b7
13 changed files with 847 additions and 471 deletions

View File

@ -194,6 +194,7 @@
<ClCompile Include="..\stdio.c" />
<ClCompile Include="..\stdlg.c" />
<ClCompile Include="..\syslinux.c" />
<ClCompile Include="..\usb.c" />
<ClCompile Include="..\vhd.c" />
</ItemGroup>
<ItemGroup>
@ -215,6 +216,7 @@
<ClInclude Include="..\license.h" />
<ClInclude Include="..\smart.h" />
<ClInclude Include="..\sys_types.h" />
<ClInclude Include="..\usb.h" />
</ItemGroup>
<ItemGroup>
<Manifest Include="..\common_controls_and_elevation.manifest" />

View File

@ -66,6 +66,9 @@
<ClCompile Include="..\smart.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\usb.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rufus.h">
@ -122,6 +125,9 @@
<ClInclude Include="..\drive.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\usb.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\res\rufus.ico">

View File

@ -45,5 +45,6 @@ SOURCES=rufus.c \
drive.c \
smart.c \
syslinux.c \
usb.c \
vhd.c \
rufus.rc

View File

@ -9,7 +9,7 @@ pkg_v_rc_0 = @echo " RC $@";
%_rc.o: %.rc ../res/localization/embedded.loc
$(pkg_v_rc)$(WINDRES) $(AM_RCFLAGS) -i $< -o $@
rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c usb.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \

View File

@ -47,10 +47,11 @@ am_rufus_OBJECTS = rufus-drive.$(OBJEXT) rufus-icon.$(OBJEXT) \
rufus-parser.$(OBJEXT) rufus-localization.$(OBJEXT) \
rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) rufus-dos.$(OBJEXT) \
rufus-dos_locale.$(OBJEXT) rufus-badblocks.$(OBJEXT) \
rufus-syslinux.$(OBJEXT) rufus-vhd.$(OBJEXT) \
rufus-format.$(OBJEXT) rufus-smart.$(OBJEXT) \
rufus-stdio.$(OBJEXT) rufus-stdfn.$(OBJEXT) \
rufus-stdlg.$(OBJEXT) rufus-rufus.$(OBJEXT)
rufus-syslinux.$(OBJEXT) rufus-usb.$(OBJEXT) \
rufus-vhd.$(OBJEXT) rufus-format.$(OBJEXT) \
rufus-smart.$(OBJEXT) rufus-stdio.$(OBJEXT) \
rufus-stdfn.$(OBJEXT) rufus-stdlg.$(OBJEXT) \
rufus-rufus.$(OBJEXT)
rufus_OBJECTS = $(am_rufus_OBJECTS)
rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \
syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
@ -186,7 +187,7 @@ SUBDIRS = ms-sys syslinux/libfat syslinux/libinstaller libcdio/iso9660 libcdio/u
pkg_v_rc = $(pkg_v_rc_$(V))
pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY))
pkg_v_rc_0 = @echo " RC $@";
rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c usb.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
@ -327,6 +328,14 @@ rufus-syslinux.obj: syslinux.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-syslinux.obj `if test -f 'syslinux.c'; then $(CYGPATH_W) 'syslinux.c'; else $(CYGPATH_W) '$(srcdir)/syslinux.c'; fi`
rufus-usb.o: usb.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-usb.o `test -f 'usb.c' || echo '$(srcdir)/'`usb.c
rufus-usb.obj: usb.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-usb.obj `if test -f 'usb.c'; then $(CYGPATH_W) 'usb.c'; else $(CYGPATH_W) '$(srcdir)/usb.c'; fi`
rufus-vhd.o: vhd.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-vhd.o `test -f 'vhd.c' || echo '$(srcdir)/'`vhd.c

View File

@ -65,6 +65,9 @@ const loc_parse parse_cmd[9] = {
{ 'a', LC_ATTRIBUTES, "s" }, // a "ra"
};
/* Hash table for reused translation commands */
static htab_table htab_loc = HTAB_EMPTY;
/* Globals */
int loc_line_nr;
struct list_head locale_list = {NULL, NULL};
@ -85,179 +88,13 @@ static void mtab_destroy(BOOL reinit)
}
}
/*
* Hash table functions - modified From glibc 2.3.2:
* [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
* [Knuth] The Art of Computer Programming, part 3 (6.4)
*/
typedef struct htab_entry {
uint32_t used;
char* str;
loc_cmd* dlg_cmd;
} htab_entry;
htab_entry* htab_table = NULL;
size_t htab_size, htab_filled;
/*
* For the used double hash method the table size has to be a prime. To
* correct the user given table size we need a prime test. This trivial
* algorithm is adequate because the code is called only during init and
* the number is likely to be small
*/
static uint32_t isprime(uint32_t number)
{
// no even number will be passed
uint32_t divider = 3;
while((divider * divider < number) && (number % divider != 0))
divider += 2;
return (number % divider != 0);
}
/*
* Before using the hash table we must allocate memory for it.
* We allocate one element more as the found prime number says.
* This is done for more effective indexing as explained in the
* comment for the hash function.
*/
static BOOL htab_create(uint32_t nel)
{
if (htab_table != NULL) {
return FALSE;
}
// Change nel to the first prime number not smaller as nel.
nel |= 1;
while(!isprime(nel))
nel += 2;
htab_size = nel;
htab_filled = 0;
// allocate memory and zero out.
htab_table = (htab_entry*)calloc(htab_size + 1, sizeof(htab_entry));
if (htab_table == NULL) {
uprintf("localization: could not allocate space for hash table\n");
return FALSE;
}
return TRUE;
}
/* After using the hash table it has to be destroyed. */
static void htab_destroy(void)
{
size_t i;
if (htab_table == NULL) {
return;
}
for (i=0; i<htab_size+1; i++) {
if (htab_table[i].used) {
safe_free(htab_table[i].str);
}
}
safe_free(htab_table);
}
/*
* This is the search function. It uses double hashing with open addressing.
* We use a trick to speed up the lookup. The table is created with one
* more element available. This enables us to use the index zero special.
* This index will never be used because we store the first hash index in
* the field used where zero means not used. Every other value means used.
* The used field can be used as a first fast comparison for equality of
* the stored and the parameter value. This helps to prevent unnecessary
* expensive calls of strcmp.
*/
static uint32_t htab_hash(char* str)
{
uint32_t hval, hval2;
uint32_t idx;
uint32_t r = 0;
int c;
char* sz = str;
if (str == NULL)
return 0;
// Compute main hash value using sdbm's algorithm (empirically
// shown to produce half the collisions as djb2's).
// See http://www.cse.yorku.ca/~oz/hash.html
while ((c = *sz++) != 0)
r = c + (r << 6) + (r << 16) - r;
if (r == 0)
++r;
// compute table hash: simply take the modulus
hval = r % htab_size;
if (hval == 0)
++hval;
// Try the first index
idx = hval;
if (htab_table[idx].used) {
if ( (htab_table[idx].used == hval)
&& (safe_strcmp(str, htab_table[idx].str) == 0) ) {
// existing hash
return idx;
}
// uprintf("hash collision ('%s' vs '%s')\n", str, htab_table[idx].str);
// Second hash function, as suggested in [Knuth]
hval2 = 1 + hval % (htab_size - 2);
do {
// Because size is prime this guarantees to step through all available indexes
if (idx <= hval2) {
idx = ((uint32_t)htab_size) + idx - hval2;
} else {
idx -= hval2;
}
// If we visited all entries leave the loop unsuccessfully
if (idx == hval) {
break;
}
// If entry is found use it.
if ( (htab_table[idx].used == hval)
&& (safe_strcmp(str, htab_table[idx].str) == 0) ) {
return idx;
}
}
while (htab_table[idx].used);
}
// Not found => New entry
// If the table is full return an error
if (htab_filled >= htab_size) {
uprintf("localization: hash table is full (%d entries)", htab_size);
return 0;
}
safe_free(htab_table[idx].str);
htab_table[idx].used = hval;
htab_table[idx].str = (char*) malloc(safe_strlen(str)+1);
if (htab_table[idx].str == NULL) {
uprintf("localization: could not duplicate string for hash table\n");
return 0;
}
memcpy(htab_table[idx].str, str, safe_strlen(str)+1);
++htab_filled;
return idx;
}
/*
* Add a localization command to a dialog/section
*/
void add_dialog_command(int index, loc_cmd* lcmd)
{
char str[128];
loc_cmd* htab_lcmd;
uint32_t i;
if ((lcmd == NULL) || (lcmd->txt[0] == NULL) || (index < 0) || (index >= ARRAYSIZE(loc_dlg))) {
uprintf("localization: invalid parameter for add_dialog_command\n");
@ -273,13 +110,14 @@ void add_dialog_command(int index, loc_cmd* lcmd)
str[0] = index + 0x30;
str[1] = lcmd->command + 0x30;
safe_strcpy(&str[2], sizeof(str)-2, lcmd->txt[0]);
i = htab_hash(str);
i = htab_hash(str, &htab_loc);
if (i != 0) {
if (htab_table[i].dlg_cmd != NULL) {
list_del(&(htab_table[i].dlg_cmd->list));
free_loc_cmd(htab_table[i].dlg_cmd);
htab_lcmd = (loc_cmd*)(htab_loc.table[i].data);
if (htab_lcmd != NULL) {
list_del(&(htab_lcmd->list));
free_loc_cmd(htab_lcmd);
}
htab_table[i].dlg_cmd = lcmd;
htab_loc.table[i].data = (void*)lcmd;
}
list_add(&lcmd->list, &loc_dlg[index].list);
}
@ -349,7 +187,7 @@ void _init_localization(BOOL reinit) {
list_init(&loc_dlg[i].list);
if (!reinit)
list_init(&locale_list);
htab_create(LOC_HTAB_SIZE);
htab_create(LOC_HTAB_SIZE, &htab_loc);
}
void _exit_localization(BOOL reinit) {
@ -360,7 +198,7 @@ void _exit_localization(BOOL reinit) {
}
free_dialog_list();
mtab_destroy(reinit);
htab_destroy();
htab_destroy(&htab_loc);
}
/*

View File

@ -28,8 +28,6 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <commctrl.h>
#include <setupapi.h>
#include <winioctl.h>
#include <shlobj.h>
#include <process.h>
@ -128,12 +126,12 @@ int dialog_showing = 0;
uint16_t rufus_version[4], embedded_sl_version[2];
char embedded_sl_version_str[2][12] = { "?.??", "?.??" };
RUFUS_UPDATE update = { {0,0,0,0}, {0,0}, NULL, NULL};
StrArray DriveID, DriveLabel;
extern char szStatusMessage[256];
static HANDLE format_thid = NULL;
static HWND hProgress = NULL, hBoot = NULL, hSelectISO = NULL;
static HICON hIconDisc, hIconDown, hIconUp, hIconLang;
static StrArray DriveID, DriveLabel;
static char szTimer[12] = "00:00:00";
static unsigned int timer;
static int64_t last_iso_blocking_status;
@ -609,267 +607,6 @@ static BOOL PopulateProperties(int ComboIndex)
return TRUE;
}
/*
* Refresh the list of USB devices
*/
static 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" };
const char* scsi_name = "SCSI";
const char* vhd_name = "Virtual Disk";
char letter_name[] = " (?:)";
BOOL found = FALSE, is_SCSI, is_UASP, is_VHD;
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, device_inst;
DWORD size, i, j, k, datatype, drive_index;
ULONG list_size[ARRAYSIZE(storage_name)], full_list_size;
HANDLE hDrive;
LONG maxwidth = 0;
RECT rect;
int s, score, drive_number;
char drive_letters[27], *devid, *devid_list = NULL, entry_msg[128];
char *label, *entry, buffer[MAX_PATH], str[sizeof("0000:0000")+1];
uint16_t vid, pid;
GUID _GUID_DEVINTERFACE_DISK = // only known to some...
{ 0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b} };
IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList));
StrArrayClear(&DriveID);
StrArrayClear(&DriveLabel);
GetClientRect(hDeviceList, &rect);
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());
return FALSE;
}
full_list_size = 0;
for (s=0; s<ARRAYSIZE(storage_name); s++) {
// Get a list of hardware IDs for all USB storage devices
// This will be used to retrieve the VID:PID of our devices
CM_Get_Device_ID_List_SizeA(&list_size[s], storage_name[s], CM_GETIDLIST_FILTER_SERVICE);
if (list_size[s] != 0)
full_list_size += list_size[s]-1; // remove extra NUL terminator
}
full_list_size += 1; // add extra NUL terminator
if (full_list_size < 2)
return FALSE;
devid_list = (char*)malloc(full_list_size);
if (devid_list == NULL) {
uprintf("Could not allocate Dev ID list\n");
return FALSE;
}
// Build a single list from all the storage enumerators we know of
for (s=0, i=0; s<ARRAYSIZE(storage_name); s++) {
if (list_size[s] > 1) {
CM_Get_Device_ID_ListA(storage_name[s], &devid_list[i], list_size[s], CM_GETIDLIST_FILTER_SERVICE);
// 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;
}
}
}
dev_info_data.cbSize = sizeof(dev_info_data);
for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
memset(buffer, 0, sizeof(buffer));
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;
memset(buffer, 0, sizeof(buffer));
vid = 0; pid = 0;
is_UASP = FALSE, is_VHD = FALSE;
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 (safe_strstr(buffer, vhd_name) != NULL) {
is_VHD = TRUE;
} else {
// Get the VID:PID 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...
for (devid = devid_list; *devid; devid += strlen(devid) + 1) {
if ( (CM_Locate_DevNodeA(&parent_inst, devid, 0) == 0)
&& (CM_Get_Child(&device_inst, parent_inst, 0) == 0)
&& (device_inst == dev_info_data.DevInst) ) {
BOOL post_backslash = FALSE;
// If we're not dealing with the USBSTOR part of our list, then this is an UASP device
is_UASP = ((((uintptr_t)devid)+2) >= ((uintptr_t)devid_list)+list_size[0]);
for (j=0, k=0; (j<strlen(devid))&&(k<2); j++) {
// The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\...
if (devid[j] == '\\')
post_backslash = TRUE;
if (!post_backslash)
continue;
if (devid[j] == '_') {
pid = (uint16_t)strtoul(&devid[j+1], NULL, 16);
// We could have used a vid_pid[] table, but keeping vid/pid separate is clearer
if (k++==0) vid = pid;
}
}
}
}
}
if (is_VHD) {
uprintf("Found VHD device '%s'\n", buffer);
} else {
if ((vid == 0) && (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!
continue;
}
safe_strcpy(str, sizeof(str), "????:????"); // Couldn't figure VID:PID
} else {
safe_sprintf(str, sizeof(str), "%04X:%04X", vid, pid);
}
uprintf("Found %s device '%s' (%s)\n", is_UASP?"UAS":"USB", buffer, str);
}
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, 0, 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) && (!is_VHD) && ((score = IsHDD(drive_index, vid, 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 {
// 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] == app_dir[0]) break;
}
// Repeat as we need to break the outside loop
if (drive_letters[k] == app_dir[0]) {
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));
safe_free(devid_list);
return TRUE;
}
/*
* Set up progress bar real estate allocation
*/

View File

@ -346,6 +346,7 @@ extern BOOL SetAutorun(const char* path);
extern char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_desc, DWORD options);
extern BOOL FileIO(BOOL save, char* path, char** buffer, DWORD* size);
extern unsigned char* GetResource(HMODULE module, char* name, char* type, const char* desc, DWORD* len, BOOL duplicate);
extern BOOL GetUSBDevices(DWORD devnum);
extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue);
extern LONG GetEntryWidth(HWND hDropDown, const char* entry);
extern DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog);
@ -378,14 +379,30 @@ static __inline void *_reallocf(void *ptr, size_t size)
return ret;
}
/* Hash tables */
typedef struct htab_entry {
uint32_t used;
char* str;
void* data;
} htab_entry;
typedef struct htab_table {
htab_entry *table;
uint32_t size;
uint32_t filled;
} htab_table;
#define HTAB_EMPTY {NULL, 0, 0}
extern BOOL htab_create(uint32_t nel, htab_table* htab);
extern void htab_destroy(htab_table* htab);
extern uint32_t htab_hash(char* str, htab_table* htab);
/* Basic String Array */
typedef struct {
char** String;
size_t Index; // Current array size
size_t Max; // Maximum array size
char** String;
uint32_t Index; // Current array size
uint32_t Max; // Maximum array size
} StrArray;
extern void StrArrayCreate(StrArray* arr, size_t initial_size);
extern void StrArrayAdd(StrArray* arr, const char* str);
extern void StrArrayCreate(StrArray* arr, uint32_t initial_size);
extern int32_t StrArrayAdd(StrArray* arr, const char* str);
extern void StrArrayClear(StrArray* arr);
extern void StrArrayDestroy(StrArray* arr);
#define IsStrArrayEmpty(arr) (arr.Index == 0)
@ -469,12 +486,3 @@ static __inline HMODULE GetLibraryHandle(char* szLibraryName) {
#define _RT_ICON MAKEINTRESOURCEA(3)
#define _RT_RCDATA MAKEINTRESOURCEA(10)
#define _RT_GROUP_ICON MAKEINTRESOURCEA((ULONG_PTR)(MAKEINTRESOURCEA(3) + 11))
/* The CM calls used in GetUSBDevices() - from MinGW's cfgmgr32.h header */
typedef DWORD CONFIGRET, DEVINST, *PDEVINST;
typedef CHAR *DEVINSTID_A;
#define CM_GETIDLIST_FILTER_SERVICE 2
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_List_SizeA(PULONG pulLen, PCSTR pszFilter, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_ListA(PCSTR pszFilter, PCHAR Buffer, ULONG BufferLen, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Locate_DevNodeA(PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Child(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);

View File

@ -32,7 +32,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 206, 329
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Rufus 1.4.8.467"
CAPTION "Rufus 1.4.8.468"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Start",IDC_START,94,291,50,14
@ -165,7 +165,7 @@ END
RTL_IDD_DIALOG DIALOGEX 12, 12, 206, 329
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL
CAPTION "Rufus 1.4.8.467"
CAPTION "Rufus 1.4.8.468"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Start",IDC_START,94,291,50,14
@ -427,8 +427,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,4,8,467
PRODUCTVERSION 1,4,8,467
FILEVERSION 1,4,8,468
PRODUCTVERSION 1,4,8,468
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -445,13 +445,13 @@ BEGIN
BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "1.4.8.467"
VALUE "FileVersion", "1.4.8.468"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2014 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "1.4.8.467"
VALUE "ProductVersion", "1.4.8.468"
END
END
BLOCK "VarFileInfo"

View File

@ -32,6 +32,174 @@
int nWindowsVersion = WINDOWS_UNDEFINED;
char WindowsVersionStr[128] = "Windows ";
/*
* Hash table functions - modified From glibc 2.3.2:
* [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
* [Knuth] The Art of Computer Programming, part 3 (6.4)
*/
/*
* For the used double hash method the table size has to be a prime. To
* correct the user given table size we need a prime test. This trivial
* algorithm is adequate because the code is called only during init and
* the number is likely to be small
*/
static uint32_t isprime(uint32_t number)
{
// no even number will be passed
uint32_t divider = 3;
while((divider * divider < number) && (number % divider != 0))
divider += 2;
return (number % divider != 0);
}
/*
* Before using the hash table we must allocate memory for it.
* We allocate one element more as the found prime number says.
* This is done for more effective indexing as explained in the
* comment for the hash function.
*/
BOOL htab_create(uint32_t nel, htab_table* htab)
{
if (htab == NULL) {
return FALSE;
}
if (htab->table != NULL) {
uprintf("warning: htab_create() was called with a non empty table");
return FALSE;
}
// Change nel to the first prime number not smaller as nel.
nel |= 1;
while(!isprime(nel))
nel += 2;
htab->size = nel;
htab->filled = 0;
// allocate memory and zero out.
htab->table = (htab_entry*)calloc(htab->size + 1, sizeof(htab_entry));
if (htab->table == NULL) {
uprintf("could not allocate space for hash table\n");
return FALSE;
}
return TRUE;
}
/* After using the hash table it has to be destroyed. */
void htab_destroy(htab_table* htab)
{
size_t i;
if ((htab == NULL) || (htab->table == NULL)) {
return;
}
for (i=0; i<htab->size+1; i++) {
if (htab->table[i].used) {
safe_free(htab->table[i].str);
}
}
htab->filled = 0; htab->size = 0;
safe_free(htab->table);
htab->table = NULL;
}
/*
* This is the search function. It uses double hashing with open addressing.
* We use a trick to speed up the lookup. The table is created with one
* more element available. This enables us to use the index zero special.
* This index will never be used because we store the first hash index in
* the field used where zero means not used. Every other value means used.
* The used field can be used as a first fast comparison for equality of
* the stored and the parameter value. This helps to prevent unnecessary
* expensive calls of strcmp.
*/
uint32_t htab_hash(char* str, htab_table* htab)
{
uint32_t hval, hval2;
uint32_t idx;
uint32_t r = 0;
int c;
char* sz = str;
if ((htab == NULL) || (htab->table == NULL) || (str == NULL)) {
return 0;
}
// Compute main hash value using sdbm's algorithm (empirically
// shown to produce half the collisions as djb2's).
// See http://www.cse.yorku.ca/~oz/hash.html
while ((c = *sz++) != 0)
r = c + (r << 6) + (r << 16) - r;
if (r == 0)
++r;
// compute table hash: simply take the modulus
hval = r % htab->size;
if (hval == 0)
++hval;
// Try the first index
idx = hval;
if (htab->table[idx].used) {
if ( (htab->table[idx].used == hval)
&& (safe_strcmp(str, htab->table[idx].str) == 0) ) {
// existing hash
return idx;
}
// uprintf("hash collision ('%s' vs '%s')\n", str, htab_table[idx].str);
// Second hash function, as suggested in [Knuth]
hval2 = 1 + hval % (htab->size - 2);
do {
// Because size is prime this guarantees to step through all available indexes
if (idx <= hval2) {
idx = ((uint32_t)htab->size) + idx - hval2;
} else {
idx -= hval2;
}
// If we visited all entries leave the loop unsuccessfully
if (idx == hval) {
break;
}
// If entry is found use it.
if ( (htab->table[idx].used == hval)
&& (safe_strcmp(str, htab->table[idx].str) == 0) ) {
return idx;
}
}
while (htab->table[idx].used);
}
// Not found => New entry
// If the table is full return an error
if (htab->filled >= htab->size) {
uprintf("hash table is full (%d entries)", htab->size);
return 0;
}
safe_free(htab->table[idx].str);
htab->table[idx].used = hval;
htab->table[idx].str = (char*) malloc(safe_strlen(str)+1);
if (htab->table[idx].str == NULL) {
uprintf("could not duplicate string for hash table\n");
return 0;
}
memcpy(htab->table[idx].str, str, safe_strlen(str)+1);
++htab->filled;
return idx;
}
BOOL is_x64(void)
{
BOOL ret = FALSE;
@ -151,7 +319,7 @@ void GetWindowsVersion(void)
/*
* String array manipulation
*/
void StrArrayCreate(StrArray* arr, size_t initial_size)
void StrArrayCreate(StrArray* arr, uint32_t initial_size)
{
if (arr == NULL) return;
arr->Max = initial_size; arr->Index = 0;
@ -160,11 +328,11 @@ void StrArrayCreate(StrArray* arr, size_t initial_size)
uprintf("Could not allocate string array\n");
}
void StrArrayAdd(StrArray* arr, const char* str)
int32_t StrArrayAdd(StrArray* arr, const char* str)
{
char** old_table;
if ((arr == NULL) || (arr->String == NULL))
return;
return -1;
if (arr->Index == arr->Max) {
arr->Max *= 2;
old_table = arr->String;
@ -172,13 +340,15 @@ void StrArrayAdd(StrArray* arr, const char* str)
if (arr->String == NULL) {
free(old_table);
uprintf("Could not reallocate string array\n");
return;
return -1;
}
}
arr->String[arr->Index] = safe_strdup(str);
if (arr->String[arr->Index++] == NULL) {
if (arr->String[arr->Index] == NULL) {
uprintf("Could not store string in array\n");
return -1;
}
return arr->Index++;
}
void StrArrayClear(StrArray* arr)

View File

@ -164,14 +164,13 @@ char* SizeToHumanReadable(uint64_t size, BOOL log, BOOL fake_units)
{
int suffix;
static char str_size[32];
char dir[4];
const char* dir = ((right_to_left_mode)&&(!log))?RIGHT_TO_LEFT_MARK:"";
double hr_size = (double)size;
double t;
uint16_t i_size;
char **_msg_table = log?default_msg_table:msg_table;
const double divider = fake_units?1000.0:1024.0;
static_sprintf(dir, right_to_left_mode?RIGHT_TO_LEFT_MARK:"");
for (suffix=0; suffix<MAX_SIZE_SUFFIXES-1; suffix++) {
if (hr_size < divider)
break;

435
src/usb.c Normal file
View File

@ -0,0 +1,435 @@
/*
* Rufus: The Reliable USB Formatting Utility
* USB device listing
* Copyright © 2014 Pete Batard <pete@akeo.ie>
*
* 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/>.
*/
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <commctrl.h>
#include <setupapi.h>
#include "msapi_utf8.h"
#include "rufus.h"
#include "drive.h"
#include "resource.h"
#include "localization.h"
#include "usb.h"
extern StrArray DriveID, DriveLabel;
extern BOOL enable_HDDs, use_fake_units;
/*
* 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;
USB_NODE_CONNECTION_INFORMATION_EX conn_info;
USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
if ((parent_path == NULL) || (device_id == NULL) || (props == NULL) || (props->port == 0)) {
return;
}
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 device '%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;
}
}
out:
safe_closehandle(handle);
}
/*
* 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" };
const char* scsi_name = "SCSI";
const char* vhd_name = "Virtual Disk";
const char* usb_speed_name[USB_SPEED_MAX] = { "", " 1.0", " 1.1", " 2.0", " 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[] = " (?:)";
BOOL 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, device_inst;
DWORD size, i, j, k, datatype, drive_index, port;
ULONG list_size[ARRAYSIZE(storage_name)], full_list_size;
HANDLE hDrive;
LONG maxwidth = 0;
RECT rect;
int s, score, drive_number;
char drive_letters[27], *devid, *devid_list = NULL, entry_msg[128];
char *label, *entry, device_id[MAX_PATH], buffer[MAX_PATH], str[128];
usb_device_props props;
PF_INIT(CM_Get_DevNode_Registry_PropertyA, Cfgmgr32);
IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList));
StrArrayClear(&DriveID);
StrArrayClear(&DriveLabel);
StrArrayCreate(&dev_if_path, 128);
GetClientRect(hDeviceList, &rect);
// 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(257, &htab_devid)) {
dev_info_data.cbSize = sizeof(dev_info_data);
for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
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 ID 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, sizeof(device_id), 0) == CR_SUCCESS)) {
// Lookup port in case SPDRP_ADDRESS doesn't work (which is the case of UASP)
port = 0;
size = sizeof(port);
if (pfCM_Get_DevNode_Registry_PropertyA != NULL)
pfCM_Get_DevNode_Registry_PropertyA(device_inst, CM_DRP_ADDRESS, NULL, (PVOID)&port, &size, 0);
if ((k = htab_hash(device_id, &htab_devid)) != 0) {
htab_devid.table[k].data = (void*)(uintptr_t)((port<<16)|s);
}
while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) {
port = 0; size = sizeof(port);
if (pfCM_Get_DevNode_Registry_PropertyA != NULL)
pfCM_Get_DevNode_Registry_PropertyA(device_inst, CM_DRP_ADDRESS, NULL, (PVOID)&port, &size, 0);
device_id[0] = 0;
if (CM_Get_Device_IDA(device_inst, device_id, sizeof(device_id), 0) == CR_SUCCESS) {
if ((k = htab_hash(device_id, &htab_devid)) != 0) {
// store both string index and fallback port in data
htab_devid.table[k].data = (void*)(uintptr_t)((port<<16)|s);
}
}
}
}
}
}
free(devint_detail_data);
}
}
}
SetupDiDestroyDeviceInfoList(dev_info);
}
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());
return FALSE;
}
full_list_size = 0;
for (s=0; s<ARRAYSIZE(storage_name); s++) {
// Get a list of hardware IDs for all USB storage devices
// This will be used to retrieve the VID:PID of our devices
CM_Get_Device_ID_List_SizeA(&list_size[s], storage_name[s], CM_GETIDLIST_FILTER_SERVICE);
if (list_size[s] != 0)
full_list_size += list_size[s]-1; // remove extra NUL terminator
}
full_list_size += 1; // add extra NUL terminator
if (full_list_size < 2)
return FALSE;
devid_list = (char*)malloc(full_list_size);
if (devid_list == NULL) {
uprintf("Could not allocate Dev ID list\n");
return FALSE;
}
// Build a single list from all the storage enumerators we know of
for (s=0, i=0; s<ARRAYSIZE(storage_name); s++) {
if (list_size[s] > 1) {
CM_Get_Device_ID_ListA(storage_name[s], &devid_list[i], list_size[s], CM_GETIDLIST_FILTER_SERVICE);
// 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;
}
}
}
dev_info_data.cbSize = sizeof(dev_info_data);
for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
memset(buffer, 0, sizeof(buffer));
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;
memset(buffer, 0, sizeof(buffer));
memset(&props, 0, sizeof(props));
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 (safe_strstr(buffer, vhd_name) != NULL) {
props.is_VHD = TRUE;
} else {
// Get the VID:PID 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...
for (devid = devid_list; *devid; devid += strlen(devid) + 1) {
if ( (CM_Locate_DevNodeA(&parent_inst, devid, 0) == CR_SUCCESS)
&& (CM_Get_Child(&device_inst, parent_inst, 0) == CR_SUCCESS)
&& (device_inst == dev_info_data.DevInst) ) {
BOOL post_backslash = FALSE;
// If we're not dealing with the USBSTOR part of our list, then this is an UASP device
props.is_UASP = ((((uintptr_t)devid)+2) >= ((uintptr_t)devid_list)+list_size[0]);
// Now get the port number of the device, and its Device ID, which we need to populate the properties
if ( (htab_devid.table != NULL) && (SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data,
SPDRP_ADDRESS, &datatype, (BYTE*)&props.port, sizeof(props.port), &size)) &&
(CM_Get_Device_IDA(parent_inst, device_id, sizeof(device_id), 0) == CR_SUCCESS) ) {
j = htab_hash(device_id, &htab_devid);
if (props.port == 0) // UAS devices will return port 0 from SPDRP_ADDRESS
props.port = ((uint32_t)htab_devid.table[j].data)>>16;
if (j > 0) {
GetUSBProperties(dev_if_path.String[((uint32_t)htab_devid.table[j].data)&0xFFFF], device_id, &props);
}
}
// If the previous calls didn't succeed in getting the VID:PID, try from the device_id
// This will be the case for UASP devices for instance
if ((props.vid == 0) && (props.pid == 0)) {
for (j=0, k=0; (j<strlen(devid))&&(k<2); j++) {
// The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\...
if (devid[j] == '\\')
post_backslash = TRUE;
if (!post_backslash)
continue;
if (devid[j] == '_') {
props.pid = (uint16_t)strtoul(&devid[j+1], NULL, 16);
if (k++==0)
props.vid = props.pid;
}
}
}
}
}
}
if (props.is_VHD) {
uprintf("Found VHD device '%s'\n", 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!
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 device '%s' (%s)\n", props.is_UASP?"UAS":"USB", usb_speed_name[props.speed], buffer, str);
}
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, 0, 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 {
// 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] == app_dir[0]) break;
}
// Repeat as we need to break the outside loop
if (drive_letters[k] == app_dir[0]) {
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));
safe_free(devid_list);
StrArrayDestroy(&dev_if_path);
htab_destroy(&htab_devid);
return TRUE;
}

171
src/usb.h Normal file
View File

@ -0,0 +1,171 @@
/*
* Rufus: The Reliable USB Formatting Utility
* USB device listing
* Copyright © 2014 Pete Batard <pete@akeo.ie>
*
* 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/>.
*/
#include <windows.h>
#define USB_SPEED_UNKNOWN 0
#define USB_SPEED_LOW 1
#define USB_SPEED_FULL 2
#define USB_SPEED_HIGH 3
#define USB_SPEED_SUPER_OR_LATER 4
#define USB_SPEED_MAX 5
/* List of the properties we are interested in */
typedef struct usb_device_props {
uint32_t vid;
uint32_t pid;
uint32_t speed;
uint32_t port;
BOOL is_UASP;
BOOL is_VHD;
} usb_device_props;
/*
* Windows DDK API definitions. Most of it copied from MinGW's includes
*/
typedef DWORD DEVNODE, DEVINST;
typedef DEVNODE *PDEVNODE, *PDEVINST;
typedef DWORD RETURN_TYPE;
typedef RETURN_TYPE CONFIGRET;
typedef CHAR *DEVINSTID_A;
#define CR_SUCCESS 0x00000000
#define CR_NO_SUCH_DEVNODE 0x0000000D
#define CM_GETIDLIST_FILTER_SERVICE 2
#define CM_DRP_ADDRESS 0x0000001D
#ifndef METHOD_BUFFERED
#define METHOD_BUFFERED 0
#endif
#ifndef FILE_ANY_ACCESS
#define FILE_ANY_ACCESS 0x00000000
#endif
#ifndef FILE_DEVICE_UNKNOWN
#define FILE_DEVICE_UNKNOWN 0x00000022
#endif
#ifndef FILE_DEVICE_USB
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
#endif
#ifndef CTL_CODE
#define CTL_CODE(DeviceType, Function, Method, Access)( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#endif
typedef enum USB_CONNECTION_STATUS {
NoDeviceConnected,
DeviceConnected,
DeviceFailedEnumeration,
DeviceGeneralFailure,
DeviceCausedOvercurrent,
DeviceNotEnoughPower,
DeviceNotEnoughBandwidth,
DeviceHubNestedTooDeeply,
DeviceInLegacyHub
} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS;
typedef enum USB_HUB_NODE {
UsbHub,
UsbMIParent
} USB_HUB_NODE;
/* Cfgmgr32.dll interface */
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_IDA(DEVINST dnDevInst, PCSTR Buffer, ULONG BufferLen, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_List_SizeA(PULONG pulLen, PCSTR pszFilter, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_ListA(PCSTR pszFilter, PCHAR Buffer, ULONG BufferLen, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Locate_DevNodeA(PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Child(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Sibling(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
// This last one is unknown from MinGW32 and needs to be fetched from the DLL
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_DevNode_Registry_PropertyA, (DEVINST, ULONG, PULONG, PVOID, PULONG, ULONG));
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS)
/* Most of the structures below need to be packed */
#pragma pack(push, 1)
typedef struct _USB_DEVICE_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
USHORT bcdUSB;
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize0;
USHORT idVendor;
USHORT idProduct;
USHORT bcdDevice;
UCHAR iManufacturer;
UCHAR iProduct;
UCHAR iSerialNumber;
UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
typedef struct USB_NODE_CONNECTION_INFORMATION_EX {
ULONG ConnectionIndex;
USB_DEVICE_DESCRIPTOR DeviceDescriptor;
UCHAR CurrentConfigurationValue;
UCHAR Speed;
BOOLEAN DeviceIsHub;
USHORT DeviceAddress;
ULONG NumberOfOpenPipes;
USB_CONNECTION_STATUS ConnectionStatus;
// USB_PIPE_INFO PipeList[0];
} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX;
typedef union _USB_PROTOCOLS {
ULONG ul;
struct {
ULONG Usb110:1;
ULONG Usb200:1;
ULONG Usb300:1;
ULONG ReservedMBZ:29;
};
} USB_PROTOCOLS, *PUSB_PROTOCOLS;
typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS {
ULONG ul;
struct {
ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1;
ULONG DeviceIsSuperSpeedCapableOrHigher:1;
ULONG ReservedMBZ:30;
};
} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS;
typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
ULONG ConnectionIndex;
ULONG Length;
USB_PROTOCOLS SupportedUsbProtocols;
USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags;
} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
#pragma pack(pop)
const GUID _GUID_DEVINTERFACE_DISK =
{ 0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b} };
const GUID _GUID_DEVINTERFACE_USB_HUB =
{ 0xf18a0e88L, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} };
#define DEVID_HTAB_SIZE 257