/* * Convert a windows error to human readable string * uses retval as errorcode, or, if 0, use GetLastError() */ const char *WindowsErrorString(void) { static char err_string[256] = { 0 }; DWORD size; DWORD errcode, format_error; errcode = GetLastError(); static_sprintf(err_string, "[0x%08lX] ", errcode); size = FormatMessageU(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, HRESULT_CODE(errcode), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[strlen(err_string)], sizeof(err_string) - (DWORD)strlen(err_string), NULL); if (size == 0) { format_error = GetLastError(); if ((format_error) && (format_error != 0x13D)) // 0x13D, decode error, is returned for unknown codes static_sprintf(err_string, "Windows error code 0x%08lX (FormatMessage error code 0x%08lX)", errcode, format_error); else static_sprintf(err_string, "Unknown error 0x%08lX", errcode); } SetLastError(errcode); // Make sure we don't change the errorcode on exit return err_string; }
// Convert a size to human readable char* SizeToHumanReadable(uint64_t size, BOOL copy_to_log, BOOL fake_units) { int suffix; static char str_size[32]; const char* dir = ((right_to_left_mode)&&(!copy_to_log))?RIGHT_TO_LEFT_MARK:""; double hr_size = (double)size; double t; uint16_t i_size; char **_msg_table = copy_to_log?default_msg_table:msg_table; const double divider = fake_units?1000.0:1024.0; for (suffix=0; suffix<MAX_SIZE_SUFFIXES-1; suffix++) { if (hr_size < divider) break; hr_size /= divider; } if (suffix == 0) { static_sprintf(str_size, "%s%d%s %s", dir, (int)hr_size, dir, _msg_table[MSG_020-MSG_000]); } else if (fake_units) { if (hr_size < 8) { static_sprintf(str_size, (fabs((hr_size*10.0)-(floor(hr_size + 0.5)*10.0)) < 0.5)?"%0.0f%s":"%0.1f%s", hr_size, _msg_table[MSG_020+suffix-MSG_000]); } else { t = (double)upo2((uint16_t)hr_size); i_size = (uint16_t)((fabs(1.0f-(hr_size / t)) < 0.05f)?t:hr_size); static_sprintf(str_size, "%s%d%s%s", dir, i_size, dir, _msg_table[MSG_020+suffix-MSG_000]); } } else { static_sprintf(str_size, (hr_size * 10.0 - (floor(hr_size) * 10.0)) < 0.5? "%s%0.0f%s %s":"%s%0.1f%s %s", dir, hr_size, dir, _msg_table[MSG_020+suffix-MSG_000]); } return str_size; }
/* * Refresh the list of USB devices */ BOOL GetUSBDevices(DWORD devnum) { // The first two are standard Microsoft drivers (including the Windows 8 UASP one). // The rest are the vendor UASP drivers I know of so far - list may be incomplete! const char* storage_name[] = { "USBSTOR", "UASPSTOR", "VUSBSTOR", "ETRONSTOR", "ASUSSTPT" }; const char* scsi_name = "SCSI"; const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0" }; // Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path htab_table htab_devid = HTAB_EMPTY; StrArray dev_if_path; char letter_name[] = " (?:)"; char uefi_togo_check[] = "?:\\EFI\\Rufus\\ntfs_x64.efi"; BOOL r = FALSE, found = FALSE, is_SCSI; HDEVINFO dev_info = NULL; SP_DEVINFO_DATA dev_info_data; SP_DEVICE_INTERFACE_DATA devint_data; PSP_DEVICE_INTERFACE_DETAIL_DATA_A devint_detail_data; DEVINST parent_inst, grandparent_inst, device_inst; DWORD size, i, j, k, l, datatype, drive_index; ULONG list_size[ARRAYSIZE(storage_name)] = { 0 }, list_start[ARRAYSIZE(storage_name)] = { 0 }, full_list_size, ulFlags; HANDLE hDrive; LONG maxwidth = 0; int s, score, drive_number; char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128]; char *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str; usb_device_props props; IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList)); StrArrayClear(&DriveID); StrArrayClear(&DriveLabel); StrArrayCreate(&dev_if_path, 128); // Add a dummy for string index zero, as this is what non matching hashes will point to StrArrayAdd(&dev_if_path, ""); device_id = (char*)malloc(MAX_PATH); if (device_id == NULL) goto out; // Build a hash table associating a CM Device ID of an USB device with the SetupDI Device Interface Path // of its parent hub - this is needed to retrieve the device speed dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info != INVALID_HANDLE_VALUE) { if (htab_create(DEVID_HTAB_SIZE, &htab_devid)) { dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { if (usb_debug) uprintf("Processing Hub %d:", i + 1); devint_detail_data = NULL; devint_data.cbSize = sizeof(devint_data); // Only care about the first interface (MemberIndex 0) if ( (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_USB_HUB, 0, &devint_data)) && (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && ((devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size)) != NULL) ) { devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { // Find the Device IDs for all the children of this hub if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { device_id[0] = 0; s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath); if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } if (usb_debug) uprintf(" Found ID[%03d]: %s", k, device_id); while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { device_id[0] = 0; if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) { if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } if (usb_debug) uprintf(" Found ID[%03d]: %s", k, device_id); } } } } } free(devint_detail_data); } } } SetupDiDestroyDeviceInfoList(dev_info); } free(device_id); // Build a single list of Device IDs from all the storage enumerators we know of full_list_size = 0; ulFlags = CM_GETIDLIST_FILTER_SERVICE; if (nWindowsVersion >= WINDOWS_7) ulFlags |= CM_GETIDLIST_FILTER_PRESENT; for (s=0; s<ARRAYSIZE(storage_name); s++) { // Get a list of device IDs for all USB storage devices // This will be used to find if a device is UASP if (CM_Get_Device_ID_List_SizeA(&list_size[s], storage_name[s], ulFlags) != CR_SUCCESS) list_size[s] = 0; if (list_size[s] != 0) full_list_size += list_size[s]-1; // remove extra NUL terminator } devid_list = NULL; if (full_list_size != 0) { full_list_size += 1; // add extra NUL terminator devid_list = (char*)malloc(full_list_size); if (devid_list == NULL) { uprintf("Could not allocate Device ID list\n"); return FALSE; } for (s=0, i=0; s<ARRAYSIZE(storage_name); s++) { list_start[s] = i; if (list_size[s] > 1) { if (CM_Get_Device_ID_ListA(storage_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS) continue; if (usb_debug) { uprintf("Processing IDs belonging to %s:", storage_name[s]); for (device_id = &devid_list[i]; *device_id != 0; device_id += strlen(device_id) + 1) uprintf(" %s", device_id); } // The list_size is sometimes larger than required thus we need to find the real end for (i += list_size[s]; i > 2; i--) { if ((devid_list[i-2] != '\0') && (devid_list[i-1] == '\0') && (devid_list[i] == '\0')) break; } } } } // Now use SetupDi to enumerate all our storage devices dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info == INVALID_HANDLE_VALUE) { uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString()); goto out; } dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { memset(buffer, 0, sizeof(buffer)); method_str = ""; if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME, &datatype, (LPBYTE)buffer, sizeof(buffer), &size)) { uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString()); continue; } // UASP drives are listed under SCSI (along with regular SYSTEM drives => "DANGER, WILL ROBINSON!!!") is_SCSI = (safe_stricmp(buffer, scsi_name) == 0); if ((safe_stricmp(buffer, storage_name[0]) != 0) && (!is_SCSI)) continue; // We can't use the friendly name to find if a drive is a VHD, as friendly name string gets translated // according to your locale, so we poke the Hardware ID memset(&props, 0, sizeof(props)); memset(buffer, 0, sizeof(buffer)); props.is_VHD = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_HARDWAREID, &datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsVHD(buffer); if (usb_debug) uprintf("Processing Device: '%s'", buffer); memset(buffer, 0, sizeof(buffer)); if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_FRIENDLYNAME, &datatype, (LPBYTE)buffer, sizeof(buffer), &size)) { uprintf("SetupDiGetDeviceRegistryProperty (Friendly Name) failed: %s\n", WindowsErrorString()); // We can afford a failure on this call - just replace the name with "USB Storage Device (Generic)" safe_strcpy(buffer, sizeof(buffer), lmprintf(MSG_045)); } else if ((!props.is_VHD) && (devid_list != NULL)) { // Get the properties of the device. We could avoid doing this lookup every time by keeping // a lookup table, but there shouldn't be that many USB storage devices connected... // NB: Each of these Device IDs have an _only_ child, from which we get the Device Instance match. for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) { if ( (CM_Locate_DevNodeA(&parent_inst, device_id, 0) == CR_SUCCESS) && (CM_Get_Child(&device_inst, parent_inst, 0) == CR_SUCCESS) && (device_inst == dev_info_data.DevInst) ) { // If we're not dealing with the USBSTOR part of our list, then this is an UASP device props.is_UASP = ((((uintptr_t)device_id)+2) >= ((uintptr_t)devid_list)+list_start[1]); // Now get the properties of the device, and its Device ID, which we need to populate the properties j = htab_hash(device_id, &htab_devid); if (usb_debug) uprintf(" Matched with ID[%03d]: %s", j, device_id); // If the hash didn't match a populated string in dev_if_path[] (htab_devid.table[j].data > 0), // we might have an extra vendor driver in between (e.g. "ASUS USB 3.0 Boost Storage Driver" // for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods) // so try to see if we can match the grandparent. if ( ((uint32_t)htab_devid.table[j].data == 0) && (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS) && (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) { device_id = str; method_str = "[GP]"; j = htab_hash(device_id, &htab_devid); if (usb_debug) uprintf(" Matched with (GP) ID[%03d]: %s", j, device_id); } if ((uint32_t)htab_devid.table[j].data > 0) GetUSBProperties(dev_if_path.String[(uint32_t)htab_devid.table[j].data], device_id, &props); if (usb_debug) uprintf(" Props VID:PID = %04X:%04X", props.vid, props.pid); // If previous calls still didn't succeed, try reading the VID:PID from the device_id if ((props.vid == 0) && (props.pid == 0)) { BOOL post_backslash = FALSE; method_str = "[ID]"; for (j=0, k=0; (j<strlen(device_id))&&(k<2); j++) { // The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\... if (device_id[j] == '\\') post_backslash = TRUE; if (!post_backslash) continue; if (device_id[j] == '_') { props.pid = (uint16_t)strtoul(&device_id[j+1], NULL, 16); if (k++==0) props.vid = props.pid; } } } } } } if (props.is_VHD) { uprintf("Found VHD device '%s'", buffer); } else { if ((props.vid == 0) && (props.pid == 0)) { if (is_SCSI) { // If we have an SCSI drive and couldn't get a VID:PID, we are most likely // dealing with a system drive => eliminate it! if (usb_debug) uprintf(" Non USB => Eliminated"); continue; } safe_strcpy(str, sizeof(str), "????:????"); // Couldn't figure VID:PID } else { static_sprintf(str, "%04X:%04X", props.vid, props.pid); } if (props.speed >= USB_SPEED_MAX) props.speed = 0; uprintf("Found %s%s%s device '%s' (%s) %s\n", props.is_UASP?"UAS (":"", usb_speed_name[props.speed], props.is_UASP?")":"", buffer, str, method_str); if (props.is_LowerSpeed) uprintf("NOTE: This device is an USB 3.0 device operating at lower speed..."); } devint_data.cbSize = sizeof(devint_data); hDrive = INVALID_HANDLE_VALUE; devint_detail_data = NULL; for (j=0; ;j++) { safe_closehandle(hDrive); safe_free(devint_detail_data); if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_DISK, j, &devint_data)) { if(GetLastError() != ERROR_NO_MORE_ITEMS) { uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString()); } else { uprintf("A device was eliminated because it didn't report itself as a disk\n"); } break; } if (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size); if (devint_detail_data == NULL) { uprintf("Unable to allocate data for SP_DEVICE_INTERFACE_DETAIL_DATA\n"); continue; } devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); } else { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) failed: %s\n", WindowsErrorString()); continue; } } if (devint_detail_data == NULL) { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) - no data was allocated\n"); continue; } if(!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { uprintf("SetupDiGetDeviceInterfaceDetail (actual) failed: %s\n", WindowsErrorString()); continue; } hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDrive == INVALID_HANDLE_VALUE) { uprintf("Could not open '%s': %s\n", devint_detail_data->DevicePath, WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, devint_detail_data->DevicePath); if (drive_number < 0) continue; drive_index = drive_number + DRIVE_INDEX_MIN; if (!IsMediaPresent(drive_index)) { uprintf("Device eliminated because it appears to contain no media\n"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } if (GetDriveLabel(drive_index, drive_letters, &label)) { if ((!enable_HDDs) && (!props.is_VHD) && ((score = IsHDD(drive_index, (uint16_t)props.vid, (uint16_t)props.pid, buffer)) > 0)) { uprintf("Device eliminated because it was detected as an USB Hard Drive (score %d > 0)\n", score); uprintf("If this device is not an USB Hard Drive, please e-mail the author of this application\n"); uprintf("NOTE: You can enable the listing of USB Hard Drives in 'Advanced Options' (after clicking the white triangle)"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } // The empty string is returned for drives that don't have any volumes assigned if (drive_letters[0] == 0) { entry = lmprintf(MSG_046, label, drive_number, SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units)); } else { // Find the UEFI:TOGO partition(s) (and eliminate them form our listing) for (k=0; drive_letters[k]; k++) { uefi_togo_check[0] = drive_letters[k]; if (PathFileExistsA(uefi_togo_check)) { for (l=k; drive_letters[l]; l++) drive_letters[l] = drive_letters[l+1]; k--; } } // We have multiple volumes assigned to the same device (multiple partitions) // If that is the case, use "Multiple Volumes" instead of the label safe_strcpy(entry_msg, sizeof(entry_msg), ((drive_letters[0] != 0) && (drive_letters[1] != 0))? lmprintf(MSG_047):label); for (k=0; drive_letters[k]; k++) { // Append all the drive letters we detected letter_name[2] = drive_letters[k]; if (right_to_left_mode) safe_strcat(entry_msg, sizeof(entry_msg), RIGHT_TO_LEFT_MARK); safe_strcat(entry_msg, sizeof(entry_msg), letter_name); if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) break; } // Repeat as we need to break the outside loop if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) { uprintf("Removing %c: from the list: This is the disk from which " APPLICATION_NAME " is running!\n", app_dir[0]); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } safe_sprintf(&entry_msg[strlen(entry_msg)], sizeof(entry_msg) - strlen(entry_msg), "%s [%s]", (right_to_left_mode)?RIGHT_TO_LEFT_MARK:"", SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units)); entry = entry_msg; } // Must ensure that the combo box is UNSORTED for indexes to be the same StrArrayAdd(&DriveID, buffer); StrArrayAdd(&DriveLabel, label); IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index)); maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry)); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } } } SetupDiDestroyDeviceInfoList(dev_info); // Adjust the Dropdown width to the maximum text size SendMessage(hDeviceList, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0); if (devnum >= DRIVE_INDEX_MIN) { for (i=0; i<ComboBox_GetCount(hDeviceList); i++) { if ((DWORD)ComboBox_GetItemData(hDeviceList, i) == devnum) { found = TRUE; break; } } } if (!found) i = 0; IGNORE_RETVAL(ComboBox_SetCurSel(hDeviceList, i)); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_DEVICE, 0); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_FILESYSTEM, ComboBox_GetCurSel(hFileSystem)); r = TRUE; out: // Set 'Start' as the selected button, so that tab selection works SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDC_START), TRUE); safe_free(devid_list); StrArrayDestroy(&dev_if_path); htab_destroy(&htab_devid); return r; }
// Returns 0 on success, nonzero on error static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; BOOL s, is_syslinux_cfg, is_old_c32[NB_OLD_C32], is_symlink; int i_length, r = 1; char tmp[128], psz_fullpath[1024], *psz_basename; const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)]; unsigned char buf[ISO_BLOCKSIZE]; CdioListNode_t* p_entnode; iso9660_stat_t *p_statbuf; CdioList_t* p_entlist; size_t i, j, nul_pos; lsn_t lsn; int64_t i_file_length; if ((p_iso == NULL) || (psz_path == NULL)) return 1; i_length = _snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path); if (i_length < 0) return 1; psz_basename = &psz_fullpath[i_length]; p_entlist = iso9660_ifs_readdir(p_iso, psz_path); if (!p_entlist) { uprintf("Could not access directory %s\n", psz_path); return 1; } _CDIO_LIST_FOREACH(p_entnode, p_entlist) { if (FormatStatus) goto out; p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode); // Eliminate . and .. entries if ( (strcmp(p_statbuf->filename, ".") == 0) || (strcmp(p_statbuf->filename, "..") == 0) ) continue; // Rock Ridge requires an exception is_symlink = FALSE; if ((p_statbuf->rr.b3_rock == yep) && enable_rockridge) { safe_strcpy(psz_basename, sizeof(psz_fullpath)-i_length-1, p_statbuf->filename); if (safe_strlen(p_statbuf->filename) > 64) iso_report.has_long_filename = TRUE; // libcdio has a memleak for Rock Ridge symlinks. It doesn't look like there's an easy fix there as // a generic list that's unaware of RR extensions is being used, so we prevent that memleak ourselves is_symlink = (p_statbuf->rr.psz_symlink != NULL); if (is_symlink) iso_report.has_symlinks = TRUE; if (scan_only) safe_free(p_statbuf->rr.psz_symlink); } else { iso9660_name_translate_ext(p_statbuf->filename, psz_basename, i_joliet_level); } if (p_statbuf->type == _STAT_DIR) { if (!scan_only) _mkdirU(psz_fullpath); if (iso_extract_files(p_iso, psz_iso_name)) goto out; } else { i_file_length = p_statbuf->size; if (check_iso_props(psz_path, &is_syslinux_cfg, is_old_c32, i_file_length, psz_basename, psz_fullpath)) { continue; } // Replace slashes with backslashes and append the size to the path for UI display nul_pos = safe_strlen(psz_fullpath); for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '/') psz_fullpath[i] = '\\'; safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE)); uprintf("Extracting: %s\n", psz_fullpath); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE)); SetWindowTextU(hISOFileName, psz_fullpath); // ISO9660 cannot handle backslashes for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '\\') psz_fullpath[i] = '/'; psz_fullpath[nul_pos] = 0; for (i=0; i<NB_OLD_C32; i++) { if (is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileA(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version\n"); break; } uprintf(" Could not replace file: %s\n", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; if (sanitize_filename(psz_fullpath)) uprintf(" File name sanitized to '%s'\n", psz_fullpath); if (is_symlink) { if (i_file_length == 0) uprintf(" Ignoring Rock Ridge symbolic link to '%s'\n", p_statbuf->rr.psz_symlink); safe_free(p_statbuf->rr.psz_symlink); } file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s\n", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_fullpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else for (i=0; i_file_length>0; i++) { if (FormatStatus) goto out; memset(buf, 0, ISO_BLOCKSIZE); lsn = p_statbuf->lsn + (lsn_t)i; if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) { uprintf(" Error reading ISO9660 file %s at LSN %lu\n", psz_iso_name, (long unsigned int)lsn); goto out; } buf_size = (DWORD)MIN(i_file_length, ISO_BLOCKSIZE); for (j=0; j<WRITE_RETRIES; j++) { ISO_BLOCKING(s = WriteFile(file_handle, buf, buf_size, &wr_size, NULL)); if ((!s) || (buf_size != wr_size)) { uprintf(" Error writing file: %s", WindowsErrorString()); if (j < WRITE_RETRIES-1) uprintf(" RETRYING...\n"); } else { break; } } if (j >= WRITE_RETRIES) goto out; i_file_length -= ISO_BLOCKSIZE; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) { SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0); UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } } ISO_BLOCKING(safe_closehandle(file_handle)); if (is_syslinux_cfg) { if (replace_in_token_data(psz_fullpath, "append", iso_report.label, iso_report.usb_label, TRUE) != NULL) uprintf("Patched %s: '%s' -> '%s'\n", psz_fullpath, iso_report.label, iso_report.usb_label); } } } r = 0; out: ISO_BLOCKING(safe_closehandle(file_handle)); _cdio_list_free(p_entlist, true); return r; }
// Returns 0 on success, nonzero on error static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; BOOL r, is_syslinux_cfg, is_old_c32[NB_OLD_C32]; int i_length; size_t i, nul_pos; char tmp[128], *psz_fullpath = NULL; const char* psz_basename; udf_dirent_t *p_udf_dirent2; uint8_t buf[UDF_BLOCKSIZE]; int64_t i_read, i_file_length; if ((p_udf_dirent == NULL) || (psz_path == NULL)) return 1; while ((p_udf_dirent = udf_readdir(p_udf_dirent)) != NULL) { if (FormatStatus) goto out; psz_basename = udf_get_filename(p_udf_dirent); if (strlen(psz_basename) == 0) continue; i_length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir) + 24); psz_fullpath = (char*)calloc(sizeof(char), i_length); if (psz_fullpath == NULL) { uprintf("Error allocating file name\n"); goto out; } i_length = _snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename); if (i_length < 0) { goto out; } if (udf_is_dir(p_udf_dirent)) { if (!scan_only) _mkdirU(psz_fullpath); p_udf_dirent2 = udf_opendir(p_udf_dirent); if (p_udf_dirent2 != NULL) { if (udf_extract_files(p_udf, p_udf_dirent2, &psz_fullpath[strlen(psz_extract_dir)])) goto out; } } else { i_file_length = udf_get_file_length(p_udf_dirent); if (check_iso_props(psz_path, &is_syslinux_cfg, is_old_c32, i_file_length, psz_basename, psz_fullpath)) { safe_free(psz_fullpath); continue; } // Replace slashes with backslashes and append the size to the path for UI display nul_pos = safe_strlen(psz_fullpath); for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '/') psz_fullpath[i] = '\\'; safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE)); uprintf("Extracting: %s\n", psz_fullpath); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE)); SetWindowTextU(hISOFileName, psz_fullpath); // Remove the appended size for extraction psz_fullpath[nul_pos] = 0; for (i=0; i<NB_OLD_C32; i++) { if (is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileA(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version\n"); break; } uprintf(" Could not replace file: %s\n", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; if (sanitize_filename(psz_fullpath)) uprintf(" File name sanitized to '%s'\n", psz_fullpath); file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s\n", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_fullpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else while (i_file_length > 0) { if (FormatStatus) goto out; memset(buf, 0, UDF_BLOCKSIZE); i_read = udf_read_block(p_udf_dirent, buf, 1); if (i_read < 0) { uprintf(" Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]); goto out; } buf_size = (DWORD)MIN(i_file_length, i_read); for (i=0; i<WRITE_RETRIES; i++) { ISO_BLOCKING(r = WriteFile(file_handle, buf, buf_size, &wr_size, NULL)); if ((!r) || (buf_size != wr_size)) { uprintf(" Error writing file: %s", WindowsErrorString()); if (i < WRITE_RETRIES-1) uprintf(" RETRYING...\n"); } else { break; } } if (i >= WRITE_RETRIES) goto out; i_file_length -= i_read; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) { SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0); UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } } // If you have a fast USB 3.0 device, the default Windows buffering does an // excellent job at compensating for our small blocks read/writes to max out the // device's bandwidth. // The drawback however is with cancellation. With a large file, CloseHandle() // may take forever to complete and is not interruptible. We try to detect this. ISO_BLOCKING(safe_closehandle(file_handle)); if (is_syslinux_cfg) { // Workaround for isolinux config files requiring an ISO label for kernel // append that may be different from our USB label. if (replace_in_token_data(psz_fullpath, "append", iso_report.label, iso_report.usb_label, TRUE) != NULL) uprintf("Patched %s: '%s' -> '%s'\n", psz_fullpath, iso_report.label, iso_report.usb_label); } } safe_free(psz_fullpath); } return 0; out: if (p_udf_dirent != NULL) udf_dirent_free(p_udf_dirent); ISO_BLOCKING(safe_closehandle(file_handle)); safe_free(psz_fullpath); return 1; }
/* * FormatMessage does not handle internet errors * https://docs.microsoft.com/en-us/windows/desktop/wininet/wininet-errors */ const char* WinInetErrorString(void) { static char error_string[256]; DWORD size = sizeof(error_string); error_code = HRESULT_CODE(GetLastError()); if ((error_code < INTERNET_ERROR_BASE) || (error_code > INTERNET_ERROR_LAST)) return WindowsErrorString(); switch(error_code) { case ERROR_INTERNET_OUT_OF_HANDLES: return "No more handles could be generated at this time."; case ERROR_INTERNET_TIMEOUT: return "The request has timed out."; case ERROR_INTERNET_INTERNAL_ERROR: return "An internal error has occurred."; case ERROR_INTERNET_INVALID_URL: return "The URL is invalid."; case ERROR_INTERNET_UNRECOGNIZED_SCHEME: return "The URL scheme could not be recognized or is not supported."; case ERROR_INTERNET_NAME_NOT_RESOLVED: return "The server name could not be resolved."; case ERROR_INTERNET_PROTOCOL_NOT_FOUND: return "The requested protocol could not be located."; case ERROR_INTERNET_INVALID_OPTION: return "A request specified an invalid option value."; case ERROR_INTERNET_BAD_OPTION_LENGTH: return "The length of an option supplied is incorrect for the type of option specified."; case ERROR_INTERNET_OPTION_NOT_SETTABLE: return "The request option cannot be set, only queried."; case ERROR_INTERNET_SHUTDOWN: return "The Win32 Internet function support is being shut down or unloaded."; case ERROR_INTERNET_INCORRECT_USER_NAME: return "The request to connect and log on to an FTP server could not be completed because the supplied user name is incorrect."; case ERROR_INTERNET_INCORRECT_PASSWORD: return "The request to connect and log on to an FTP server could not be completed because the supplied password is incorrect."; case ERROR_INTERNET_LOGIN_FAILURE: return "The request to connect to and log on to an FTP server failed."; case ERROR_INTERNET_INVALID_OPERATION: return "The requested operation is invalid."; case ERROR_INTERNET_OPERATION_CANCELLED: return "The operation was cancelled, usually because the handle on which the request was operating was closed before the operation completed."; case ERROR_INTERNET_INCORRECT_HANDLE_TYPE: return "The type of handle supplied is incorrect for this operation."; case ERROR_INTERNET_INCORRECT_HANDLE_STATE: return "The requested operation cannot be carried out because the handle supplied is not in the correct state."; case ERROR_INTERNET_NOT_PROXY_REQUEST: return "The request cannot be made via a proxy."; case ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND: return "A required registry value could not be located."; case ERROR_INTERNET_BAD_REGISTRY_PARAMETER: return "A required registry value was located but is an incorrect type or has an invalid value."; case ERROR_INTERNET_NO_DIRECT_ACCESS: return "Direct network access cannot be made at this time."; case ERROR_INTERNET_NO_CONTEXT: return "An asynchronous request could not be made because a zero context value was supplied."; case ERROR_INTERNET_NO_CALLBACK: return "An asynchronous request could not be made because a callback function has not been set."; case ERROR_INTERNET_REQUEST_PENDING: return "The required operation could not be completed because one or more requests are pending."; case ERROR_INTERNET_INCORRECT_FORMAT: return "The format of the request is invalid."; case ERROR_INTERNET_ITEM_NOT_FOUND: return "The requested item could not be located."; case ERROR_INTERNET_CANNOT_CONNECT: return "The attempt to connect to the server failed."; case ERROR_INTERNET_CONNECTION_ABORTED: return "The connection with the server has been terminated."; case ERROR_INTERNET_CONNECTION_RESET: return "The connection with the server has been reset."; case ERROR_INTERNET_FORCE_RETRY: return "Calls for the Win32 Internet function to redo the request."; case ERROR_INTERNET_INVALID_PROXY_REQUEST: return "The request to the proxy was invalid."; case ERROR_INTERNET_HANDLE_EXISTS: return "The request failed because the handle already exists."; case ERROR_INTERNET_SEC_INVALID_CERT: return "The SSL certificate is invalid."; case ERROR_INTERNET_SEC_CERT_DATE_INVALID: return "SSL certificate date that was received from the server is bad. The certificate is expired."; case ERROR_INTERNET_SEC_CERT_CN_INVALID: return "SSL certificate common name (host name field) is incorrect."; case ERROR_INTERNET_SEC_CERT_ERRORS: return "The SSL certificate contains errors."; case ERROR_INTERNET_SEC_CERT_NO_REV: return "The SSL certificate was not revoked."; case ERROR_INTERNET_SEC_CERT_REV_FAILED: return "The revocation check of the SSL certificate failed."; case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: return "The application is moving from a non-SSL to an SSL connection because of a redirect."; case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR: return "The application is moving from an SSL to an non-SSL connection because of a redirect."; case ERROR_INTERNET_MIXED_SECURITY: return "Some of the content being viewed may have come from unsecured servers."; case ERROR_INTERNET_CHG_POST_IS_NON_SECURE: return "The application is posting and attempting to change multiple lines of text on a server that is not secure."; case ERROR_INTERNET_POST_IS_NON_SECURE: return "The application is posting data to a server that is not secure."; case ERROR_FTP_TRANSFER_IN_PROGRESS: return "The requested operation cannot be made on the FTP session handle because an operation is already in progress."; case ERROR_FTP_DROPPED: return "The FTP operation was not completed because the session was aborted."; case ERROR_GOPHER_PROTOCOL_ERROR: case ERROR_GOPHER_NOT_FILE: case ERROR_GOPHER_DATA_ERROR: case ERROR_GOPHER_END_OF_DATA: case ERROR_GOPHER_INVALID_LOCATOR: case ERROR_GOPHER_INCORRECT_LOCATOR_TYPE: case ERROR_GOPHER_NOT_GOPHER_PLUS: case ERROR_GOPHER_ATTRIBUTE_NOT_FOUND: case ERROR_GOPHER_UNKNOWN_LOCATOR: return "Gopher? Really??? What is this, 1994?"; case ERROR_HTTP_HEADER_NOT_FOUND: return "The requested header could not be located."; case ERROR_HTTP_DOWNLEVEL_SERVER: return "The server did not return any headers."; case ERROR_HTTP_INVALID_SERVER_RESPONSE: return "The server response could not be parsed."; case ERROR_HTTP_INVALID_HEADER: return "The supplied header is invalid."; case ERROR_HTTP_INVALID_QUERY_REQUEST: return "The request made to HttpQueryInfo is invalid."; case ERROR_HTTP_HEADER_ALREADY_EXISTS: return "The header could not be added because it already exists."; case ERROR_HTTP_REDIRECT_FAILED: return "The redirection failed because either the scheme changed or all attempts made to redirect failed."; case ERROR_INTERNET_SECURITY_CHANNEL_ERROR: return "This system's SSL library is too old to be able to access this website."; case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED: return "Client Authentication certificate needed"; case ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT: return "Bad auto proxy script."; case ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT: return "Unable to download script."; case ERROR_INTERNET_NOT_INITIALIZED: return "Internet has not be initialized."; case ERROR_INTERNET_UNABLE_TO_CACHE_FILE: return "Unable to cache the file."; case ERROR_INTERNET_TCPIP_NOT_INSTALLED: return "TPC/IP not installed."; case ERROR_INTERNET_DISCONNECTED: return "Internet is disconnected."; case ERROR_INTERNET_SERVER_UNREACHABLE: return "Server could not be reached."; case ERROR_INTERNET_PROXY_SERVER_UNREACHABLE: return "Proxy server could not be reached."; case ERROR_INTERNET_FAILED_DUETOSECURITYCHECK: return "A security check prevented internet connection."; case ERROR_INTERNET_NEED_MSN_SSPI_PKG: return "This connection requires an MSN Security Support Provider Interface package."; case ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY: return "Please ask Microsoft about that one!"; case ERROR_INTERNET_EXTENDED_ERROR: InternetGetLastResponseInfoA(&error_code, error_string, &size); return error_string; default: static_sprintf(error_string, "Unknown internet error 0x%08lX", error_code); return error_string; } }
/* * Background thread to check for updates */ static DWORD WINAPI CheckForUpdatesThread(LPVOID param) { BOOL releases_only, found_new_version = FALSE; int status = 0; const char* server_url = APPLICATION_URL "/"; int i, j, k, verbose = 0, verpos[4]; static const char* archname[] = {"win_x86", "win_x64"}; static const char* channel[] = {"release", "beta"}; // release channel const char* accept_types[] = {"*/*\0", NULL}; DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus; char* buf = NULL; char agent[64], hostname[64], urlpath[128]; OSVERSIONINFOA os_version = {sizeof(OSVERSIONINFOA), 0, 0, 0, 0, ""}; HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL; URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0, hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1}; SYSTEMTIME ServerTime, LocalTime; FILETIME FileTime; int64_t local_time = 0, reg_time, server_time, update_interval; update_check_in_progress = TRUE; verbose = ReadRegistryKey32(REGKEY_HKCU, REGKEY_VERBOSE_UPDATES); // Without this the FileDialog will produce error 0x8001010E when compiled for Vista or later IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); // Unless the update was forced, wait a while before performing the update check if (!force_update_check) { // TODO: Also check on inactivity // It would of course be a lot nicer to use a timer and wake the thread, but my // development time is limited and this is FASTER to implement. do { for (i=0; (i<30) && (!force_update_check); i++) Sleep(500); } while ((!force_update_check) && ((installation_running || (dialog_showing>0)))); if (!force_update_check) { if ((ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL) == -1)) { vuprintf("Check for updates disabled, as per registry settings.\n"); goto out; } reg_time = ReadRegistryKey64(REGKEY_HKCU, REGKEY_LAST_UPDATE); update_interval = (int64_t)ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL); if (update_interval == 0) { WriteRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL); update_interval = DEFAULT_UPDATE_INTERVAL; } GetSystemTime(&LocalTime); if (!SystemTimeToFileTime(&LocalTime, &FileTime)) goto out; local_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000; vvuprintf("Local time: %" PRId64 "\n", local_time); if (local_time < reg_time + update_interval) { vuprintf("Next update check in %" PRId64 " seconds.\n", reg_time + update_interval - local_time); goto out; } } } print_status(3000, TRUE, "Checking for " APPLICATION_NAME " updates..."); status++; // 1 if (!GetVersionExA(&os_version)) { dprintf("Could not read Windows version - Check for updates cancelled.\n"); goto out; } if ((!InternetCrackUrlA(server_url, (DWORD)safe_strlen(server_url), 0, &UrlParts)) || (!InternetGetConnectedState(&dwFlags, 0))) goto out; hostname[sizeof(hostname)-1] = 0; static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)", application_version[0], application_version[1], application_version[2], nWindowsVersion >> 4, nWindowsVersion & 0x0F, is_x64() ? "; WOW64" : ""); hSession = InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hSession == NULL) goto out; hConnection = InternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL); if (hConnection == NULL) goto out; status++; // 2 releases_only = !GetRegistryKeyBool(REGKEY_HKCU, REGKEY_INCLUDE_BETAS); for (k=0; (k<(releases_only?1:(int)ARRAYSIZE(channel))) && (!found_new_version); k++) { dprintf("Checking %s channel...\n", channel[k]); // At this stage we can query the server for various update version files. // We first try to lookup for "<appname>_<os_arch>_<os_version_major>_<os_version_minor>.ver" // and then remove each each of the <os_> components until we find our match. For instance, we may first // look for <app_name>_win_x64_6.2.ver (Win8 x64) but only get a match for <app_name>_win_x64_6.ver (Vista x64 or later) // This allows sunsetting OS versions (eg XP) or providing different downloads for different archs/groups. static_sprintf(urlpath, "%s%s%s_%s_%ld.%ld.ver", APPLICATION_NAME, (k==0)?"":"_", (k==0)?"":channel[k], archname[is_x64()?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion); vuprintf("Base update check: %s\n", urlpath); for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) { if ((urlpath[j] == '.') || (urlpath[j] == '_')) { verpos[i++] = j; } } if (i != ARRAYSIZE(verpos)) { dprintf("Broken code in CheckForUpdatesThread()!\n"); goto out; } UrlParts.lpszUrlPath = urlpath; UrlParts.dwUrlPathLength = sizeof(urlpath); for (i=0; i<ARRAYSIZE(verpos); i++) { vvuprintf("Trying %s\n", UrlParts.lpszUrlPath); hRequest = HttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_HYPERLINK | ((UrlParts.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0), (DWORD_PTR)NULL); if ((hRequest == NULL) || (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0))) goto out; // Ensure that we get a text file dwSize = sizeof(dwStatus); dwStatus = 404; HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwStatus, &dwSize, NULL); if (dwStatus == 200) break; InternetCloseHandle(hRequest); hRequest = NULL; safe_strcpy(&urlpath[verpos[i]], 5, ".ver"); } if (dwStatus != 200) { vuprintf("Could not find a %s version file on server %s", channel[k], server_url); if ((releases_only) || (k+1 >= ARRAYSIZE(channel))) goto out; continue; } vuprintf("Found match for %s on server %s", urlpath, server_url); // We also get a date from Apache, which we'll use to avoid out of sync check, // in case some set their clock way into the future and back. // On the other hand, if local clock is set way back in the past, we will never check. dwSize = sizeof(ServerTime); // If we can't get a date we can trust, don't bother... if ( (!HttpQueryInfoA(hRequest, HTTP_QUERY_DATE|HTTP_QUERY_FLAG_SYSTEMTIME, (LPVOID)&ServerTime, &dwSize, NULL)) || (!SystemTimeToFileTime(&ServerTime, &FileTime)) ) goto out; server_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000; vvuprintf("Server time: %" PRId64 "\n", server_time); // Always store the server response time - the only clock we trust! WriteRegistryKey64(REGKEY_HKCU, REGKEY_LAST_UPDATE, server_time); // Might as well let the user know if (!force_update_check) { if ((local_time > server_time + 600) || (local_time < server_time - 600)) { dprintf("IMPORTANT: Your local clock is more than 10 minutes in the %s. Unless you fix this, " APPLICATION_NAME " may not be able to check for updates...", (local_time > server_time + 600)?"future":"past"); } } dwSize = sizeof(dwTotalSize); if (!HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) goto out; safe_free(buf); // Make sure the file is NUL terminated buf = (char*)calloc(dwTotalSize+1, 1); if (buf == NULL) goto out; // This is a version file - we should be able to gulp it down in one go if (!InternetReadFile(hRequest, buf, dwTotalSize, &dwDownloaded) || (dwDownloaded != dwTotalSize)) goto out; status++; vuprintf("Successfully downloaded version file (%d bytes)\n", dwTotalSize); parse_update(buf, dwTotalSize+1); vuprintf("UPDATE DATA:\n"); vuprintf(" version: %d.%d.%d (%s)\n", update.version[0], update.version[1], update.version[2], channel[k]); vuprintf(" platform_min: %d.%d\n", update.platform_min[0], update.platform_min[1]); vuprintf(" url: %s\n", update.download_url); found_new_version = ((to_uint64_t(update.version) > to_uint64_t(application_version)) || (force_update)) && ( (os_version.dwMajorVersion > update.platform_min[0]) || ( (os_version.dwMajorVersion == update.platform_min[0]) && (os_version.dwMinorVersion >= update.platform_min[1])) ); dprintf("N%sew %s version found%c\n", found_new_version?"":"o n", channel[k], found_new_version?'!':'.'); } out: safe_free(buf); if (hRequest) InternetCloseHandle(hRequest); if (hConnection) InternetCloseHandle(hConnection); if (hSession) InternetCloseHandle(hSession); switch(status) { case 1: print_status(3000, TRUE, "Updates: Unable to connect to the internet"); break; case 2: print_status(3000, TRUE, "Updates: Unable to acces version data"); break; case 3: case 4: print_status(3000, FALSE, found_new_version?"A new version of " APPLICATION_NAME " is available!": "No new version of " APPLICATION_NAME " was found"); default: break; } // Start the new download after cleanup if (found_new_version) { // User may have started an operation while we were checking while ((!force_update_check) && (installation_running || (dialog_showing > 0))) { Sleep(15000); } download_new_version(); } else if (force_update_check) { PostMessage(hMainDialog, UM_NO_UPDATE, 0, 0); } force_update_check = FALSE; update_check_in_progress = FALSE; ExitThread(0); }
/* * Download a file from an URL * Mostly taken from http://support.microsoft.com/kb/234913 * If hProgressDialog is not NULL, this function will send INIT and EXIT messages * to the dialog in question, with WPARAM being set to nonzero for EXIT on success * and also attempt to indicate progress using an IDC_PROGRESS control */ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog) { HWND hProgressBar = NULL; BOOL r = FALSE; LONG progress_style; DWORD dwFlags, dwSize, dwWritten, dwDownloaded, dwTotalSize; DWORD DownloadStatus; HANDLE hFile = INVALID_HANDLE_VALUE; const char* accept_types[] = {"*/*\0", NULL}; unsigned char buf[DOWNLOAD_BUFFER_SIZE]; char agent[64], hostname[64], urlpath[128], msg[MAX_PATH]; HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL; URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0, hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1}; size_t last_slash; int i; DownloadStatus = 404; if (hProgressDialog != NULL) { // Use the progress control provided, if any hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS); if (hProgressBar != NULL) { progress_style = GetWindowLong(hProgressBar, GWL_STYLE); SetWindowLong(hProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hProgressBar, PBM_SETPOS, 0, 0); } SendMessage(hProgressDialog, UM_DOWNLOAD_INIT, 0, 0); } if (file == NULL) goto out; for (last_slash = safe_strlen(file); last_slash != 0; last_slash--) { if ((file[last_slash] == '/') || (file[last_slash] == '\\')) { last_slash++; break; } } static_sprintf(msg, "Downloading %s: Connecting...", file); print_status(0, FALSE, msg); dprintf("Downloading %s from %s\n", file, url); if ( (!InternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts)) || (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL)) { dprintf("Unable to decode URL: %s\n", WinInetErrorString()); goto out; } hostname[sizeof(hostname)-1] = 0; // Open an Internet session for (i=5; (i>0) && (!InternetGetConnectedState(&dwFlags, 0)); i--) { Sleep(1000); } if (i <= 0) { // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong... SetLastError(ERROR_INTERNET_NOT_INITIALIZED); dprintf("Network is unavailable: %s\n", WinInetErrorString()); goto out; } static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)", application_version[0], application_version[1], application_version[2], nWindowsVersion>>4, nWindowsVersion&0x0F, is_x64()?"; WOW64":""); hSession = InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hSession == NULL) { dprintf("Could not open Internet session: %s\n", WinInetErrorString()); goto out; } hConnection = InternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL); if (hConnection == NULL) { dprintf("Could not connect to server %s:%d: %s\n", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString()); goto out; } hRequest = HttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS| INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK| ((UrlParts.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL); if (hRequest == NULL) { dprintf("Could not open URL %s: %s\n", url, WinInetErrorString()); goto out; } if (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0)) { dprintf("Unable to send request: %s\n", WinInetErrorString()); goto out; } // Get the file size dwSize = sizeof(DownloadStatus); HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL); if (DownloadStatus != 200) { error_code = ERROR_SEVERITY_ERROR|ERROR_INTERNET_ITEM_NOT_FOUND; dprintf("Unable to access file: %d\n", DownloadStatus); goto out; } dwSize = sizeof(dwTotalSize); if (!HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) { dprintf("Unable to retrieve file length: %s\n", WinInetErrorString()); goto out; } dprintf("File length: %d bytes\n", dwTotalSize); hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { dprintf("Unable to create file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } // Keep checking for data until there is nothing left. dwSize = 0; while (1) { if (IS_ERROR(error_code)) goto out; if (!InternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0)) break; dwSize += dwDownloaded; SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize)/(1.0f*dwTotalSize))), 0); static_sprintf(msg, "Downloading: %0.1f%%", (100.0f*dwSize)/(1.0f*dwTotalSize)); print_status(0, FALSE, msg); if (!WriteFile(hFile, buf, dwDownloaded, &dwWritten, NULL)) { dprintf("Error writing file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } else if (dwDownloaded != dwWritten) { dprintf("Error writing file '%s': Only %d/%d bytes written\n", dwWritten, dwDownloaded); goto out; } } if (dwSize != dwTotalSize) { dprintf("Could not download complete file - read: %d bytes, expected: %d bytes\n", dwSize, dwTotalSize); error_code = ERROR_SEVERITY_ERROR|ERROR_WRITE_FAULT; goto out; } else { r = TRUE; dprintf("Successfully downloaded '%s'\n", &file[last_slash]); } out: if (hProgressDialog != NULL) SendMessage(hProgressDialog, UM_DOWNLOAD_EXIT, (WPARAM)r, 0); if (hFile != INVALID_HANDLE_VALUE) { // Force a flush - May help with the PKI API trying to process downloaded updates too early... FlushFileBuffers(hFile); CloseHandle(hFile); } if (!r) { if (file != NULL) _unlinkU(file); print_status(0, FALSE, "Failed to download file."); SetLastError(error_code); MessageBoxU(hMainDialog, WinInetErrorString(), "File download", MB_OK|MB_ICONERROR); } if (hRequest) InternetCloseHandle(hRequest); if (hConnection) InternetCloseHandle(hConnection); if (hSession) InternetCloseHandle(hSession); return r?dwSize:0; }
/* * Return the UTF8 path of a file selected through a load or save dialog * All string parameters are UTF-8 * IMPORTANT NOTE: Remember that you need to call CoInitializeEx() for * *EACH* thread you invoke FileDialog from, as GetDisplayName() will * return error 0x8001010E otherwise. */ char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options) { DWORD tmp; OPENFILENAMEA ofn; char selected_name[MAX_PATH]; char *ext_string = NULL, *all_files = NULL; size_t i, j, ext_strlen; BOOL r; char* filepath = NULL; HRESULT hr = FALSE; IFileDialog *pfd = NULL; IShellItem *psiResult; COMDLG_FILTERSPEC* filter_spec = NULL; wchar_t *wpath = NULL, *wfilename = NULL; IShellItem *si_path = NULL; // Automatically freed if ((ext == NULL) || (ext->count == 0) || (ext->extension == NULL) || (ext->description == NULL)) return NULL; dialog_showing++; filter_spec = (COMDLG_FILTERSPEC*)calloc(ext->count + 1, sizeof(COMDLG_FILTERSPEC)); if (filter_spec != NULL) { // Setup the file extension filter table for (i = 0; i < ext->count; i++) { filter_spec[i].pszSpec = utf8_to_wchar(ext->extension[i]); filter_spec[i].pszName = utf8_to_wchar(ext->description[i]); } filter_spec[i].pszSpec = L"*.*"; filter_spec[i].pszName = L"All files"; hr = CoCreateInstance(save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC, &IID_IFileDialog, (LPVOID)&pfd); if (FAILED(hr)) { SetLastError(hr); dprintf("CoCreateInstance for FileOpenDialog failed: %s\n", WindowsErrorString()); pfd = NULL; // Just in case goto fallback; } // Set the file extension filters pfd->lpVtbl->SetFileTypes(pfd, (UINT)ext->count + 1, filter_spec); // Set the default directory wpath = utf8_to_wchar(path); hr = SHCreateItemFromParsingName(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path); if (SUCCEEDED(hr)) { pfd->lpVtbl->SetFolder(pfd, si_path); } safe_free(wpath); // Set the default filename wfilename = utf8_to_wchar((ext->filename == NULL) ? "" : ext->filename); if (wfilename != NULL) { pfd->lpVtbl->SetFileName(pfd, wfilename); } // Display the dialog hr = pfd->lpVtbl->Show(pfd, hMainDialog); // Cleanup safe_free(wfilename); for (i = 0; i < ext->count; i++) { safe_free(filter_spec[i].pszSpec); safe_free(filter_spec[i].pszName); } safe_free(filter_spec); if (SUCCEEDED(hr)) { // Obtain the result of the user's interaction with the dialog. hr = pfd->lpVtbl->GetResult(pfd, &psiResult); if (SUCCEEDED(hr)) { hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath); if (SUCCEEDED(hr)) { filepath = wchar_to_utf8(wpath); CoTaskMemFree(wpath); } else { SetLastError(hr); dprintf("Unable to access file path: %s\n", WindowsErrorString()); } psiResult->lpVtbl->Release(psiResult); } } else if ((hr & 0xFFFF) != ERROR_CANCELLED) { // If it's not a user cancel, assume the dialog didn't show and fallback SetLastError(hr); dprintf("Could not show FileOpenDialog: %s\n", WindowsErrorString()); goto fallback; } pfd->lpVtbl->Release(pfd); dialog_showing--; return filepath; } fallback: safe_free(filter_spec); if (pfd != NULL) { pfd->lpVtbl->Release(pfd); } memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hMainDialog; // Selected File name static_sprintf(selected_name, "%s", (ext->filename == NULL) ? "" : ext->filename); ofn.lpstrFile = selected_name; ofn.nMaxFile = MAX_PATH; // Set the file extension filters all_files = "All files"; ext_strlen = 0; for (i = 0; i<ext->count; i++) { ext_strlen += safe_strlen(ext->description[i]) + 2 * safe_strlen(ext->extension[i]) + sizeof(" ()\r\r"); } ext_strlen += safe_strlen(all_files) + sizeof(" (*.*)\r*.*\r"); ext_string = (char*)malloc(ext_strlen + 1); if (ext_string == NULL) return NULL; ext_string[0] = 0; for (i = 0, j = 0; i<ext->count; i++) { j += _snprintf(&ext_string[j], ext_strlen - j, "%s (%s)\r%s\r", ext->description[i], ext->extension[i], ext->extension[i]); } j = _snprintf(&ext_string[j], ext_strlen - j, "%s (*.*)\r*.*\r", all_files); // Microsoft could really have picked a better delimiter! for (i = 0; i<ext_strlen; i++) { // Since the VS Code Analysis tool is dumb... #if defined(_MSC_VER) #pragma warning(suppress: 6385) #endif if (ext_string[i] == '\r') { #if defined(_MSC_VER) #pragma warning(suppress: 6386) #endif ext_string[i] = 0; } } ofn.lpstrFilter = ext_string; ofn.nFilterIndex = 1; ofn.lpstrInitialDir = path; ofn.Flags = OFN_OVERWRITEPROMPT | options; // Show Dialog if (save) { r = GetSaveFileNameU(&ofn); } else { r = GetOpenFileNameU(&ofn); } if (r) { filepath = safe_strdup(selected_name); } else { tmp = CommDlgExtendedError(); if (tmp != 0) { dprintf("Could not select file for %s. Error %X\n", save ? "save" : "open", tmp); } } safe_free(ext_string); dialog_showing--; return filepath; }
/* * Refresh the list of USB devices */ BOOL GetDevices(DWORD devnum) { // List of USB storage drivers we know - list may be incomplete! const char* usbstor_name[] = { // Standard MS USB storage driver "USBSTOR", // USB card readers, with proprietary drivers (Realtek,etc...) // Mostly "guessed" from http://www.carrona.org/dvrref.php "RTSUER", "CMIUCR", "EUCR", // UASP Drivers *MUST* be listed after this, starting with "UASPSTOR" // (which is Microsoft's native UASP driver for Windows 8 and later) // as we use "UASPSTOR" as a delimiter "UASPSTOR", "VUSBSTOR", "ETRONSTOR", "ASUSSTPT" }; // These are the generic (non USB) storage enumerators we also test const char* genstor_name[] = { // Generic storage drivers (Careful now!) "SCSI", // "STORAGE", // "STORAGE" is used by 'Storage Spaces" and stuff => DANGEROUS! // Non-USB card reader drivers - This list *MUST* start with "SD" (delimiter) // See http://itdoc.hitachi.co.jp/manuals/3021/30213B5200e/DMDS0094.HTM // Also http://www.carrona.org/dvrref.php. NB: These should be reported // as enumerators by Rufus when Enum Debug is enabled "SD", "PCISTOR", "RTSOR", "JMCR", "JMCF", "RIMMPTSK", "RIMSPTSK", "RISD", "RIXDPTSK", "TI21SONY", "ESD7SK", "ESM7SK", "O2MD", "O2SD", "VIACR" }; // Oh, and we also have card devices (e.g. 'SCSI\DiskO2Micro_SD_...') under the SCSI enumerator... const char* scsi_disk_prefix = "SCSI\\Disk"; const char* scsi_card_name[] = { "_SD_", "_SDHC_", "_MMC_", "_MS_", "_MSPro_", "_xDPicture_", "_O2Media_" }; const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0" }; // Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path htab_table htab_devid = HTAB_EMPTY; StrArray dev_if_path; char letter_name[] = " (?:)"; char drive_name[] = "?:\\"; char uefi_togo_check[] = "?:\\EFI\\Rufus\\ntfs_x64.efi"; char scsi_card_name_copy[16]; BOOL r = FALSE, found = FALSE, post_backslash; HDEVINFO dev_info = NULL; SP_DEVINFO_DATA dev_info_data; SP_DEVICE_INTERFACE_DATA devint_data; PSP_DEVICE_INTERFACE_DETAIL_DATA_A devint_detail_data; DEVINST parent_inst, grandparent_inst, device_inst; DWORD size, i, j, k, l, datatype, drive_index; DWORD uasp_start = ARRAYSIZE(usbstor_name), card_start = ARRAYSIZE(genstor_name); ULONG list_size[ARRAYSIZE(usbstor_name)] = { 0 }, list_start[ARRAYSIZE(usbstor_name)] = { 0 }, full_list_size, ulFlags; HANDLE hDrive; LONG maxwidth = 0; int s, score, drive_number, remove_drive; char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128]; char *p, *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str, *hub_path; usb_device_props props; IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList)); StrArrayClear(&DriveID); StrArrayClear(&DriveLabel); StrArrayClear(&DriveHub); StrArrayCreate(&dev_if_path, 128); // Add a dummy for string index zero, as this is what non matching hashes will point to StrArrayAdd(&dev_if_path, "", TRUE); device_id = (char*)malloc(MAX_PATH); if (device_id == NULL) goto out; // Build a hash table associating a CM Device ID of an USB device with the SetupDI Device Interface Path // of its parent hub - this is needed to retrieve the device speed dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info != INVALID_HANDLE_VALUE) { if (htab_create(DEVID_HTAB_SIZE, &htab_devid)) { dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { uuprintf("Processing Hub %d:", i + 1); devint_detail_data = NULL; devint_data.cbSize = sizeof(devint_data); // Only care about the first interface (MemberIndex 0) if ( (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_USB_HUB, 0, &devint_data)) && (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && ((devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size)) != NULL) ) { devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { // Find the Device IDs for all the children of this hub if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { device_id[0] = 0; s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath, TRUE); uuprintf(" Hub[%d] = '%s'", s, devint_detail_data->DevicePath); if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { ToUpper(device_id); if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } uuprintf(" Found ID[%03d]: %s", k, device_id); while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { device_id[0] = 0; if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) { ToUpper(device_id); if ((k = htab_hash(device_id, &htab_devid)) != 0) { htab_devid.table[k].data = (void*)(uintptr_t)s; } uuprintf(" Found ID[%03d]: %s", k, device_id); } } } } } free(devint_detail_data); } } } SetupDiDestroyDeviceInfoList(dev_info); } free(device_id); // Build a single list of Device IDs from all the storage enumerators we know of full_list_size = 0; ulFlags = CM_GETIDLIST_FILTER_SERVICE | CM_GETIDLIST_FILTER_PRESENT; for (s=0; s<ARRAYSIZE(usbstor_name); s++) { // Get a list of device IDs for all USB storage devices // This will be used to find if a device is UASP // Also compute the uasp_start index if (strcmp(usbstor_name[s], "UASPSTOR") == 0) uasp_start = s; if (CM_Get_Device_ID_List_SizeA(&list_size[s], usbstor_name[s], ulFlags) != CR_SUCCESS) list_size[s] = 0; if (list_size[s] != 0) full_list_size += list_size[s]-1; // remove extra NUL terminator } // Compute the card_start index for (s=0; s<ARRAYSIZE(genstor_name); s++) { if (strcmp(genstor_name[s], "SD") == 0) card_start = s; } // Better safe than sorry. And yeah, we could have used arrays of // arrays to avoid this, but it's more readable this way. assert((uasp_start > 0) && (uasp_start < ARRAYSIZE(usbstor_name))); assert((card_start > 0) && (card_start < ARRAYSIZE(genstor_name))); devid_list = NULL; if (full_list_size != 0) { full_list_size += 1; // add extra NUL terminator devid_list = (char*)malloc(full_list_size); if (devid_list == NULL) { uprintf("Could not allocate Device ID list\n"); goto out; } for (s=0, i=0; s<ARRAYSIZE(usbstor_name); s++) { list_start[s] = i; if (list_size[s] > 1) { if (CM_Get_Device_ID_ListA(usbstor_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS) continue; if (usb_debug) { uprintf("Processing IDs belonging to '%s':", usbstor_name[s]); for (device_id = &devid_list[i]; *device_id != 0; device_id += strlen(device_id) + 1) uprintf(" %s", device_id); } // The list_size is sometimes larger than required thus we need to find the real end for (i += list_size[s]; i > 2; i--) { if ((devid_list[i-2] != '\0') && (devid_list[i-1] == '\0') && (devid_list[i] == '\0')) break; } } } } // Now use SetupDi to enumerate all our disk storage devices dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); if (dev_info == INVALID_HANDLE_VALUE) { uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString()); goto out; } dev_info_data.cbSize = sizeof(dev_info_data); for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { memset(buffer, 0, sizeof(buffer)); memset(&props, 0, sizeof(props)); method_str = ""; hub_path = NULL; 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; } for (j = 0; j < ARRAYSIZE(usbstor_name); j++) { if (safe_stricmp(buffer, usbstor_name[0]) == 0) { props.is_USB = TRUE; if ((j != 0) && (j < uasp_start)) props.is_CARD = TRUE; break; } } // UASP drives are listed under SCSI, and we also have non USB card readers to populate for (j = 0; j < ARRAYSIZE(genstor_name); j++) { if (safe_stricmp(buffer, genstor_name[j]) == 0) { props.is_SCSI = TRUE; if (j >= card_start) props.is_CARD = TRUE; break; } } uuprintf("Processing '%s' device:", buffer); if ((!props.is_USB) && (!props.is_SCSI)) { uuprintf(" Disabled by policy"); continue; } // We can't use the friendly name to find if a drive is a VHD, as friendly name string gets translated // according to your locale, so we poke the Hardware ID memset(buffer, 0, sizeof(buffer)); props.is_VHD = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_HARDWAREID, &datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsVHD(buffer); // Additional detection for SCSI card readers if ((!props.is_CARD) && (safe_strnicmp(buffer, scsi_disk_prefix, sizeof(scsi_disk_prefix)-1) == 0)) { for (j = 0; j < ARRAYSIZE(scsi_card_name); j++) { static_strcpy(scsi_card_name_copy, scsi_card_name[j]); if (safe_strstr(buffer, scsi_card_name_copy) != NULL) { props.is_CARD = TRUE; break; } // Also test for "_SD&" instead of "_SD_" and so on to allow for devices like // "SCSI\DiskRicoh_Storage_SD&REV_3.0" to be detected. scsi_card_name_copy[strlen(scsi_card_name_copy) - 1] = '&'; if (safe_strstr(buffer, scsi_card_name_copy) != NULL) { props.is_CARD = TRUE; break; } } } uuprintf(" Hardware ID: '%s'", buffer); memset(buffer, 0, sizeof(buffer)); props.is_Removable = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_REMOVAL_POLICY, &datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsRemovable(buffer); memset(buffer, 0, sizeof(buffer)); if (!SetupDiGetDeviceRegistryPropertyU(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)" static_strcpy(buffer, lmprintf(MSG_045)); } else if ((!props.is_VHD) && (devid_list != NULL)) { // Get the properties of the device. We could avoid doing this lookup every time by keeping // a lookup table, but there shouldn't be that many USB storage devices connected... // NB: Each of these Device IDs should have a child, from which we get the Device Instance match. for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) { if (CM_Locate_DevNodeA(&parent_inst, device_id, 0) != CR_SUCCESS) { uuprintf("Could not locate device node for '%s'", device_id); continue; } if (CM_Get_Child(&device_inst, parent_inst, 0) != CR_SUCCESS) { uuprintf("Could not get children of '%s'", device_id); continue; } if (device_inst != dev_info_data.DevInst) { // Try the siblings while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { if (device_inst == dev_info_data.DevInst) { uuprintf("NOTE: Matched instance from sibling for '%s'", device_id); break; } } if (device_inst != dev_info_data.DevInst) continue; } post_backslash = FALSE; method_str = ""; // If we're not dealing with the USBSTOR part of our list, then this is an UASP device props.is_UASP = ((((uintptr_t)device_id)+2) >= ((uintptr_t)devid_list)+list_start[uasp_start]); // Now get the properties of the device, and its Device ID, which we need to populate the properties ToUpper(device_id); j = htab_hash(device_id, &htab_devid); uuprintf(" Matched with ID[%03d]: %s", j, device_id); // Try to parse the current device_id string for VID:PID // We'll use that if we can't get anything better for (k = 0, l = 0; (k<strlen(device_id)) && (l<2); k++) { // The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\... if (device_id[k] == '\\') post_backslash = TRUE; if (!post_backslash) continue; if (device_id[k] == '_') { props.pid = (uint16_t)strtoul(&device_id[k + 1], NULL, 16); if (l++ == 0) props.vid = props.pid; } } if (props.vid != 0) method_str = "[ID]"; // If the hash didn't match a populated string in dev_if_path[] (htab_devid.table[j].data > 0), // we might have an extra vendor driver in between (e.g. "ASUS USB 3.0 Boost Storage Driver" // for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods) // so try to see if we can match the grandparent. if ( ((uintptr_t)htab_devid.table[j].data == 0) && (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS) && (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) { device_id = str; method_str = "[GP]"; ToUpper(device_id); j = htab_hash(device_id, &htab_devid); uuprintf(" Matched with (GP) ID[%03d]: %s", j, device_id); } if ((uintptr_t)htab_devid.table[j].data > 0) { uuprintf(" Matched with Hub[%d]: '%s'", (uintptr_t)htab_devid.table[j].data, dev_if_path.String[(uintptr_t)htab_devid.table[j].data]); if (GetUSBProperties(dev_if_path.String[(uintptr_t)htab_devid.table[j].data], device_id, &props)) { method_str = ""; hub_path = dev_if_path.String[(uintptr_t)htab_devid.table[j].data]; } #ifdef FORCED_DEVICE props.vid = FORCED_VID; props.pid = FORCED_PID; static_strcpy(buffer, FORCED_NAME); #endif } break; } } if (props.is_VHD) { uprintf("Found VHD device '%s'", buffer); } else if ((props.is_CARD) && ((!props.is_USB) || ((props.vid == 0) && (props.pid == 0)))) { uprintf("Found card reader device '%s'", buffer); } else if ((!props.is_USB) && (!props.is_UASP) && (props.is_Removable)) { if (!list_non_usb_removable_drives) { uprintf("Found non-USB removable device '%s' => Eliminated", buffer); uuprintf("If you *REALLY* need, you can enable listing of this device with <Ctrl><Alt><F>"); continue; } uprintf("Found non-USB removable device '%s'", buffer); } else { if ((props.vid == 0) && (props.pid == 0)) { if (!props.is_USB) { // If we have a non removable SCSI drive and couldn't get a VID:PID, // we are most likely dealing with a system drive => eliminate it! uuprintf("Found non-USB non-removable device '%s' => Eliminated", buffer); continue; } static_strcpy(str, "????:????"); // Couldn't figure VID:PID } else { static_sprintf(str, "%04X:%04X", props.vid, props.pid); } if (props.speed >= USB_SPEED_MAX) props.speed = 0; uprintf("Found %s%s%s device '%s' (%s) %s\n", props.is_UASP?"UAS (":"", usb_speed_name[props.speed], props.is_UASP?")":"", buffer, str, method_str); if (props.is_LowerSpeed) uprintf("NOTE: This device is an USB 3.0 device operating at lower speed..."); } devint_data.cbSize = sizeof(devint_data); hDrive = INVALID_HANDLE_VALUE; devint_detail_data = NULL; for (j=0; ;j++) { safe_closehandle(hDrive); safe_free(devint_detail_data); if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_DISK, j, &devint_data)) { if(GetLastError() != ERROR_NO_MORE_ITEMS) { uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString()); } else { uprintf("A device was eliminated because it didn't report itself as a disk\n"); } break; } if (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size); if (devint_detail_data == NULL) { uprintf("Unable to allocate data for SP_DEVICE_INTERFACE_DETAIL_DATA\n"); continue; } devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); } else { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) failed: %s\n", WindowsErrorString()); continue; } } if (devint_detail_data == NULL) { uprintf("SetupDiGetDeviceInterfaceDetail (dummy) - no data was allocated\n"); continue; } if(!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { uprintf("SetupDiGetDeviceInterfaceDetail (actual) failed: %s\n", WindowsErrorString()); continue; } hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDrive == INVALID_HANDLE_VALUE) { uprintf("Could not open '%s': %s\n", devint_detail_data->DevicePath, WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, devint_detail_data->DevicePath); if (drive_number < 0) continue; drive_index = drive_number + DRIVE_INDEX_MIN; if (!IsMediaPresent(drive_index)) { uprintf("Device eliminated because it appears to contain no media\n"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } if (GetDriveSize(drive_index) < (MIN_DRIVE_SIZE*MB)) { uprintf("Device eliminated because it is smaller than %d MB\n", MIN_DRIVE_SIZE); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } if (GetDriveLabel(drive_index, drive_letters, &label)) { if ((props.is_SCSI) && (!props.is_UASP) && (!props.is_VHD)) { if (!props.is_Removable) { // Non removables should have been eliminated above, but since we // are potentially dealing with system drives, better safe than sorry safe_closehandle(hDrive); safe_free(devint_detail_data); break; } if (!list_non_usb_removable_drives) { // Go over the mounted partitions and find if GetDriveType() says they are // removable. If they are not removable, don't allow the drive to be listed for (p = drive_letters; *p; p++) { drive_name[0] = *p; if (GetDriveTypeA(drive_name) != DRIVE_REMOVABLE) break; } if (*p) { uprintf("Device eliminated because it contains a mounted partition that is set as non-removable"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } } } if ((!enable_HDDs) && (!props.is_VHD) && (!props.is_CARD) && ((score = IsHDD(drive_index, (uint16_t)props.vid, (uint16_t)props.pid, buffer)) > 0)) { uprintf("Device eliminated because it was detected as a Hard Drive (score %d > 0)", score); if (!list_non_usb_removable_drives) uprintf("If this device is not a Hard Drive, please e-mail the author of this application"); uprintf("NOTE: You can enable the listing of Hard Drives under 'advanced drive properties'"); safe_closehandle(hDrive); safe_free(devint_detail_data); break; } // The empty string is returned for drives that don't have any volumes assigned if (drive_letters[0] == 0) { entry = lmprintf(MSG_046, label, drive_number, SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units)); } else { // Find the UEFI:TOGO partition(s) (and eliminate them form our listing) for (k=0; drive_letters[k]; k++) { uefi_togo_check[0] = drive_letters[k]; if (PathFileExistsA(uefi_togo_check)) { for (l=k; drive_letters[l]; l++) drive_letters[l] = drive_letters[l+1]; k--; } } // We have multiple volumes assigned to the same device (multiple partitions) // If that is the case, use "Multiple Volumes" instead of the label static_strcpy(entry_msg, (((drive_letters[0] != 0) && (drive_letters[1] != 0))? lmprintf(MSG_047):label)); for (k=0, remove_drive=0; drive_letters[k] && (!remove_drive); k++) { // Append all the drive letters we detected letter_name[2] = drive_letters[k]; if (right_to_left_mode) static_strcat(entry_msg, RIGHT_TO_LEFT_MARK); static_strcat(entry_msg, letter_name); if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) remove_drive = 1; if (drive_letters[k] == (PathGetDriveNumberU(system_dir) + 'A')) remove_drive = 2; } // Make sure that we don't list any drive that should not be listed if (remove_drive) { uprintf("Removing %C: from the list: This is the %s!", drive_letters[--k], (remove_drive==1)?"disk from which " APPLICATION_NAME " is running":"system disk"); 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, TRUE); StrArrayAdd(&DriveLabel, label, TRUE); if ((hub_path != NULL) && (StrArrayAdd(&DriveHub, hub_path, TRUE) >= 0)) DrivePort[DriveHub.Index - 1] = props.port; 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); r = TRUE; out: // Set 'Start' as the selected button, so that tab selection works SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDC_START), TRUE); safe_free(devid_list); StrArrayDestroy(&dev_if_path); htab_destroy(&htab_devid); return r; }
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) { size_t i, size; int j; uint16_t sl_version; FILE* fd; int r = 1; iso9660_t* p_iso = NULL; udf_t* p_udf = NULL; udf_dirent_t* p_udf_root; char *tmp, *buf, *ext; char path[MAX_PATH], path2[16]; const char* basedir[] = { "i386", "minint" }; const char* tmp_sif = ".\\txtsetup.sif~"; iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; if ((!enable_iso) || (src_iso == NULL) || (dest_dir == NULL)) return FALSE; scan_only = scan; cdio_log_set_handler(log_handler); psz_extract_dir = dest_dir; // Change progress style to marquee for scanning if (scan_only) { SendMessage(hMainDialog, UM_PROGRESS_INIT, PBS_MARQUEE, 0); total_blocks = 0; memset(&iso_report, 0, sizeof(iso_report)); has_ldlinux_c32 = FALSE; // String array of all isolinux/syslinux locations StrArrayCreate(&config_path, 8); StrArrayCreate(&isolinux_path, 8); PrintInfo(0, MSG_202); } else { uprintf("Extracting files...\n"); IGNORE_RETVAL(_chdirU(app_dir)); PrintInfo(0, MSG_231); if (total_blocks == 0) { uprintf("Error: ISO has not been properly scanned.\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_SCAN); goto out; } nb_blocks = 0; iso_blocking_status = 0; } /* First try to open as UDF - fallback to ISO if it failed */ p_udf = udf_open(src_iso); if (p_udf == NULL) goto try_iso; uprintf("Disc image is an UDF image\n"); p_udf_root = udf_get_root(p_udf, true, 0); if (p_udf_root == NULL) { uprintf("Could not locate UDF root directory\n"); goto out; } if (scan_only) { if (udf_get_logical_volume_id(p_udf, iso_report.label, sizeof(iso_report.label)) <= 0) iso_report.label[0] = 0; } r = udf_extract_files(p_udf, p_udf_root, ""); goto out; try_iso: // Perform our first scan with Joliet disabled (if Rock Ridge is enabled), so that we can find if // there exists a Rock Ridge file with a name > 64 chars or if there are symlinks. If that is the // case then we also disable Joliet during the extract phase. if ((!enable_joliet) || (enable_rockridge && (scan_only || iso_report.has_long_filename || iso_report.has_symlinks))) { iso_extension_mask &= ~ISO_EXTENSION_JOLIET; } if (!enable_rockridge) { iso_extension_mask &= ~ISO_EXTENSION_ROCK_RIDGE; } p_iso = iso9660_open_ext(src_iso, iso_extension_mask); if (p_iso == NULL) { uprintf("'%s' doesn't look like an ISO image\n", src_iso); r = 1; goto out; } uprintf("Disc image is an ISO9660 image\n"); i_joliet_level = iso9660_ifs_get_joliet_level(p_iso); if (scan_only) { if (iso9660_ifs_get_volume_id(p_iso, &tmp)) { safe_strcpy(iso_report.label, sizeof(iso_report.label), tmp); safe_free(tmp); } else iso_report.label[0] = 0; } else { if (iso_extension_mask & (ISO_EXTENSION_JOLIET|ISO_EXTENSION_ROCK_RIDGE)) uprintf("This image will be extracted using %s extensions (if present)", (iso_extension_mask & ISO_EXTENSION_JOLIET)?"Joliet":"Rock Ridge"); else uprintf("This image will not be extracted using any ISO extensions"); } r = iso_extract_files(p_iso, ""); out: iso_blocking_status = -1; if (scan_only) { // Remove trailing spaces from the label for (j=(int)safe_strlen(iso_report.label)-1; ((j>=0)&&(isspaceU(iso_report.label[j]))); j--) iso_report.label[j] = 0; // We use the fact that UDF_BLOCKSIZE and ISO_BLOCKSIZE are the same here iso_report.projected_size = total_blocks * ISO_BLOCKSIZE; // We will link the existing isolinux.cfg from a syslinux.cfg we create // If multiple config files exist, choose the one with the shortest path // (so that a '/syslinux.cfg' is preferred over a '/isolinux/isolinux.cfg') if (!IsStrArrayEmpty(config_path)) { // Set the iso_report.cfg_path string to maximum length, so that we don't have to // do a special case for StrArray entry 0. memset(iso_report.cfg_path, '_', sizeof(iso_report.cfg_path)-1); iso_report.cfg_path[sizeof(iso_report.cfg_path)-1] = 0; for (i=0; i<config_path.Index; i++) { // OpenSuse based Live image have a /syslinux.cfg that doesn't work, so we enforce // the use of the one in '/boot/[i386|x86_64]/loader/isolinux.cfg' if present. // Note that, because the openSuse live script are not designed to handle anything but // an ISO9660 filesystem for the live device, this still won't allow for proper boot. // See https://github.com/openSUSE/kiwi/issues/354 if ( (_stricmp(config_path.String[i], "/boot/i386/loader/isolinux.cfg") == 0) || (_stricmp(config_path.String[i], "/boot/x86_64/loader/isolinux.cfg") == 0)) { safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[i]); iso_report.needs_syslinux_overwrite = TRUE; break; } // Tails uses an '/EFI/BOOT/isolinux.cfg' along with a '/isolinux/isolinux.cfg' // which are the exact same length. However, only the /isolinux one will work, // so for now, at equal length, always pick the latest. // We may have to revisit this and prefer a path that contains '/isolinux' if // this hack is not enough for other images. if (safe_strlen(iso_report.cfg_path) >= safe_strlen(config_path.String[i])) safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[i]); } uprintf("Will use '%s' for Syslinux\n", iso_report.cfg_path); // Extract all of the isolinux.bin files we found to identify their versions for (i=0; i<isolinux_path.Index; i++) { size = (size_t)ExtractISOFile(src_iso, isolinux_path.String[i], dot_isolinux_bin, FILE_ATTRIBUTE_NORMAL); if (size == 0) { uprintf("Could not access %s\n", isolinux_path.String[i]); } else { buf = (char*)calloc(size, 1); if (buf == NULL) break; fd = fopen(dot_isolinux_bin, "rb"); if (fd == NULL) { free(buf); continue; } fread(buf, 1, size, fd); fclose(fd); sl_version = GetSyslinuxVersion(buf, size, &ext); if (iso_report.sl_version == 0) { safe_strcpy(iso_report.sl_version_ext, sizeof(iso_report.sl_version_ext), ext); iso_report.sl_version = sl_version; j = (int)i; } else if ((iso_report.sl_version != sl_version) || (safe_strcmp(iso_report.sl_version_ext, ext) != 0)) { uprintf("Found conflicting %s versions:\n '%s' (%d.%02d%s) vs '%s' (%d.%02d%s)\n", isolinux_bin, isolinux_path.String[j], SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version), iso_report.sl_version_ext, isolinux_path.String[i], SL_MAJOR(sl_version), SL_MINOR(sl_version), ext); } free(buf); _unlink(dot_isolinux_bin); } } if (iso_report.sl_version != 0) { static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Detected Isolinux version: %s%s (from '%s')", iso_report.sl_version_str, iso_report.sl_version_ext, isolinux_path.String[j]); if ( (has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) < 5)) || (!has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) >= 5)) ) uprintf("Warning: Conflict between Isolinux version and the presence of ldlinux.c32...\n"); } else { // Couldn't find a version from isolinux.bin. Force set to the versions we embed iso_report.sl_version = embedded_sl_version[has_ldlinux_c32?1:0]; static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Warning: Could not detect Isolinux version - Forcing to %s (embedded)", iso_report.sl_version_str); } } if (IS_WINPE(iso_report.winpe)) { // In case we have a WinPE 1.x based iso, we extract and parse txtsetup.sif // during scan, to see if /minint was provided for OsLoadOptions, as it decides // whether we should use 0x80 or 0x81 as the disk ID in the MBR safe_sprintf(path, sizeof(path), "/%s/txtsetup.sif", basedir[((iso_report.winpe&WINPE_I386) == WINPE_I386)?0:1]); ExtractISOFile(src_iso, path, tmp_sif, FILE_ATTRIBUTE_NORMAL); tmp = get_token_data_file("OsLoadOptions", tmp_sif); if (tmp != NULL) { for (i=0; i<strlen(tmp); i++) tmp[i] = (char)tolower(tmp[i]); uprintf("Checking txtsetup.sif:\n OsLoadOptions = %s\n", tmp); iso_report.uses_minint = (strstr(tmp, "/minint") != NULL); } _unlink(tmp_sif); safe_free(tmp); } if (iso_report.has_grub2) { // In case we have a GRUB2 based iso, we extract boot/grub/i386-pc/normal.mod to parse its version iso_report.grub2_version[0] = 0; if ((GetTempPathU(sizeof(path), path) != 0) && (GetTempFileNameU(path, APPLICATION_NAME, 0, path) != 0)) { size = (size_t)ExtractISOFile(src_iso, "boot/grub/i386-pc/normal.mod", path, FILE_ATTRIBUTE_NORMAL); buf = (char*)calloc(size, 1); fd = fopen(path, "rb"); if ((size == 0) || (buf == NULL) || (fd == NULL)) { uprintf("Could not read Grub version from 'boot/grub/i386-pc/normal.mod'"); } else { fread(buf, 1, size, fd); fclose(fd); GetGrubVersion(buf, size); } free(buf); _unlink(path); } if (iso_report.grub2_version[0] != 0) uprintf("Detected Grub version: %s", iso_report.grub2_version); else { uprintf("Could not detect Grub version"); iso_report.has_grub2 = FALSE; } } StrArrayDestroy(&config_path); StrArrayDestroy(&isolinux_path); SendMessage(hMainDialog, UM_PROGRESS_EXIT, 0, 0); } else if (HAS_SYSLINUX(iso_report)) { safe_sprintf(path, sizeof(path), "%s\\syslinux.cfg", dest_dir); // Create a /syslinux.cfg (if none exists) that points to the existing isolinux cfg fd = fopen(path, "r"); if (fd != NULL && iso_report.needs_syslinux_overwrite) { fclose(fd); fd = NULL; safe_sprintf(path2, sizeof(path2), "%s\\syslinux.org", dest_dir); uprintf("Renaming: %s ⇨ %s", path, path2); IGNORE_RETVAL(rename(path, path2)); } if (fd == NULL) { fd = fopen(path, "w"); // No "/syslinux.cfg" => create a new one if (fd == NULL) { uprintf("Unable to create %s - booting from USB will not work\n", path); r = 1; } else { fprintf(fd, "DEFAULT loadconfig\n\nLABEL loadconfig\n CONFIG %s\n", iso_report.cfg_path); for (i=safe_strlen(iso_report.cfg_path); (i>0)&&(iso_report.cfg_path[i]!='/'); i--); if (i>0) { iso_report.cfg_path[i] = 0; fprintf(fd, " APPEND %s/\n", iso_report.cfg_path); iso_report.cfg_path[i] = '/'; } uprintf("Created: %s\n", path); } } if (fd != NULL) fclose(fd); } if (p_iso != NULL) iso9660_close(p_iso); if (p_udf != NULL) udf_close(p_udf); if ((r != 0) && (FormatStatus == 0)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR((scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT)); return (r == 0); }
// Returns 0 on success, nonzero on error static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; EXTRACT_PROPS props; BOOL r, is_identical; int i_length; size_t i; char tmp[128], *psz_fullpath = NULL, *psz_sanpath = NULL; const char* psz_basename; udf_dirent_t *p_udf_dirent2; uint8_t buf[UDF_BLOCKSIZE]; int64_t i_read, i_file_length; if ((p_udf_dirent == NULL) || (psz_path == NULL)) return 1; while ((p_udf_dirent = udf_readdir(p_udf_dirent)) != NULL) { if (FormatStatus) goto out; psz_basename = udf_get_filename(p_udf_dirent); if (strlen(psz_basename) == 0) continue; i_length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir) + 24); psz_fullpath = (char*)calloc(sizeof(char), i_length); if (psz_fullpath == NULL) { uprintf("Error allocating file name\n"); goto out; } i_length = _snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename); if (i_length < 0) { goto out; } if (udf_is_dir(p_udf_dirent)) { if (!scan_only) { psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); IGNORE_RETVAL(_mkdirU(psz_sanpath)); safe_free(psz_sanpath); } p_udf_dirent2 = udf_opendir(p_udf_dirent); if (p_udf_dirent2 != NULL) { if (udf_extract_files(p_udf, p_udf_dirent2, &psz_fullpath[strlen(psz_extract_dir)])) goto out; } } else { i_file_length = udf_get_file_length(p_udf_dirent); if (check_iso_props(psz_path, i_file_length, psz_basename, psz_fullpath, &props)) { safe_free(psz_fullpath); continue; } print_extracted_file(psz_fullpath, i_file_length); for (i=0; i<NB_OLD_C32; i++) { if (props.is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileA(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version\n"); break; } uprintf(" Could not replace file: %s\n", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); if (!is_identical) uprintf(" File name sanitized to '%s'\n", psz_sanpath); file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s\n", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_sanpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else while (i_file_length > 0) { if (FormatStatus) goto out; memset(buf, 0, UDF_BLOCKSIZE); i_read = udf_read_block(p_udf_dirent, buf, 1); if (i_read < 0) { uprintf(" Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]); goto out; } buf_size = (DWORD)MIN(i_file_length, i_read); for (i=0; i<WRITE_RETRIES; i++) { ISO_BLOCKING(r = WriteFile(file_handle, buf, buf_size, &wr_size, NULL)); if ((!r) || (buf_size != wr_size)) { uprintf(" Error writing file: %s", WindowsErrorString()); if (i < WRITE_RETRIES-1) uprintf(" RETRYING...\n"); } else { break; } } if (i >= WRITE_RETRIES) goto out; i_file_length -= i_read; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } // If you have a fast USB 3.0 device, the default Windows buffering does an // excellent job at compensating for our small blocks read/writes to max out the // device's bandwidth. // The drawback however is with cancellation. With a large file, CloseHandle() // may take forever to complete and is not interruptible. We try to detect this. ISO_BLOCKING(safe_closehandle(file_handle)); if (props.is_syslinux_cfg || props.is_grub_cfg) fix_config(psz_sanpath, psz_path, psz_basename, &props); safe_free(psz_sanpath); } safe_free(psz_fullpath); } return 0; out: if (p_udf_dirent != NULL) udf_dirent_free(p_udf_dirent); ISO_BLOCKING(safe_closehandle(file_handle)); safe_free(psz_fullpath); return 1; }
/* * Scan and set ISO properties * Returns true if the the current file does not need to be processed further */ static BOOL check_iso_props(const char* psz_dirname, int64_t i_file_length, const char* psz_basename, const char* psz_fullpath, EXTRACT_PROPS *props) { size_t i, j; // Check for an isolinux/syslinux config file anywhere memset(props, 0, sizeof(EXTRACT_PROPS)); for (i=0; i<ARRAYSIZE(syslinux_cfg); i++) { if (safe_stricmp(psz_basename, syslinux_cfg[i]) == 0) { props->is_syslinux_cfg = TRUE; if ((scan_only) && (i == 1) && (safe_stricmp(psz_dirname, efi_dirname) == 0)) iso_report.has_efi_syslinux = TRUE; } } // Check for an old incompatible c32 file anywhere for (i=0; i<NB_OLD_C32; i++) { if ((safe_stricmp(psz_basename, old_c32_name[i]) == 0) && (i_file_length <= old_c32_threshold[i])) props->is_old_c32[i] = TRUE; } // Check for the Grub config file if ((safe_stricmp(psz_dirname, grub_dirname) == 0) && (safe_stricmp(psz_basename, grub_cfg) == 0)) { if (scan_only) iso_report.has_grub2 = TRUE; else props->is_grub_cfg = TRUE; } if (scan_only) { // Check for a syslinux v5.0+ file anywhere if (safe_stricmp(psz_basename, ldlinux_c32) == 0) { has_ldlinux_c32 = TRUE; } // Check for various files in root (psz_dirname = "") if (*psz_dirname == 0) { if (safe_strnicmp(psz_basename, bootmgr_efi_name, safe_strlen(bootmgr_efi_name)-5) == 0) { iso_report.has_bootmgr = TRUE; } if (safe_stricmp(psz_basename, grldr_name) == 0) { iso_report.has_grub4dos = TRUE; } if (safe_stricmp(psz_basename, kolibri_name) == 0) { iso_report.has_kolibrios = TRUE; } if (safe_stricmp(psz_basename, bootmgr_efi_name) == 0) { iso_report.has_efi |= 1; } } // Check for ReactOS' setupldr.sys anywhere if ((iso_report.reactos_path[0] == 0) && (safe_stricmp(psz_basename, reactos_name) == 0)) safe_strcpy(iso_report.reactos_path, sizeof(iso_report.reactos_path), psz_fullpath); // Check for the EFI boot entries if (safe_stricmp(psz_dirname, efi_dirname) == 0) { for (i=0; i<ARRAYSIZE(efi_bootname); i++) if (safe_stricmp(psz_basename, efi_bootname[i]) == 0) iso_report.has_efi |= (2<<i); // start at 2 since "bootmgr.efi" is bit 0 } // Check for "install.wim" or "install.swm" in "/sources" if (safe_stricmp(psz_dirname, install_wim_path) == 0) { for (i=0; i<ARRAYSIZE(install_wim_name); i++) if (safe_stricmp(psz_basename, install_wim_name[i]) == 0) static_sprintf(iso_report.install_wim_path, "?:\\%s\\%s", &install_wim_path[1], install_wim_name[i]); } // Check for PE (XP) specific files in "/i386" or "/minint" for (i=0; i<ARRAYSIZE(pe_dirname); i++) if (safe_stricmp(psz_dirname, pe_dirname[i]) == 0) for (j=0; j<ARRAYSIZE(pe_file); j++) if (safe_stricmp(psz_basename, pe_file[j]) == 0) iso_report.winpe |= (1<<i)<<(ARRAYSIZE(pe_dirname)*j); if (props->is_syslinux_cfg) { // Maintain a list of all the isolinux/syslinux configs identified so far StrArrayAdd(&config_path, psz_fullpath); } if (safe_stricmp(psz_basename, isolinux_bin) == 0) { // Maintain a list of all the isolinux.bin files found StrArrayAdd(&isolinux_path, psz_fullpath); } for (i=0; i<NB_OLD_C32; i++) { if (props->is_old_c32[i]) iso_report.has_old_c32[i] = TRUE; } if (i_file_length >= FOUR_GIGABYTES) iso_report.has_4GB_file = TRUE; // Compute projected size needed total_blocks += i_file_length/UDF_BLOCKSIZE; // NB: ISO_BLOCKSIZE = UDF_BLOCKSIZE if ((i_file_length != 0) && (i_file_length%ISO_BLOCKSIZE == 0)) // total_blocks++; return TRUE; } // In case there's an ldlinux.sys on the ISO, prevent it from overwriting ours if ((*psz_dirname == 0) && (safe_strcmp(psz_basename, ldlinux_name) == 0)) { uprintf("skipping % file from ISO image\n", ldlinux_name); return TRUE; } return FALSE; }
// Returns 0 on success, nonzero on error static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; EXTRACT_PROPS props; BOOL is_symlink, is_identical; int i_length, r = 1; char tmp[128], psz_fullpath[MAX_PATH], *psz_basename, *psz_sanpath; const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)]; unsigned char buf[ISO_BLOCKSIZE]; CdioListNode_t* p_entnode; iso9660_stat_t *p_statbuf; CdioList_t* p_entlist; size_t i; lsn_t lsn; int64_t i_file_length; if ((p_iso == NULL) || (psz_path == NULL)) return 1; i_length = _snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path); if (i_length < 0) return 1; psz_basename = &psz_fullpath[i_length]; p_entlist = iso9660_ifs_readdir(p_iso, psz_path); if (!p_entlist) { uprintf("Could not access directory %s", psz_path); return 1; } _CDIO_LIST_FOREACH(p_entnode, p_entlist) { if (FormatStatus) goto out; p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode); // Eliminate . and .. entries if ( (strcmp(p_statbuf->filename, ".") == 0) || (strcmp(p_statbuf->filename, "..") == 0) ) continue; // Rock Ridge requires an exception is_symlink = FALSE; if ((p_statbuf->rr.b3_rock == yep) && enable_rockridge) { safe_strcpy(psz_basename, sizeof(psz_fullpath)-i_length-1, p_statbuf->filename); if (safe_strlen(p_statbuf->filename) > 64) img_report.has_long_filename = TRUE; // libcdio has a memleak for Rock Ridge symlinks. It doesn't look like there's an easy fix there as // a generic list that's unaware of RR extensions is being used, so we prevent that memleak ourselves is_symlink = (p_statbuf->rr.psz_symlink != NULL); if (is_symlink) img_report.has_symlinks = TRUE; if (scan_only) safe_free(p_statbuf->rr.psz_symlink); } else { iso9660_name_translate_ext(p_statbuf->filename, psz_basename, i_joliet_level); } if (p_statbuf->type == _STAT_DIR) { if (!scan_only) { psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); IGNORE_RETVAL(_mkdirU(psz_sanpath)); if (preserve_timestamps) { LPFILETIME ft = to_filetime(mktime(&p_statbuf->tm)); set_directory_timestamp(psz_sanpath, ft, ft, ft); } safe_free(psz_sanpath); } if (iso_extract_files(p_iso, psz_iso_name)) goto out; } else { i_file_length = p_statbuf->size; if (check_iso_props(psz_path, i_file_length, psz_basename, psz_fullpath, &props)) { continue; } print_extracted_file(psz_fullpath, i_file_length); for (i=0; i<NB_OLD_C32; i++) { if (props.is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileU(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version %s", IsFileInDB(tmp)?"✓":"✗"); break; } uprintf(" Could not replace file: %s", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); if (!is_identical) uprintf(" File name sanitized to '%s'", psz_sanpath); if (is_symlink) { if (i_file_length == 0) uprintf(" Ignoring Rock Ridge symbolic link to '%s'", p_statbuf->rr.psz_symlink); safe_free(p_statbuf->rr.psz_symlink); } file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_sanpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else for (i=0; i_file_length>0; i++) { if (FormatStatus) goto out; memset(buf, 0, ISO_BLOCKSIZE); lsn = p_statbuf->lsn + (lsn_t)i; if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) { uprintf(" Error reading ISO9660 file %s at LSN %lu", psz_iso_name, (long unsigned int)lsn); goto out; } buf_size = (DWORD)MIN(i_file_length, ISO_BLOCKSIZE); ISO_BLOCKING(r = WriteFileWithRetry(file_handle, buf, buf_size, &wr_size, WRITE_RETRIES)); if (!r) { uprintf(" Error writing file: %s", WindowsErrorString()); goto out; } i_file_length -= ISO_BLOCKSIZE; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } if (preserve_timestamps) { LPFILETIME ft = to_filetime(mktime(&p_statbuf->tm)); if (!SetFileTime(file_handle, ft, ft, ft)) uprintf(" Could not set timestamp: %s", WindowsErrorString()); } ISO_BLOCKING(safe_closehandle(file_handle)); if (props.is_syslinux_cfg || props.is_grub_cfg) fix_config(psz_sanpath, psz_path, psz_basename, &props); safe_free(psz_sanpath); } } r = 0; out: ISO_BLOCKING(safe_closehandle(file_handle)); _cdio_list_free(p_entlist, true); return r; }
static DWORD WINAPI SearchProcessThread(LPVOID param) { const char *access_rights_str[8] = { "n", "r", "w", "rw", "x", "rx", "wx", "rwx" }; char tmp[MAX_PATH]; NTSTATUS status = STATUS_SUCCESS; PSYSTEM_HANDLE_INFORMATION_EX handles = NULL; POBJECT_NAME_INFORMATION buffer = NULL; ULONG_PTR i; ULONG_PTR pid[2]; ULONG_PTR last_access_denied_pid = 0; ULONG bufferSize; USHORT wHandleNameLen; WCHAR *wHandleName = NULL; HANDLE dupHandle = NULL; HANDLE processHandle = NULL; BOOLEAN bFound = FALSE, bGotExePath, verbose = !_bQuiet; ULONG access_rights = 0; DWORD size; char exe_path[MAX_PATH] = { 0 }; wchar_t wexe_path[MAX_PATH]; int cur_pid; PF_INIT_OR_SET_STATUS(NtQueryObject, Ntdll); PF_INIT_OR_SET_STATUS(NtDuplicateObject, NtDll); PF_INIT_OR_SET_STATUS(NtClose, NtDll); StrArrayClear(&BlockingProcess); if (NT_SUCCESS(status)) status = PhCreateHeap(); if (NT_SUCCESS(status)) status = PhEnumHandlesEx(&handles); if (!NT_SUCCESS(status)) { uprintf("Warning: Could not enumerate process handles: %s", NtStatusError(status)); goto out; } pid[0] = (ULONG_PTR)0; cur_pid = 1; wHandleName = utf8_to_wchar(_HandleName); wHandleNameLen = (USHORT)wcslen(wHandleName); bufferSize = 0x200; buffer = PhAllocate(bufferSize); if (buffer == NULL) goto out; for (i = 0; ; i++) { ULONG attempts = 8; PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = (i < handles->NumberOfHandles) ? &handles->Handles[i] : NULL; if ((dupHandle != NULL) && (processHandle != NtCurrentProcess())) { pfNtClose(dupHandle); dupHandle = NULL; } // Update the current handle's process PID and compare against last // Note: Be careful about not trying to overflow our list! pid[cur_pid] = (handleInfo != NULL) ? handleInfo->UniqueProcessId : -1; if (pid[0] != pid[1]) { cur_pid = (cur_pid + 1) % 2; // If we're switching process and found a match, print it if (bFound) { vuprintf("● '%s' (pid: %ld, access: %s)", exe_path, pid[cur_pid], access_rights_str[access_rights & 0x7]); static_sprintf(tmp, "● %s (%s)", exe_path, access_rights_str[access_rights & 0x7]); StrArrayAdd(&BlockingProcess, tmp, TRUE); bFound = FALSE; access_rights = 0; } // Close the previous handle if (processHandle != NULL) { if (processHandle != NtCurrentProcess()) pfNtClose(processHandle); processHandle = NULL; } } CHECK_FOR_USER_CANCEL; // Exit loop condition if (i >= handles->NumberOfHandles) break; // Don't bother with processes we can't access if (handleInfo->UniqueProcessId == last_access_denied_pid) continue; // Filter out handles that aren't opened with Read (bit 0), Write (bit 1) or Execute (bit 5) access if ((handleInfo->GrantedAccess & 0x23) == 0) continue; // Open the process to which the handle we are after belongs, if not already opened if (pid[0] != pid[1]) { status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, (HANDLE)handleInfo->UniqueProcessId); // There exists some processes we can't access if (!NT_SUCCESS(status)) { uuprintf("SearchProcess: Could not open process %ld: %s", handleInfo->UniqueProcessId, NtStatusError(status)); processHandle = NULL; if (status == STATUS_ACCESS_DENIED) { last_access_denied_pid = handleInfo->UniqueProcessId; } continue; } } // Now duplicate this handle onto our own process, so that we can access its properties if (processHandle == NtCurrentProcess()) { if (_bIgnoreSelf) continue; dupHandle = (HANDLE)handleInfo->HandleValue; } else { status = pfNtDuplicateObject(processHandle, (HANDLE)handleInfo->HandleValue, NtCurrentProcess(), &dupHandle, 0, 0, 0); if (!NT_SUCCESS(status)) continue; } // Filter non-storage handles. We're not interested in them and they make NtQueryObject() freeze if (GetFileType(dupHandle) != FILE_TYPE_DISK) continue; // A loop is needed because the I/O subsystem likes to give us the wrong return lengths... do { ULONG returnSize; // TODO: We might potentially still need a timeout on ObjectName queries, as PH does... status = pfNtQueryObject(dupHandle, ObjectNameInformation, buffer, bufferSize, &returnSize); if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_BUFFER_TOO_SMALL) { uuprintf("SearchProcess: Realloc from %d to %d", bufferSize, returnSize); bufferSize = returnSize; PhFree(buffer); buffer = PhAllocate(bufferSize); } else { break; } } while (--attempts); if (!NT_SUCCESS(status)) { uuprintf("SearchProcess: NtQueryObject failed for handle %X of process %ld: %s", handleInfo->HandleValue, handleInfo->UniqueProcessId, NtStatusError(status)); continue; } // Don't bother comparing if we are looking for full match and the length is different if ((!_bPartialMatch) && (wHandleNameLen != buffer->Name.Length)) continue; // Likewise, if we are looking for a partial match and the current length is smaller if ((_bPartialMatch) && (wHandleNameLen > buffer->Name.Length)) continue; // Match against our target string if (wcsncmp(wHandleName, buffer->Name.Buffer, wHandleNameLen) != 0) continue; // If we are here, we have a process accessing our target! bFound = TRUE; // Keep a mask of all the access rights being used access_rights |= handleInfo->GrantedAccess; // The Executable bit is in a place we don't like => reposition it if (access_rights & 0x20) access_rights = (access_rights & 0x03) | 0x04; access_mask |= (BYTE) (access_rights & 0x7) + 0x80; // Bit 7 is always set if a process was found // If this is the very first process we find, print a header if (exe_path[0] == 0) vuprintf("WARNING: The following process(es) or service(s) are accessing %s:", _HandleName); // First, we try to get the executable path using GetModuleFileNameEx bGotExePath = (GetModuleFileNameExU(processHandle, 0, exe_path, MAX_PATH - 1) != 0); // The above may not work on Windows 7, so try QueryFullProcessImageName (Vista or later) if (!bGotExePath) { size = MAX_PATH; PF_INIT(QueryFullProcessImageNameW, kernel32); if ( (pfQueryFullProcessImageNameW != NULL) && (bGotExePath = pfQueryFullProcessImageNameW(processHandle, 0, wexe_path, &size)) ) wchar_to_utf8_no_alloc(wexe_path, exe_path, sizeof(exe_path)); } // Still nothing? Try GetProcessImageFileName. Note that GetProcessImageFileName uses // '\Device\Harddisk#\Partition#\' instead drive letters if (!bGotExePath) { bGotExePath = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0); if (bGotExePath) wchar_to_utf8_no_alloc(wexe_path, exe_path, sizeof(exe_path)); } // Complete failure => Just craft a default process name that includes the PID if (!bGotExePath) { safe_sprintf(exe_path, MAX_PATH, "Unknown_Process_%" PRIu64, (ULONGLONG)handleInfo->UniqueProcessId); } } out: if (exe_path[0] != 0) vuprintf("You should close these applications before attempting to reformat the drive."); else vuprintf("NOTE: Could not identify the process(es) or service(s) accessing %s", _HandleName); free(wHandleName); PhFree(buffer); PhFree(handles); PhDestroyHeap(); ExitThread(0); }
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) { size_t i, k, size; int j; uint16_t sl_version; FILE* fd; int r = 1; iso9660_t* p_iso = NULL; udf_t* p_udf = NULL; udf_dirent_t* p_udf_root; LONG progress_style; char *tmp, *buf; char path[MAX_PATH]; const char* basedir[] = { "i386", "minint" }; const char* tmp_sif = ".\\txtsetup.sif~"; const char ISOLINUX[] = { 'I', 'S', 'O', 'L', 'I', 'N', 'U', 'X', ' ' }; iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; if ((src_iso == NULL) || (dest_dir == NULL)) return FALSE; scan_only = scan; cdio_log_set_handler(log_handler); psz_extract_dir = dest_dir; progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE); if (scan_only) { total_blocks = 0; memset(&iso_report, 0, sizeof(iso_report)); has_ldlinux_c32 = FALSE; // String array of all isolinux/syslinux locations StrArrayCreate(&config_path, 8); StrArrayCreate(&isolinux_path, 8); // Change the Window title and static text SetWindowTextU(hISOProgressDlg, lmprintf(MSG_202)); SetWindowTextU(hISOFileName, lmprintf(MSG_202)); // Change progress style to marquee for scanning SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style | PBS_MARQUEE); SendMessage(hISOProgressBar, PBM_SETMARQUEE, TRUE, 0); } else { uprintf("Extracting files...\n"); IGNORE_RETVAL(_chdirU(app_dir)); SetWindowTextU(hISOProgressDlg, lmprintf(MSG_231)); if (total_blocks == 0) { uprintf("Error: ISO has not been properly scanned.\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_SCAN); goto out; } nb_blocks = 0; iso_blocking_status = 0; SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); } SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0); /* First try to open as UDF - fallback to ISO if it failed */ p_udf = udf_open(src_iso); if (p_udf == NULL) goto try_iso; uprintf("Disc image is an UDF image\n"); p_udf_root = udf_get_root(p_udf, true, 0); if (p_udf_root == NULL) { uprintf("Couldn't locate UDF root directory\n"); goto out; } if (scan_only) { if (udf_get_logical_volume_id(p_udf, iso_report.label, sizeof(iso_report.label)) <= 0) iso_report.label[0] = 0; } r = udf_extract_files(p_udf, p_udf_root, ""); goto out; try_iso: // Perform our first scan with Joliet disabled (if Rock Ridge is enabled), so that we can find if // there exists a Rock Ridge file with a name > 64 chars or if there are symlinks. If that is the // case then we also disable Joliet during the extract phase. if ((!enable_joliet) || (enable_rockridge && (scan_only || iso_report.has_long_filename || iso_report.has_symlinks))) { iso_extension_mask &= ~ISO_EXTENSION_JOLIET; } if (!enable_rockridge) { iso_extension_mask &= ~ISO_EXTENSION_ROCK_RIDGE; } p_iso = iso9660_open_ext(src_iso, iso_extension_mask); if (p_iso == NULL) { uprintf("Unable to open '%s' as an ISO image.\n", src_iso); r = 1; goto out; } uprintf("Disc image is an ISO9660 image\n"); i_joliet_level = iso9660_ifs_get_joliet_level(p_iso); if (scan_only) { if (iso9660_ifs_get_volume_id(p_iso, &tmp)) { safe_strcpy(iso_report.label, sizeof(iso_report.label), tmp); safe_free(tmp); } else iso_report.label[0] = 0; } else { if (iso_extension_mask & (ISO_EXTENSION_JOLIET|ISO_EXTENSION_ROCK_RIDGE)) uprintf("This image will be extracted using %s extensions (if present)", (iso_extension_mask & ISO_EXTENSION_JOLIET)?"Joliet":"Rock Ridge"); else uprintf("This image will not be extracted using any ISO extensions"); } r = iso_extract_files(p_iso, ""); out: iso_blocking_status = -1; if (scan_only) { // Remove trailing spaces from the label for (j=(int)safe_strlen(iso_report.label)-1; ((j>=0)&&(isspaceU(iso_report.label[j]))); j--) iso_report.label[j] = 0; // We use the fact that UDF_BLOCKSIZE and ISO_BLOCKSIZE are the same here iso_report.projected_size = total_blocks * ISO_BLOCKSIZE; // We will link the existing isolinux.cfg from a syslinux.cfg we create // If multiple config files exist, choose the one with the shortest path // (so that a '/syslinux.cfg' is preferred over a '/isolinux/isolinux.cfg') if (!IsStrArrayEmpty(config_path)) { safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[0]); for (i=1; i<config_path.Index; i++) { if (safe_strlen(iso_report.cfg_path) > safe_strlen(config_path.String[i])) safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[i]); } uprintf("Will use %s for Syslinux\n", iso_report.cfg_path); // Extract all of the isolinux.bin files we found to identify their versions for (i=0; i<isolinux_path.Index; i++) { size = (size_t)ExtractISOFile(src_iso, isolinux_path.String[i], dot_isolinux_bin); if (size == 0) { uprintf("Could not access %s\n", isolinux_path.String[i]); } else { buf = (char*)calloc(size, 1); if (buf == NULL) break; fd = fopen(dot_isolinux_bin, "rb"); if (fd == NULL) { free(buf); continue; } fread(buf, 1, size, fd); fclose(fd); for (k=0; k<size-16; k++) { if (memcmp(&buf[k], ISOLINUX, sizeof(ISOLINUX)) == 0) { k += sizeof(ISOLINUX); sl_version = (((uint8_t)strtoul(&buf[k], &tmp, 10))<<8) + (uint8_t)strtoul(&tmp[1], NULL, 10); if (iso_report.sl_version == 0) { iso_report.sl_version = sl_version; j = (int)i; } else if (iso_report.sl_version != sl_version) { uprintf("Found conflicting %s versions:\n '%s' (%d.%02d) vs '%s' (%d.%02d)\n", isolinux_bin, isolinux_path.String[j], SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version), isolinux_path.String[i], SL_MAJOR(sl_version), SL_MINOR(sl_version)); } break; } } free(buf); _unlink(dot_isolinux_bin); } } if (iso_report.sl_version != 0) { static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Detected Isolinux version: %s (from '%s')", iso_report.sl_version_str, isolinux_path.String[j]); if ( (has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) < 5)) || (!has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) >= 5)) ) uprintf("Warning: Conflict between Isolinux version and the presence of ldlinux.c32...\n"); } else { // Couldn't find a version from isolinux.bin. Force set to the versions we embed iso_report.sl_version = embedded_sl_version[has_ldlinux_c32?1:0]; static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Warning: Could not detect Isolinux version - Forcing to %s (embedded)", iso_report.sl_version_str); } } if (IS_WINPE(iso_report.winpe)) { // In case we have a WinPE 1.x based iso, we extract and parse txtsetup.sif // during scan, to see if /minint was provided for OsLoadOptions, as it decides // whether we should use 0x80 or 0x81 as the disk ID in the MBR safe_sprintf(path, sizeof(path), "/%s/txtsetup.sif", basedir[((iso_report.winpe&WINPE_I386) == WINPE_I386)?0:1]); ExtractISOFile(src_iso, path, tmp_sif); tmp = get_token_data_file("OsLoadOptions", tmp_sif); if (tmp != NULL) { for (i=0; i<strlen(tmp); i++) tmp[i] = (char)tolower(tmp[i]); uprintf("Checking txtsetup.sif:\n OsLoadOptions = %s\n", tmp); iso_report.uses_minint = (strstr(tmp, "/minint") != NULL); } _unlink(tmp_sif); safe_free(tmp); } StrArrayDestroy(&config_path); StrArrayDestroy(&isolinux_path); } else if (HAS_SYSLINUX(iso_report)) { safe_sprintf(path, sizeof(path), "%s\\syslinux.cfg", dest_dir); // Create a /syslinux.cfg (if none exists) that points to the existing isolinux cfg fd = fopen(path, "r"); if (fd == NULL) { fd = fopen(path, "w"); // No "/syslinux.cfg" => create a new one if (fd == NULL) { uprintf("Unable to create %s - booting from USB will not work\n", path); r = 1; } else { fprintf(fd, "DEFAULT loadconfig\n\nLABEL loadconfig\n CONFIG %s\n", iso_report.cfg_path); for (i=safe_strlen(iso_report.cfg_path); (i>0)&&(iso_report.cfg_path[i]!='/'); i--); if (i>0) { iso_report.cfg_path[i] = 0; fprintf(fd, " APPEND %s/\n", iso_report.cfg_path); iso_report.cfg_path[i] = '/'; } uprintf("Created: %s\n", path); } } if (fd != NULL) fclose(fd); } SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); if (p_iso != NULL) iso9660_close(p_iso); if (p_udf != NULL) udf_close(p_udf); if ((r != 0) && (FormatStatus == 0)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR((scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT)); return (r == 0); }