From 0196de6f4d98ef828324006ac8dc9752c91b1b95 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sun, 20 Jan 2013 22:46:11 +0000 Subject: [PATCH] [efi] add Windows 7 EFI support for XP and Vista * Requires 7-Zip for WIM extraction as wimgapi.dll is not available * Also add more comprehensive choice between MBR/GPT and BIOS/UEFI --- src/format.c | 23 ++++----- src/registry.h | 27 ++++++++--- src/rufus.c | 117 +++++++++++++++++++++++++++------------------ src/rufus.h | 16 +++++-- src/rufus.rc | 12 ++--- src/vhd.c | 126 +++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 233 insertions(+), 88 deletions(-) diff --git a/src/format.c b/src/format.c index 9a580107..20870053 100644 --- a/src/format.c +++ b/src/format.c @@ -749,7 +749,7 @@ static BOOL ClearMBRGPT(HANDLE hPhysicalDrive, LONGLONG DiskSize, DWORD SectorSi uint64_t i, last_sector = DiskSize/SectorSize; unsigned char* pBuf = (unsigned char*) calloc(SectorSize, 1); - PrintStatus(0, TRUE, "Clearing MBR & GPT structures..."); + PrintStatus(0, TRUE, "Clearing MBR/GPT structures..."); if (pBuf == NULL) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY; goto out; @@ -818,7 +818,6 @@ static BOOL WriteMBR(HANDLE hPhysicalDrive) // FormatEx rewrites the MBR and removes the LBA attribute of FAT16 // and FAT32 partitions - we need to correct this in the MBR - // TODO: Something else for bootable GPT buf = (unsigned char*)malloc(SecSize * nSecs); if (buf == NULL) { uprintf("Could not allocate memory for MBR"); @@ -1110,10 +1109,7 @@ static BOOL RemountVolume(char drive_letter) */ DWORD WINAPI FormatThread(LPVOID param) { - int r; - int fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); - int dt = (int)ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType)); - int pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); + int r, pt, bt, fs, dt; BOOL ret; DWORD num = (DWORD)(uintptr_t)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; @@ -1126,6 +1122,11 @@ DWORD WINAPI FormatThread(LPVOID param) char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; FILE* log_fd; + fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); + dt = (int)ComboBox_GetItemData(hDOSType, ComboBox_GetCurSel(hDOSType)); + pt = GETPARTTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); + bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); + hPhysicalDrive = GetDriveHandle(num, NULL, TRUE, TRUE); if (hPhysicalDrive == INVALID_HANDLE_VALUE) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; @@ -1239,7 +1240,7 @@ DWORD WINAPI FormatThread(LPVOID param) goto out; } - if (pt == PT_MBR) { + if (pt == PARTITION_STYLE_MBR) { PrintStatus(0, TRUE, "Writing master boot record..."); if (!WriteMBR(hPhysicalDrive)) { if (!FormatStatus) @@ -1250,7 +1251,7 @@ DWORD WINAPI FormatThread(LPVOID param) } if (IsChecked(IDC_DOS)) { - if (pt == PT_GPT) { + if (bt == BT_UEFI) { // For once, no need to do anything - just check our sanity if ( (dt != DT_ISO) || (!IS_EFI(iso_report)) || (fs > FS_FAT32) ) { uprintf("Spock gone crazy error!\n"); @@ -1312,7 +1313,7 @@ DWORD WINAPI FormatThread(LPVOID param) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; goto out; } - if ((pt == PT_GPT) && (!iso_report.has_efi) && (iso_report.has_win7_efi)) { + if ((bt == BT_UEFI) && (!iso_report.has_efi) && (iso_report.has_win7_efi)) { // TODO: progress PrintStatus(0, TRUE, "Win7 EFI boot setup (this may take a while)..."); wim_image[0] = drive_name[0]; @@ -1323,14 +1324,14 @@ DWORD WINAPI FormatThread(LPVOID param) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } else { efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = '\\'; - if (!WIMExtractFile(wim_image, 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst)) { + if (!WimExtractFile(wim_image, 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst)) { uprintf("Failed to setup Win7 EFI boot\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } } } } - if ( (pt == PT_MBR) && (IS_WINPE(iso_report.winpe)) ) { + if ( (bt == BT_BIOS) && (IS_WINPE(iso_report.winpe)) ) { // Apply WinPe fixup if (!SetupWinPE(drive_name[0])) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); diff --git a/src/registry.h b/src/registry.h index 786a0ca9..4ccc180e 100644 --- a/src/registry.h +++ b/src/registry.h @@ -57,24 +57,39 @@ static __inline BOOL DeleteRegistryKey(const char* key_name) return ((s == ERROR_SUCCESS) || (s == ERROR_FILE_NOT_FOUND)); } -/* Read a generic registry key value (create the key if it doesn't exist) */ +/* Read a generic registry key value. If a short key_name is used, assume that it belongs to + the application and create the app subkey if required */ static __inline BOOL _GetRegistryKey(const char* key_name, DWORD reg_type, LPBYTE dest, DWORD dest_size) { BOOL r = FALSE; + size_t i = 0; LONG s; HKEY hSoftware = NULL, hApp = NULL; DWORD dwDisp, dwType = -1, dwSize = dest_size; + char long_key_name[256] = "SOFTWARE\\"; memset(dest, 0, dest_size); - if ( (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE", 0, KEY_READ|KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) - || (RegCreateKeyExA(hSoftware, COMPANY_NAME "\\" APPLICATION_NAME, 0, NULL, 0, - KEY_SET_VALUE|KEY_QUERY_VALUE|KEY_CREATE_SUB_KEY, NULL, &hApp, &dwDisp) != ERROR_SUCCESS) ) { + for (i=safe_strlen(key_name); i>0; i--) { + if (key_name[i] == '\\') + break; + } + + if (i != 0) { + safe_strcat(long_key_name, sizeof(long_key_name), key_name); + long_key_name[sizeof("SOFTWARE\\") + i-1] = 0; + i++; + if (RegOpenKeyExA(HKEY_CURRENT_USER, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) + goto out; + } else { + if ( (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE", 0, KEY_READ|KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) + || (RegCreateKeyExA(hSoftware, COMPANY_NAME "\\" APPLICATION_NAME, 0, NULL, 0, + KEY_SET_VALUE|KEY_QUERY_VALUE|KEY_CREATE_SUB_KEY, NULL, &hApp, &dwDisp) != ERROR_SUCCESS) ) goto out; } - s = RegQueryValueExA(hApp, key_name, NULL, &dwType, (LPBYTE)dest, &dwSize); + s = RegQueryValueExA(hApp, &key_name[i], NULL, &dwType, (LPBYTE)dest, &dwSize); // No key means default value of 0 or empty string - if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType = reg_type) && (dwSize = dest_size))) { + if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType = reg_type) && (dwSize > 0))) { r = TRUE; } out: diff --git a/src/rufus.c b/src/rufus.c index 4215e223..f0100234 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -86,7 +86,8 @@ static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" } static const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes", "16 kilobytes", "32 kilobytes", "64 kilobytes", "128 kilobytes", "256 kilobytes", "512 kilobytes", "1024 kilobytes","2048 kilobytes","4096 kilobytes","8192 kilobytes","16 megabytes","32 megabytes" }; -static const char* PartitionSchemeName[PT_MAX] = { "MBR", "GPT" }; +static const char* BiosTypeName[BT_MAX] = { "BIOS", "UEFI" }; +static const char* PartitionTypeName[2] = { "MBR", "GPT" }; static BOOL existing_key = FALSE; // For LGP set/restore static BOOL iso_size_check = TRUE; static BOOL log_displayed = FALSE; @@ -350,7 +351,7 @@ static __inline char* size_to_hr(LARGE_INTEGER size) /* * Fill the drive properties (size, FS, etc) */ -static BOOL GetDriveInfo(void) +static BOOL GetDriveInfo(DWORD DeviceNumber) { BOOL r; HANDLE hDrive; @@ -363,7 +364,8 @@ static BOOL GetDriveInfo(void) char DrivePath[] = "#:\\", tmp[256], fs_type[32]; DWORD i, nb_partitions = 0; - SelectedDrive.DiskSize = 0; + memset(&SelectedDrive, 0, sizeof(SelectedDrive)); + SelectedDrive.DeviceNumber = DeviceNumber; hDrive = GetDriveHandle(SelectedDrive.DeviceNumber, DrivePath, FALSE, FALSE); if (hDrive == INVALID_HANDLE_VALUE) @@ -389,7 +391,7 @@ static BOOL GetDriveInfo(void) } else { switch (DriveLayout->PartitionStyle) { case PARTITION_STYLE_MBR: - SelectedDrive.PartitionType = PT_MBR; + SelectedDrive.PartitionType = PARTITION_STYLE_MBR; for (i=0; iPartitionCount; i++) { if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { nb_partitions++; @@ -406,11 +408,13 @@ static BOOL GetDriveInfo(void) DriveLayout->PartitionEntry[i].PartitionLength, DriveLayout->PartitionEntry[i].Mbr.HiddenSectors, DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No", DriveLayout->PartitionEntry[i].Mbr.RecognizedPartition?"Yes":"No"); + if (part_type == 0xee) // Flag a protective MBR for non GPT platforms (XP) + SelectedDrive.has_protective_mbr = TRUE; } } break; case PARTITION_STYLE_GPT: - SelectedDrive.PartitionType = PT_GPT; + SelectedDrive.PartitionType = PARTITION_STYLE_GPT; uprintf("Partition type: GPT, NB Partitions: %d\n", DriveLayout->PartitionCount); uprintf("Disk GUID: %s\n", GuidToString(&DriveLayout->Gpt.DiskId)); uprintf("Max parts: %d, Start Offset: %lld, Usable = %lld bytes\n", @@ -428,7 +432,7 @@ static BOOL GetDriveInfo(void) } break; default: - SelectedDrive.PartitionType = PT_MBR; + SelectedDrive.PartitionType = PARTITION_STYLE_MBR; uprintf("Partition type: RAW\n"); break; } @@ -480,6 +484,7 @@ static void SetFSFromISO(void) { int i, fs, selected_fs = FS_UNKNOWN; uint32_t fs_mask = 0; + int bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); if (iso_path == NULL) return; @@ -490,8 +495,8 @@ static void SetFSFromISO(void) fs_mask |= 1<= ARRAYSIZE(suffix)) uprintf("Could not populate partition scheme data\n"); - IGNORE_RETVAL(ComboBox_SetCurSel(hPartitionScheme, SelectedDrive.PartitionType)); + if (SelectedDrive.PartitionType == PARTITION_STYLE_GPT) { + j = 2; + } else if (SelectedDrive.has_protective_mbr) { + j = 1; + } else { + j = 0; + } + IGNORE_RETVAL(ComboBox_SetCurSel(hPartitionScheme, j)); + // TODO: create a tooltip for hPartitionScheme CreateTooltip(hDeviceList, DriveID.Table[ComboIndex], -1); // Set a proposed label according to the size (eg: "256MB", "8GB") @@ -638,11 +656,11 @@ BOOL CreatePartition(HANDLE hDrive) BOOL r; DWORD size; LONGLONG size_in_sectors; - int pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); + int pt = GETPARTTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); - PrintStatus(0, TRUE, "Partitioning (%s)...", PartitionSchemeName[pt]); + PrintStatus(0, TRUE, "Partitioning (%s)...", PartitionTypeName[pt]); - if ((pt == PT_GPT) || (!IsChecked(IDC_EXTRA_PARTITION))) { + if ((pt == PARTITION_STYLE_GPT) || (!IsChecked(IDC_EXTRA_PARTITION))) { // Go with the MS 1 MB wastage at the beginning... DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart = 1024*1024; } else { @@ -653,7 +671,7 @@ BOOL CreatePartition(HANDLE hDrive) size_in_sectors = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart) / SelectedDrive.Geometry.BytesPerSector; switch (pt) { - case PT_MBR: + case PARTITION_STYLE_MBR: CreateDisk.PartitionStyle = PARTITION_STYLE_MBR; CreateDisk.Mbr.Signature = GetTickCount(); @@ -672,7 +690,7 @@ BOOL CreatePartition(HANDLE hDrive) return FALSE; } break; - case PT_GPT: + case PARTITION_STYLE_GPT: CreateDisk.PartitionStyle = PARTITION_STYLE_GPT; IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId)); CreateDisk.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; @@ -698,7 +716,7 @@ BOOL CreatePartition(HANDLE hDrive) DriveLayoutEx.PartitionEntry[0].RewritePartition = TRUE; switch (pt) { - case PT_MBR: + case PARTITION_STYLE_MBR: DriveLayoutEx.PartitionEntry[0].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack; switch (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))) { case FS_FAT16: @@ -730,7 +748,7 @@ BOOL CreatePartition(HANDLE hDrive) // For the remaining partitions, PartitionStyle & PartitionType have already // been zeroed => already set to MBR/unused break; - case PT_GPT: + case PARTITION_STYLE_GPT: DriveLayoutEx.PartitionEntry[0].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; wcscpy(DriveLayoutEx.PartitionEntry[0].Gpt.Name, L"Microsoft Basic Data"); IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[0].Gpt.PartitionId)); @@ -749,7 +767,7 @@ BOOL CreatePartition(HANDLE hDrive) return FALSE; } - size = sizeof(DriveLayoutEx) - ((pt == PT_GPT)?(3*sizeof(PARTITION_INFORMATION_EX)):0); + size = sizeof(DriveLayoutEx) - ((pt == PARTITION_STYLE_GPT)?(3*sizeof(PARTITION_INFORMATION_EX)):0); r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL ); if (!r) { @@ -1241,10 +1259,6 @@ BOOL CALLBACK LogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) break; } break; -// case WM_SHOWWINDOW: -// if (wParam) -// SendMessage(hLog, EM_LINESCROLL, 0, SendMessage(hLog, EM_GETLINECOUNT, 0, 0)); -// return FALSE; case WM_CLOSE: ShowWindow(hDlg, SW_HIDE); log_displayed = FALSE; @@ -1637,7 +1651,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA DRAWITEMSTRUCT* pDI; POINT Point; RECT DialogRect, DesktopRect; - int nDeviceIndex, fs, pt, i, nWidth, nHeight; + int nDeviceIndex, fs, bt, i, nWidth, nHeight; static DWORD DeviceNum = 0; wchar_t wtmp[128], wstr[MAX_PATH]; static UINT uDOSChecked = BST_CHECKED, uQFChecked; @@ -1787,7 +1801,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA if (HIWORD(wParam) != CBN_SELCHANGE) break; fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); - pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); + bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); SetClusterSizes(fs); // Disable/restore the quick format control depending on large FAT32 if ((fs == FS_FAT32) && (SelectedDrive.DiskSize > LARGE_FAT32_SIZE)) { @@ -1812,7 +1826,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } break; } - if ((fs == FS_EXFAT) || ((pt == PT_GPT) && (fs == FS_NTFS))) { + if (fs == FS_EXFAT) { if (IsWindowEnabled(hDOS)) { // unlikely to be supported by BIOSes => don't bother IGNORE_RETVAL(ComboBox_SetCurSel(hDOSType, 0)); @@ -1824,13 +1838,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; } IGNORE_RETVAL(ComboBox_ResetContent(hDOSType)); - if ((pt == PT_MBR) && ((fs == FS_FAT16) || (fs == FS_FAT32))) { + if ((bt == BT_BIOS) && ((fs == FS_FAT16) || (fs == FS_FAT32))) { IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "MS-DOS"), DT_WINME)); IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "FreeDOS"), DT_FREEDOS)); } IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "ISO Image"), DT_ISO)); // If needed (advanced mode) also append a Syslinux option - if ( (pt == PT_MBR) && (((fs == FS_FAT16) || (fs == FS_FAT32)) && (advanced_mode)) ) + if ( (bt == BT_BIOS) && (((fs == FS_FAT16) || (fs == FS_FAT32)) && (advanced_mode)) ) IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "SysLinux"), DT_SYSLINUX)); if ( ((!advanced_mode) && (selection_default == DT_SYSLINUX)) ) { selection_default = DT_FREEDOS; @@ -1923,24 +1937,37 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; } fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); - pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); - if ((pt == PT_GPT) && ((!IS_EFI(iso_report)) || ((fs > FS_FAT32)))) { - MessageBoxA(hMainDialog, "When using GPT, only EFI bootable ISOs are supported. " - "Please select an EFI bootable ISO or change the Partition Scheme to MBR.", "Unsupported GPT ISO...", MB_OK|MB_ICONERROR); - break; - } - else if ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe))) { + bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); + if (bt == BT_UEFI) { + if (!IS_EFI(iso_report)) { + MessageBoxA(hMainDialog, "When using UEFI Target Type, only EFI bootable ISO images are supported. " + "Please select an EFI bootable ISO or set the Target Type to BIOS.", "Unsupported ISO...", MB_OK|MB_ICONERROR); + break; + } else if (fs > FS_FAT32) { + MessageBoxA(hMainDialog, "When using UEFI Target Type, only FAT/FAT32 is supported. " + "Please select FAT/FAT32 as the File system or set the Target Type to BIOS.", "Unsupported filesystem...", MB_OK|MB_ICONERROR); + break; + } + } else if ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe))) { if (iso_report.has_isolinux) { - MessageBoxA(hMainDialog, "Only FAT32 is supported for this type of ISO. " - "Please revert the filesystem back from NTFS to FAT32.", "Unsupported filesystem...", MB_OK|MB_ICONERROR); + MessageBoxA(hMainDialog, "Only FAT/FAT32 is supported for this type of ISO. " + "Please select FAT/FAT32 as the File system.", "Unsupported filesystem...", MB_OK|MB_ICONERROR); } else { MessageBoxA(hMainDialog, "Only 'bootmgr' or 'WinPE' based ISO " "images can currently be used with NTFS.", "Unsupported ISO...", MB_OK|MB_ICONERROR); } break; - } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && ((!iso_report.has_isolinux) && (pt != PT_GPT))) { - MessageBoxA(hMainDialog, "Only isolinux or EFI based ISO " - "images can currently be used with FAT/FAT32.", "Unsupported ISO...", MB_OK|MB_ICONERROR); + } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && (!iso_report.has_isolinux)) { + MessageBoxA(hMainDialog, "FAT/FAT32 can only be used for isolinux based ISO images " + "or when the Target Type is UEFI.", "Unsupported ISO...", MB_OK|MB_ICONERROR); + break; + } + if ((bt == BT_UEFI) && (iso_report.has_win7_efi) && (!WimExtractCheck())) { + if (MessageBoxA(hMainDialog, "Your platform cannot extract files from WIM archives. WIM extraction " + "is required to create EFI bootable Windows 7 and Windows Vista USB drives. You can fix that " + "by installing a recent version of 7-Zip.\r\nDo you want to visit the 7-zip download page?", + "Missing WIM support...", MB_YESNO|MB_ICONERROR) == IDYES) + ShellExecuteA(hDlg, "open", SEVENZIP_URL, NULL, NULL, SW_SHOWNORMAL); break; } } diff --git a/src/rufus.h b/src/rufus.h index f6d830c1..526a3763 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -51,6 +51,7 @@ #define WHITE RGB(255,255,255) #define SEPARATOR_GREY RGB(223,223,223) #define RUFUS_URL "http://rufus.akeo.ie" +#define SEVENZIP_URL "http://sourceforge.net/projects/sevenzip/files/7-Zip/" #define IGNORE_RETVAL(expr) do { (void)(expr); } while(0) #ifndef ARRAYSIZE #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) @@ -150,11 +151,14 @@ enum dos_type { DT_MAX }; -enum part_type { - PT_MBR = 0, - PT_GPT, - PT_MAX +enum bios_type { + BT_BIOS = 0, + BT_UEFI, + BT_MAX }; +// For the partition types we'll use Microsoft's PARTITION_STYLE_### constants +#define GETBIOSTYPE(x) (((x) >> 16) & 0xFFFF) +#define GETPARTTYPE(x) ((x) & 0xFFFF); /* Current drive info */ typedef struct { @@ -165,6 +169,7 @@ typedef struct { char proposed_label[16]; int PartitionType; int FSType; + BOOL has_protective_mbr; struct { ULONG Allowed; ULONG Default; @@ -298,7 +303,8 @@ extern char* get_token_data_buffer(const char* token, unsigned int n, const char extern char* insert_section_data(const char* filename, const char* section, const char* data, BOOL dos2unix); extern char* replace_in_token_data(const char* filename, const char* token, const char* src, const char* rep, BOOL dos2unix); extern void parse_update(char* buf, size_t len); -extern BOOL WIMExtractFile(const char* wim_image, int index, const char* src, const char* dst); +extern BOOL WimExtractCheck(void); +extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst); __inline static BOOL UnlockDrive(HANDLE hDrive) { diff --git a/src/rufus.rc b/src/rufus.rc index df2c0b27..8c12b24b 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -30,7 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 316 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.3.1.224" +CAPTION "Rufus v1.3.1.225" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -40,7 +40,7 @@ BEGIN COMBOBOX IDC_FILESYSTEM,8,75,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "File system",IDC_STATIC,9,64,51,10 COMBOBOX IDC_PARTITION_SCHEME,8,46,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Partition Scheme",IDC_STATIC,9,35,75,8 + LTEXT "Partition Scheme and Target Type",IDC_STATIC,9,35,176,8 COMBOBOX IDC_CLUSTERSIZE,8,104,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Cluster size",IDC_STATIC,9,93,105,10 PUSHBUTTON "About...",IDC_ABOUT,8,278,50,14 @@ -274,8 +274,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,1,224 - PRODUCTVERSION 1,3,1,224 + FILEVERSION 1,3,1,225 + PRODUCTVERSION 1,3,1,225 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -292,13 +292,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.3.1.224" + VALUE "FileVersion", "1.3.1.225" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "(c) 2011-2012 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.3.1.224" + VALUE "ProductVersion", "1.3.1.225" END END BLOCK "VarFileInfo" diff --git a/src/vhd.c b/src/vhd.c index db8aca5c..bc9495e5 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -17,8 +17,14 @@ * along with this program. If not, see . */ +#include +#include + #include "rufus.h" #include "msapi_utf8.h" +#include "registry.h" + +static BOOL has_wimgapi = FALSE, has_7z = FALSE; #define WIM_GENERIC_READ GENERIC_READ #define WIM_OPEN_EXISTING OPEN_EXISTING @@ -53,9 +59,39 @@ typedef BOOL (WINAPI *WIMCloseHandle_t)( HANDLE hObj ); -// Extract a file from a WIM image +// WIM API Prototypes +static PF_DECL(WIMCreateFile); +static PF_DECL(WIMSetTemporaryPath); +static PF_DECL(WIMLoadImage); +static PF_DECL(WIMExtractImagePath); +static PF_DECL(WIMCloseHandle); + +// Find out if we have any way to extraxt WIM files on this platform +BOOL WimExtractCheck(void) +{ + char sevenzip_path[MAX_PATH]; + + PF_INIT(WIMCreateFile, wimgapi); + PF_INIT(WIMSetTemporaryPath, wimgapi); + PF_INIT(WIMLoadImage, wimgapi); + PF_INIT(WIMExtractImagePath, wimgapi); + PF_INIT(WIMCloseHandle, wimgapi); + + has_wimgapi = (pfWIMCreateFile && pfWIMSetTemporaryPath && pfWIMLoadImage && pfWIMExtractImagePath && pfWIMCloseHandle); + if (GetRegistryKeyStr("7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) { + safe_strcat(sevenzip_path, sizeof(sevenzip_path), "\\7z.exe"); + has_7z = (_access(sevenzip_path, 0) != -1); + } + + uprintf("WIM extraction method(s) supported: %s%s%s\n", has_7z?"7z":(has_wimgapi?"":"NONE"), + (has_wimgapi && has_7z)?", ":"", has_wimgapi?"wimgapi.dll":""); + return (has_wimgapi || has_7z); +} + + +// Extract a file from a WIM image using wimgapi.dll (Windows 7 or later) // NB: Don't bother trying to get progress from a WIM callback - it doesn't work! -BOOL WIMExtractFile(const char* image, int index, const char* src, const char* dst) +static BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst) { BOOL r = FALSE; DWORD dw = 0; @@ -65,11 +101,6 @@ BOOL WIMExtractFile(const char* image, int index, const char* src, const char* d wchar_t* wimage = utf8_to_wchar(image); wchar_t* wsrc = utf8_to_wchar(src); wchar_t* wdst = utf8_to_wchar(dst); - PF_DECL(WIMCreateFile); - PF_DECL(WIMSetTemporaryPath); - PF_DECL(WIMLoadImage); - PF_DECL(WIMExtractImagePath); - PF_DECL(WIMCloseHandle); PF_INIT_OR_OUT(WIMCreateFile, wimgapi); PF_INIT_OR_OUT(WIMSetTemporaryPath, wimgapi); @@ -77,37 +108,36 @@ BOOL WIMExtractFile(const char* image, int index, const char* src, const char* d PF_INIT_OR_OUT(WIMExtractImagePath, wimgapi); PF_INIT_OR_OUT(WIMCloseHandle, wimgapi); - // TODO: check for NULL and missing wimgapi.dll - + uprintf("Opening: %s:[%d] (API)\n", image, index); if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) { - uprintf("Could not fetch temp path: %s\n", WindowsErrorString()); + uprintf(" Could not fetch temp path: %s\n", WindowsErrorString()); goto out; } - uprintf("Opening: %s (index #%d)\n", image, index); hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING, 0, 0, &dw); if (hWim == NULL) { - uprintf(" Error: '%s': %s\n", WindowsErrorString()); + uprintf(" Could not access image: %s\n", WindowsErrorString()); goto out; } if (!pfWIMSetTemporaryPath(hWim, wtemp)) { - uprintf(" Error setting temp path: %s\n", WindowsErrorString()); + uprintf(" Could not set temp path: %s\n", WindowsErrorString()); goto out; } hImage = pfWIMLoadImage(hWim, (DWORD)index); if (hImage == NULL) { - uprintf(" Error setting index: %s.\n", WindowsErrorString()); + uprintf(" Could not set index: %s\n", WindowsErrorString()); goto out; } uprintf("Extracting: %s (From \\%s)\n", dst, src); if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) { - uprintf(" Could not extract file: %s.\n", WindowsErrorString()); + uprintf(" Could not extract file: %s\n", WindowsErrorString()); goto out; } r = TRUE; + UpdateProgress(OP_FINALIZE, -1.0f); out: if ((hImage != NULL) || (hWim != NULL)) { @@ -120,3 +150,69 @@ out: safe_free(wdst); return r; } + +// Extract a file from a WIM image using 7-Zip +static BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst) +{ + size_t i; + STARTUPINFOA si = {0}; + PROCESS_INFORMATION pi = {0}; + char sevenzip_path[MAX_PATH]; + char cmdline[MAX_PATH]; + char tmpdst[MAX_PATH]; + + uprintf("Opening: %s:[%d] (7-Zip)\n", image, index); + if (!GetRegistryKeyStr("7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) { + uprintf(" Could not read 7-Zip path from registry\n"); + return FALSE; + } + safe_strcat(sevenzip_path, sizeof(sevenzip_path), "\\7z.exe"); + + if (_access(sevenzip_path, 0) == -1) { + uprintf(" Could not locate 7z.exe at '%s'\n", sevenzip_path); + return FALSE; + } + + safe_strcpy(tmpdst, sizeof(tmpdst), dst); + for (i=safe_strlen(tmpdst); i>0; i--) { + if (tmpdst[i] == '\\') + break; + } + tmpdst[i] = 0; + + si.cb = sizeof(si); + safe_sprintf(cmdline, sizeof(cmdline), "7z -y e \"%s\" %d\\Windows\\Boot\\EFI\\bootmgfw.efi", image, index); + uprintf("Extracting: %s (From \\%s)\n", dst, src); + if (!CreateProcessU(sevenzip_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, tmpdst, &si, &pi)) { + uprintf(" Could not launch 7z.exe: %s\n", WindowsErrorString()); + return FALSE; + } + WaitForSingleObject(pi.hProcess, INFINITE); + UpdateProgress(OP_FINALIZE, -1.0f); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + safe_strcat(tmpdst, sizeof(tmpdst), "\\bootmgfw.efi"); + if (_access(tmpdst, 0) == -1) { + uprintf(" 7z.exe did not extract %s\n", tmpdst); + return FALSE; + } + if (rename(tmpdst, dst) != 0) { + uprintf(" Could not rename %s to %s\n", tmpdst, dst); + return FALSE; + } + + return TRUE; +} + +// Extract a file from a WIM image +BOOL WimExtractFile(const char* image, int index, const char* src, const char* dst) +{ + if ((!has_wimgapi) && (!has_7z) && (!WimExtractCheck())) + return FALSE; + + // Prefer 7-Zip as, unsurprisingly, it's faster than the Microsoft way, + // but allow fallback if 7-Zip doesn't succeed + return ( (has_7z && WimExtractFile_7z(image, index, src, dst)) + || (has_wimgapi && WimExtractFile_API(image, index, src, dst)) ); +}