diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj index c451b956..ed4d73b1 100644 --- a/src/.msvc/rufus.vcxproj +++ b/src/.msvc/rufus.vcxproj @@ -194,6 +194,7 @@ + @@ -215,6 +216,7 @@ + diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters index e5ced23c..9ed84c82 100644 --- a/src/.msvc/rufus.vcxproj.filters +++ b/src/.msvc/rufus.vcxproj.filters @@ -66,6 +66,9 @@ Source Files + + Source Files + @@ -122,6 +125,9 @@ Header Files + + Header Files + diff --git a/src/.msvc/rufus_sources b/src/.msvc/rufus_sources index 38385424..b88e38c3 100644 --- a/src/.msvc/rufus_sources +++ b/src/.msvc/rufus_sources @@ -45,5 +45,6 @@ SOURCES=rufus.c \ drive.c \ smart.c \ syslinux.c \ + usb.c \ vhd.c \ rufus.rc diff --git a/src/Makefile.am b/src/Makefile.am index 97426851..914dd9ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/Makefile.in b/src/Makefile.in index cabe4936..e7dbe8d9 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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 diff --git a/src/localization.c b/src/localization.c index 44c08024..2cae1dcd 100644 --- a/src/localization.c +++ b/src/localization.c @@ -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 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); } /* diff --git a/src/rufus.c b/src/rufus.c index 86ff8b5d..e7d624f2 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include @@ -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 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 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; itable != 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; isize+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) diff --git a/src/stdio.c b/src/stdio.c index f987a6e8..7dcbed9d 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -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 + * + * 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 . + */ + +/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */ +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 + * + * 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 . + */ + +#include + +#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