diff --git a/src/dev.c b/src/dev.c index 0e20843f..ef25037c 100644 --- a/src/dev.c +++ b/src/dev.c @@ -146,6 +146,104 @@ static __inline BOOL IsRemovable(const char* buffer) } } +BOOL GetOpticalMedia(IMG_SAVE* img_save) +{ + static char str[MAX_PATH]; + static char label[33]; + int k; + BYTE geometry[256], *buffer = NULL; + PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; + DWORD i, j, size, datatype; + 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; + HANDLE hDrive = INVALID_HANDLE_VALUE; + LARGE_INTEGER li; + + dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_CDROM, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dev_info == INVALID_HANDLE_VALUE) { + uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString()); + return FALSE; + } + dev_info_data.cbSize = sizeof(dev_info_data); + for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { + memset(str, 0, sizeof(str)); + if (!SetupDiGetDeviceRegistryPropertyU(dev_info, &dev_info_data, SPDRP_FRIENDLYNAME, + &datatype, (LPBYTE)str, sizeof(str), &size)) { + uprintf("SetupDiGetDeviceRegistryProperty (Friendly Name) failed: %s\n", WindowsErrorString()); + safe_strcpy(str, sizeof(str), "Generic Optical Drive"); + } + uprintf("Found '%s' optical device", str); + devint_data.cbSize = sizeof(devint_data); + devint_detail_data = NULL; + for (j = 0; ; j++) { + safe_closehandle(hDrive); + safe_free(devint_detail_data); + safe_free(buffer); + + if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_CDROM, j, &devint_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString()); + } + 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; + } + + // Get the size of the inserted media (if any) + hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hDrive == INVALID_HANDLE_VALUE) + continue; + if (!DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + NULL, 0, geometry, sizeof(geometry), &size, NULL)) + continue; + // Rewritable media usually has a one sector + if (DiskGeometry->DiskSize.QuadPart <= 4096) + continue; + // Read the label directly, since it's a massive PITA to get it from Windows + li.QuadPart = 0x8000LL; + buffer = malloc(2048); + if ((buffer != NULL) && (SetFilePointerEx(hDrive, li, NULL, FILE_BEGIN)) && + ReadFile(hDrive, buffer, 2048, &size, NULL) && (size == 2048)) { + safe_strcpy(label, sizeof(label), (char*)&buffer[0x28]); + for (k = safe_strlen(label) - 1; (k >= 0) && (label[k] == 0x20); k--) + label[k] = 0; + img_save->Label = label; + } + safe_strcpy(str, sizeof(str), devint_detail_data->DevicePath); + img_save->DevicePath = str; + img_save->DeviceSize = DiskGeometry->DiskSize.QuadPart; + safe_closehandle(hDrive); + safe_free(devint_detail_data); + safe_free(buffer); + return TRUE; + } + } + return FALSE; +} + /* For debugging user reports of HDDs vs UFDs */ //#define FORCED_DEVICE #ifdef FORCED_DEVICE diff --git a/src/dev.h b/src/dev.h index a3787cb1..2b1841a1 100644 --- a/src/dev.h +++ b/src/dev.h @@ -176,6 +176,8 @@ typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { const GUID _GUID_DEVINTERFACE_DISK = { 0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b} }; +const GUID _GUID_DEVINTERFACE_CDROM = + { 0x53f56308L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b} }; const GUID _GUID_DEVINTERFACE_USB_HUB = { 0xf18a0e88L, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} }; diff --git a/src/format.c b/src/format.c index c1cfb92e..7861dc1f 100644 --- a/src/format.c +++ b/src/format.c @@ -1992,7 +1992,7 @@ DWORD WINAPI SaveImageThread(void* param) { BOOL s; DWORD rSize, wSize; - VHD_SAVE *vhd_save = (VHD_SAVE*)param; + IMG_SAVE *img_save = (IMG_SAVE*)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hDestImage = INVALID_HANDLE_VALUE; LARGE_INTEGER li; @@ -2002,39 +2002,57 @@ DWORD WINAPI SaveImageThread(void* param) PrintInfoDebug(0, MSG_225); LastRefresh = 0; - hPhysicalDrive = GetPhysicalHandle(vhd_save->DeviceNum, FALSE, TRUE); + switch (img_save->Type) { + case IMG_SAVE_TYPE_VHD: + hPhysicalDrive = GetPhysicalHandle(img_save->DeviceNum, FALSE, TRUE); + break; + case IMG_SAVE_TYPE_ISO: + hPhysicalDrive = CreateFileA(img_save->DevicePath, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + break; + default: + uprintf("invalid image type"); + } if (hPhysicalDrive == INVALID_HANDLE_VALUE) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } // Write an image file - // We poked the MBR and other stuff, so we need to rewind + // We may have poked the MBR and other stuff, so need to rewind li.QuadPart = 0; if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) uprintf("Warning: Unable to rewind device position - wrong data might be copied!"); - hDestImage = CreateFileU(vhd_save->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, + hDestImage = CreateFileU(img_save->ImagePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hDestImage == INVALID_HANDLE_VALUE) { - uprintf("Could not open image '%s': %s", vhd_save->path, WindowsErrorString()); + uprintf("Could not open image '%s': %s", img_save->ImagePath, WindowsErrorString()); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; goto out; } - uprintf("Saving to image '%s'...", vhd_save->path); - buffer = (uint8_t*)_mm_malloc(DD_BUFFER_SIZE, 16); + buffer = (uint8_t*)_mm_malloc(img_save->BufSize, 16); if (buffer == NULL) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY; uprintf("could not allocate buffer"); goto out; } + uprintf("Will use a buffer size of %s", SizeToHumanReadable(img_save->BufSize, FALSE, FALSE)); + uprintf("Saving to image '%s'...", img_save->ImagePath); + // Don't bother trying for something clever, using double buffering overlapped and whatnot: // With Windows' default optimizations, sync read + sync write for sequential operations // will be as fast, if not faster, than whatever async scheme you can come up with. for (wb = 0; ; wb += wSize) { + if (img_save->Type == IMG_SAVE_TYPE_ISO) { + // Optical drives do not appear to increment the sectors to read automatically + li.QuadPart = wb; + if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) + uprintf("Warning: Unable to set device position - wrong data might be copied!"); + } s = ReadFile(hPhysicalDrive, buffer, - (DWORD)MIN(DD_BUFFER_SIZE, SelectedDrive.DiskSize - wb), &rSize, NULL); + (DWORD)MIN(img_save->BufSize, img_save->DeviceSize - wb), &rSize, NULL); if (!s) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT; uprintf("read error: %s", WindowsErrorString()); @@ -2044,7 +2062,7 @@ DWORD WINAPI SaveImageThread(void* param) break; if (_GetTickCount64() > LastRefresh + MAX_REFRESH) { LastRefresh = _GetTickCount64(); - format_percent = (100.0f*wb)/(1.0f*SelectedDrive.DiskSize); + format_percent = (100.0f*wb)/(1.0f*img_save->DeviceSize); PrintInfo(0, MSG_261, format_percent); UpdateProgress(OP_FORMAT, format_percent); } @@ -2068,21 +2086,23 @@ DWORD WINAPI SaveImageThread(void* param) } if (i >= WRITE_RETRIES) goto out; } - if (wb != SelectedDrive.DiskSize) { - uprintf("Error: wrote %" PRIu64 " bytes, expected %" PRIu64, wb, SelectedDrive.DiskSize); + if (wb != img_save->DeviceSize) { + uprintf("Error: wrote %s, expected %s", SizeToHumanReadable(wb, FALSE, FALSE), + SizeToHumanReadable(img_save->DeviceSize, FALSE, FALSE)); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } - uprintf("%" PRIu64 " bytes written", wb); - uprintf("Appending VHD footer..."); - if (!AppendVHDFooter(vhd_save->path)) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; - goto out; + if (img_save->Type == IMG_SAVE_TYPE_VHD) { + uprintf("Appending VHD footer..."); + if (!AppendVHDFooter(img_save->ImagePath)) { + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; + goto out; + } } - uprintf("Done"); + uprintf("Operation complete (Wrote %s).", SizeToHumanReadable(wb, FALSE, FALSE)); out: - safe_free(vhd_save->path); + safe_free(img_save->ImagePath); safe_mm_free(buffer); safe_closehandle(hDestImage); safe_unlockclose(hPhysicalDrive); diff --git a/src/rufus.c b/src/rufus.c index d90f8b9a..4c1b7f81 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -960,7 +960,7 @@ static void DisplayISOProps(void) int i; uprintf("ISO label: '%s'", img_report.label); - uprintf(" Size: %" PRIu64 " bytes", img_report.projected_size); + uprintf(" Size: %s", SizeToHumanReadable( img_report.projected_size, FALSE, FALSE)); PRINT_ISO_PROP(img_report.has_4GB_file, " Has a >4GB file"); PRINT_ISO_PROP(img_report.has_long_filename, " Has a >64 chars filename"); PRINT_ISO_PROP(HAS_SYSLINUX(img_report), " Uses: Syslinux/Isolinux v%s", img_report.sl_version_str); @@ -1994,19 +1994,22 @@ void SetBoot(int fs, int tt) void SaveVHD(void) { - static VHD_SAVE vhd_save; + static IMG_SAVE img_save = { 0 }; char filename[128]; char path[MAX_PATH]; int DriveIndex = ComboBox_GetCurSel(hDeviceList); - EXT_DECL(vhd_ext, filename, __VA_GROUP__("*.vhd"), __VA_GROUP__("VHD File")); + EXT_DECL(img_ext, filename, __VA_GROUP__("*.vhd"), __VA_GROUP__(lmprintf(MSG_095))); ULARGE_INTEGER free_space; if (DriveIndex >= 0) safe_sprintf(filename, sizeof(filename), "%s.vhd", DriveLabel.String[DriveIndex]); if ((DriveIndex != CB_ERR) && (!format_op_in_progress) && (format_thid == NULL)) { - vhd_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex); - vhd_save.path = FileDialog(TRUE, NULL, &vhd_ext, 0); - if (vhd_save.path != NULL) { + img_save.Type = IMG_SAVE_TYPE_VHD; + img_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex); + img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); + img_save.BufSize = DD_BUFFER_SIZE; + img_save.DeviceSize = SelectedDrive.DiskSize; + if (img_save.ImagePath != NULL) { // Reset all progress bars SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0); SetTaskbarProgressState(TASKBAR_NORMAL); @@ -2015,14 +2018,14 @@ void SaveVHD(void) FormatStatus = 0; format_op_in_progress = TRUE; free_space.QuadPart = 0; - if ((GetVolumePathNameA(vhd_save.path, path, sizeof(path))) + if ((GetVolumePathNameA(img_save.ImagePath, path, sizeof(path))) && (GetDiskFreeSpaceExA(path, &free_space, NULL, NULL)) && ((LONGLONG)free_space.QuadPart > (SelectedDrive.DiskSize + 512))) { // Disable all controls except cancel EnableControls(FALSE); FormatStatus = 0; InitProgress(TRUE); - format_thid = CreateThread(NULL, 0, SaveImageThread, &vhd_save, 0, NULL); + format_thid = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); if (format_thid != NULL) { uprintf("\r\nSave to VHD operation started"); PrintInfo(0, -1); @@ -2030,25 +2033,22 @@ void SaveVHD(void) safe_sprintf(szTimer, sizeof(szTimer), "00:00:00"); SendMessageA(hStatus, SB_SETTEXTA, SBT_OWNERDRAW | SB_SECTION_RIGHT, (LPARAM)szTimer); SetTimer(hMainDialog, TID_APP_TIMER, 1000, ClockTimer); - } - else { + } else { uprintf("Unable to start VHD save thread"); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); - safe_free(vhd_save.path); + safe_free(img_save.ImagePath); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); format_op_in_progress = FALSE; } - } - else { + } else { if (free_space.QuadPart == 0) { uprintf("Unable to isolate drive name for VHD save"); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_PATH_NOT_FOUND; - } - else { + } else { uprintf("The VHD size is too large for the target drive"); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_FILE_TOO_LARGE; } - safe_free(vhd_save.path); + safe_free(img_save.ImagePath); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); format_op_in_progress = FALSE; } @@ -2056,6 +2056,57 @@ void SaveVHD(void) } } +void SaveISO(void) +{ + static IMG_SAVE img_save = { 0 }; + char filename[33] = "disc_image.iso"; + EXT_DECL(img_ext, filename, __VA_GROUP__("*.iso"), __VA_GROUP__(lmprintf(MSG_036))); + + if ((format_op_in_progress) || (format_thid != NULL)) + return; + + img_save.Type = IMG_SAVE_TYPE_ISO; + if (!GetOpticalMedia(&img_save)) { + uprintf("No dumpable optical media found."); + return; + } + // Adjust the buffer size according to the disc size so that we get a decent speed. + for (img_save.BufSize = 32 * MB; + (img_save.BufSize > 8 * MB) && (img_save.DeviceSize <= img_save.BufSize * 64); + img_save.BufSize /= 2); + if ((img_save.Label != NULL) && (img_save.Label[0] != 0)) + safe_sprintf(filename, sizeof(filename), "%s.iso", img_save.Label); + uprintf("ISO media size %s", SizeToHumanReadable(img_save.DeviceSize, FALSE, FALSE)); + + img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); + if (img_save.ImagePath == NULL) + return; + // Reset all progress bars + SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0); + SetTaskbarProgressState(TASKBAR_NORMAL); + SetTaskbarProgressValue(0, MAX_PROGRESS); + SendMessage(hProgress, PBM_SETPOS, 0, 0); + FormatStatus = 0; + format_op_in_progress = TRUE; + // Disable all controls except cancel + EnableControls(FALSE); + InitProgress(TRUE); + format_thid = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); + if (format_thid != NULL) { + uprintf("\r\nSave to ISO operation started"); + PrintInfo(0, -1); + timer = 0; + safe_sprintf(szTimer, sizeof(szTimer), "00:00:00"); + SendMessageA(hStatus, SB_SETTEXTA, SBT_OWNERDRAW | SB_SECTION_RIGHT, (LPARAM)szTimer); + SetTimer(hMainDialog, TID_APP_TIMER, 1000, ClockTimer); + } else { + uprintf("Unable to start ISO save thread"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); + safe_free(img_save.ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); + format_op_in_progress = FALSE; + } +} /* * Main dialog callback @@ -3115,7 +3166,7 @@ relaunch: // Do our own event processing and process "magic" commands while(GetMessage(&msg, NULL, 0, 0)) { - // ** ***** **** * ******** * + // ** ***** **** ** ******** * // .,ABCDEFGHIJKLMNOPQRSTUVWXYZ // Ctrl-A => Select the log data @@ -3225,6 +3276,11 @@ relaunch: PrintStatusTimeout(lmprintf(MSG_260), enable_ntfs_compression); continue; } + // Alt-O => Save from Optical drive to ISO + if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'O')) { + SaveISO(); + continue; + } // Alt-Q => Disable file indexing (for file systems that support it) if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Q')) { disable_file_indexing = !disable_file_indexing; diff --git a/src/rufus.h b/src/rufus.h index 66ce2736..a613d598 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -303,10 +303,18 @@ typedef struct { char* release_notes; } RUFUS_UPDATE; +#define IMG_SAVE_TYPE_VHD 1 +#define IMG_SAVE_TYPE_ISO 2 + typedef struct { + DWORD Type; DWORD DeviceNum; - char* path; -} VHD_SAVE; + DWORD BufSize; + LONGLONG DeviceSize; + char* DevicePath; + char* ImagePath; + char* Label; +} IMG_SAVE; /* * Structure and macros used for the extensions specification of FileDialog() @@ -432,6 +440,7 @@ extern DWORD GetResourceSize(HMODULE module, char* name, char* type, const char* extern DWORD RunCommand(const char* cmdline, const char* dir, BOOL log); extern BOOL CompareGUID(const GUID *guid1, const GUID *guid2); extern BOOL GetDevices(DWORD devnum); +extern BOOL GetOpticalMedia(IMG_SAVE* img_save); extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue); extern LONG GetEntryWidth(HWND hDropDown, const char* entry); extern DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog); diff --git a/src/rufus.rc b/src/rufus.rc index 08575c07..2b8aacc5 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 242, 376 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 2.11.1006" +CAPTION "Rufus 2.11.1007" FONT 8, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 @@ -320,8 +320,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,11,1006,0 - PRODUCTVERSION 2,11,1006,0 + FILEVERSION 2,11,1007,0 + PRODUCTVERSION 2,11,1007,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -338,13 +338,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "2.11.1006" + VALUE "FileVersion", "2.11.1007" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2016 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "2.11.1006" + VALUE "ProductVersion", "2.11.1007" END END BLOCK "VarFileInfo"