From 84e4aecfcd9457bc02ad20d2e4a772f2519acc8c Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 19 Jan 2013 04:04:54 +0000 Subject: [PATCH] [efi] add support for Windows 7 UEFI boot * Add WIM file handling in vhd.c to extract bootx64.efi * x64 only, since Microsoft does not provide Win7 x86 EFI support --- src/.msvc/rufus.vcxproj | 1 + src/.msvc/rufus.vcxproj.filters | 3 + src/.msvc/rufus_sources | 1 + src/Makefile.am | 2 +- src/Makefile.in | 15 +++- src/format.c | 21 +++++- src/iso.c | 13 ++-- src/rufus.c | 16 +++-- src/rufus.h | 3 + src/rufus.rc | 10 +-- src/vhd.c | 122 ++++++++++++++++++++++++++++++++ 11 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 src/vhd.c diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj index 84a30fc7..b9027638 100644 --- a/src/.msvc/rufus.vcxproj +++ b/src/.msvc/rufus.vcxproj @@ -175,6 +175,7 @@ + diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters index 53302d1c..068f5682 100644 --- a/src/.msvc/rufus.vcxproj.filters +++ b/src/.msvc/rufus.vcxproj.filters @@ -54,6 +54,9 @@ Source Files + + Source Files + diff --git a/src/.msvc/rufus_sources b/src/.msvc/rufus_sources index 78a4486d..59dbaf1a 100644 --- a/src/.msvc/rufus_sources +++ b/src/.msvc/rufus_sources @@ -41,4 +41,5 @@ SOURCES=rufus.c \ badblocks.c \ drive.c \ syslinux.c \ + vhd.c \ rufus.rc diff --git a/src/Makefile.am b/src/Makefile.am index cf181606..1aa6f424 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ pkg_v_rc_0 = @echo " RC $@"; %_rc.o: %.rc $(pkg_v_rc)$(WINDRES) $(AM_RCFLAGS) -i $< -o $@ -rufus_SOURCES = drive.c icon.c parser.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c format.c stdio.c stdlg.c rufus.c +rufus_SOURCES = drive.c icon.c parser.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdlg.c rufus.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ diff --git a/src/Makefile.in b/src/Makefile.in index af1bdff2..87a9faaa 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -47,8 +47,9 @@ am_rufus_OBJECTS = rufus-drive.$(OBJEXT) rufus-icon.$(OBJEXT) \ rufus-parser.$(OBJEXT) rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) \ rufus-dos.$(OBJEXT) rufus-dos_locale.$(OBJEXT) \ rufus-badblocks.$(OBJEXT) rufus-syslinux.$(OBJEXT) \ - rufus-format.$(OBJEXT) rufus-stdio.$(OBJEXT) \ - rufus-stdlg.$(OBJEXT) rufus-rufus.$(OBJEXT) + rufus-vhd.$(OBJEXT) rufus-format.$(OBJEXT) \ + rufus-stdio.$(OBJEXT) rufus-stdlg.$(OBJEXT) \ + rufus-rufus.$(OBJEXT) rufus_OBJECTS = $(am_rufus_OBJECTS) rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \ syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ @@ -183,7 +184,7 @@ SUBDIRS = ms-sys syslinux/libfat syslinux/libinstaller libcdio/iso9660 libcdio/u pkg_v_rc = $(pkg_v_rc_$(V)) pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY)) pkg_v_rc_0 = @echo " RC $@"; -rufus_SOURCES = drive.c icon.c parser.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c format.c stdio.c stdlg.c rufus.c +rufus_SOURCES = drive.c icon.c parser.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdlg.c rufus.c rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS) rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \ @@ -316,6 +317,14 @@ rufus-syslinux.obj: syslinux.c $(AM_V_CC) @AM_BACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-syslinux.obj `if test -f 'syslinux.c'; then $(CYGPATH_W) 'syslinux.c'; else $(CYGPATH_W) '$(srcdir)/syslinux.c'; fi` +rufus-vhd.o: vhd.c + $(AM_V_CC) @AM_BACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-vhd.o `test -f 'vhd.c' || echo '$(srcdir)/'`vhd.c + +rufus-vhd.obj: vhd.c + $(AM_V_CC) @AM_BACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-vhd.obj `if test -f 'vhd.c'; then $(CYGPATH_W) 'vhd.c'; else $(CYGPATH_W) '$(srcdir)/vhd.c'; fi` + rufus-format.o: format.c $(AM_V_CC) @AM_BACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format.o `test -f 'format.c' || echo '$(srcdir)/'`format.c diff --git a/src/format.c b/src/format.c index 943567cc..9a580107 100644 --- a/src/format.c +++ b/src/format.c @@ -1122,6 +1122,8 @@ DWORD WINAPI FormatThread(LPVOID param) char drive_name[] = "?:\\"; char bb_msg[512]; char logfile[MAX_PATH], *userdir; + char wim_image[] = "?:\\sources\\install.wim"; + char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; FILE* log_fd; hPhysicalDrive = GetDriveHandle(num, NULL, TRUE, TRUE); @@ -1250,7 +1252,7 @@ DWORD WINAPI FormatThread(LPVOID param) if (IsChecked(IDC_DOS)) { if (pt == PT_GPT) { // For once, no need to do anything - just check our sanity - if ( (dt != DT_ISO) || (!iso_report.has_efi) || (fs > FS_FAT32) ) { + if ( (dt != DT_ISO) || (!IS_EFI(iso_report)) || (fs > FS_FAT32) ) { uprintf("Spock gone crazy error!\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; goto out; @@ -1310,6 +1312,23 @@ 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)) { + // TODO: progress + PrintStatus(0, TRUE, "Win7 EFI boot setup (this may take a while)..."); + wim_image[0] = drive_name[0]; + efi_dst[0] = drive_name[0]; + efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = 0; + if (!CreateDirectoryA(efi_dst, 0)) { + uprintf("Could not create directory '%s': %s\n", WindowsErrorString()); + 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)) { + 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)) ) { // Apply WinPe fixup diff --git a/src/iso.c b/src/iso.c index 4737cf44..60861257 100644 --- a/src/iso.c +++ b/src/iso.c @@ -55,7 +55,7 @@ RUFUS_ISO_REPORT iso_report; int64_t iso_blocking_status = -1; #define ISO_BLOCKING(x) do {x; iso_blocking_status++; } while(0) static const char* psz_extract_dir; -static const char* bootmgr_name = "bootmgr"; +static const char* bootmgr_efi_name = "bootmgr.efi"; static const char* ldlinux_name = "ldlinux.sys"; static const char* efi_dirname = "/efi/boot"; static const char* isolinux_name[] = { "isolinux.cfg", "syslinux.cfg", "extlinux.conf"}; @@ -123,9 +123,14 @@ static __inline BOOL check_iso_props(const char* psz_dirname, BOOL* is_syslinux_ } if (scan_only) { - // Check for a "bootmgr" file in root (psz_path = "") - if ((*psz_dirname == 0) && (safe_stricmp(psz_basename, bootmgr_name) == 0)) - iso_report.has_bootmgr = TRUE; + // Check for a "bootmgr(.efi)" file in root (psz_path = "") + if (*psz_dirname == 0) { + if (safe_strnicmp(psz_basename, bootmgr_efi_name, sizeof(bootmgr_efi_name)-4) == 0) + iso_report.has_bootmgr = TRUE; + if (safe_stricmp(psz_basename, bootmgr_efi_name) == 0) { + iso_report.has_win7_efi = TRUE; + } + } // Check for the EFI boot directory if (safe_stricmp(psz_dirname, efi_dirname) == 0) diff --git a/src/rufus.c b/src/rufus.c index 8ef7a2e9..4215e223 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -491,7 +491,7 @@ static void SetFSFromISO(void) } // Syslinux and EFI have precedence over bootmgr - if ((iso_report.has_isolinux) || (iso_report.has_efi)) { + if ((iso_report.has_isolinux) || (IS_EFI(iso_report))) { if (fs_mask & (1<4GB file: %s\r\n Uses EFI: %s\r\n Uses Bootmgr: %s\r\n Uses WinPE: %s%s\r\n Uses isolinux: %s\n", - iso_report.label, iso_report.projected_size, iso_report.has_4GB_file?"Yes":"No", iso_report.has_efi?"Yes":"No", iso_report.has_bootmgr?"Yes":"No", + // TODO: 4GB and UEFI = BAD!!! + uprintf("ISO label: '%s'\r\n Size: %lld bytes\r\n Has a >4GB file: %s\r\n Uses EFI: %s%s\r\n Uses Bootmgr: %s\r\n Uses WinPE: %s%s\r\n Uses isolinux: %s\n", + iso_report.label, iso_report.projected_size, iso_report.has_4GB_file?"Yes":"No", (iso_report.has_efi || iso_report.has_win7_efi)?"Yes":"No", + (iso_report.has_win7_efi && (!iso_report.has_efi))?" (win7_x64)":"", iso_report.has_bootmgr?"Yes":"No", IS_WINPE(iso_report.winpe)?"Yes":"No", (iso_report.uses_minint)?" (with /minint)":"", iso_report.has_isolinux?"Yes":"No"); if (iso_report.has_isolinux) { for (i=0; i FS_FAT32)))) { + 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; @@ -1937,8 +1939,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } break; } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && ((!iso_report.has_isolinux) && (pt != PT_GPT))) { - MessageBoxA(hMainDialog, "Only 'isolinux' based ISO " - "images can currently be used with FAT.", "Unsupported ISO...", MB_OK|MB_ICONERROR); + MessageBoxA(hMainDialog, "Only isolinux or EFI based ISO " + "images can currently be used with FAT/FAT32.", "Unsupported ISO...", MB_OK|MB_ICONERROR); break; } } diff --git a/src/rufus.h b/src/rufus.h index ab463bc5..f6d830c1 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -181,6 +181,7 @@ typedef struct { #define WINPE_MININT 0x2A #define WINPE_I386 0x15 #define IS_WINPE(r) (((r&WINPE_MININT) == WINPE_MININT)||((r&WINPE_I386) == WINPE_I386)) +#define IS_EFI(r) ((r.has_efi) || (r.has_win7_efi)) typedef struct { char label[192]; /* 3*64 to account for UTF-8 */ @@ -191,6 +192,7 @@ typedef struct { BOOL has_4GB_file; BOOL has_bootmgr; BOOL has_efi; + BOOL has_win7_efi; BOOL has_isolinux; BOOL has_autorun; BOOL has_old_c32[NB_OLD_C32]; @@ -296,6 +298,7 @@ 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); __inline static BOOL UnlockDrive(HANDLE hDrive) { diff --git a/src/rufus.rc b/src/rufus.rc index 3a878dd5..df2c0b27 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.223" +CAPTION "Rufus v1.3.1.224" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -274,8 +274,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,1,223 - PRODUCTVERSION 1,3,1,223 + FILEVERSION 1,3,1,224 + PRODUCTVERSION 1,3,1,224 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.223" + VALUE "FileVersion", "1.3.1.224" 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.223" + VALUE "ProductVersion", "1.3.1.224" END END BLOCK "VarFileInfo" diff --git a/src/vhd.c b/src/vhd.c new file mode 100644 index 00000000..db8aca5c --- /dev/null +++ b/src/vhd.c @@ -0,0 +1,122 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * Virtual Disk Handling functions + * Copyright (c) 2013 Pete Batard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "rufus.h" +#include "msapi_utf8.h" + +#define WIM_GENERIC_READ GENERIC_READ +#define WIM_OPEN_EXISTING OPEN_EXISTING + +typedef HANDLE (WINAPI *WIMCreateFile_t)( + PWSTR pszWimPath, + DWORD dwDesiredAccess, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + DWORD dwCompressionType, + PDWORD pdwCreationResult +); + +typedef BOOL (WINAPI *WIMSetTemporaryPath_t)( + HANDLE hWim, + PWSTR pszPath +); + +typedef HANDLE (WINAPI *WIMLoadImage_t)( + HANDLE hWim, + DWORD dwImageIndex +); + +typedef BOOL (WINAPI *WIMExtractImagePath_t)( + HANDLE hImage, + PWSTR pszImagePath, + PWSTR pszDestinationPath, + DWORD dwExtractFlags +); + +typedef BOOL (WINAPI *WIMCloseHandle_t)( + HANDLE hObj +); + +// Extract a file from a WIM image +// 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) +{ + BOOL r = FALSE; + DWORD dw = 0; + HANDLE hWim = NULL; + HANDLE hImage = NULL; + wchar_t wtemp[MAX_PATH] = {0}; + 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); + PF_INIT_OR_OUT(WIMLoadImage, wimgapi); + PF_INIT_OR_OUT(WIMExtractImagePath, wimgapi); + PF_INIT_OR_OUT(WIMCloseHandle, wimgapi); + + // TODO: check for NULL and missing wimgapi.dll + + if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) { + 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()); + goto out; + } + + if (!pfWIMSetTemporaryPath(hWim, wtemp)) { + uprintf(" Error setting temp path: %s\n", WindowsErrorString()); + goto out; + } + + hImage = pfWIMLoadImage(hWim, (DWORD)index); + if (hImage == NULL) { + uprintf(" Error setting 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()); + goto out; + } + r = TRUE; + +out: + if ((hImage != NULL) || (hWim != NULL)) { + uprintf("Closing: %s\n", image); + } + if (hImage != NULL) pfWIMCloseHandle(hImage); + if (hWim != NULL) pfWIMCloseHandle(hWim); + safe_free(wimage); + safe_free(wsrc); + safe_free(wdst); + return r; +}