[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
This commit is contained in:
Pete Batard 2013-01-19 04:04:54 +00:00
parent 997c0a0f28
commit 84e4aecfcd
11 changed files with 186 additions and 21 deletions

View File

@ -175,6 +175,7 @@
<ClCompile Include="..\stdio.c" />
<ClCompile Include="..\stdlg.c" />
<ClCompile Include="..\syslinux.c" />
<ClCompile Include="..\vhd.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\badblocks.h" />

View File

@ -54,6 +54,9 @@
<ClCompile Include="..\net.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\vhd.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rufus.h">

View File

@ -41,4 +41,5 @@ SOURCES=rufus.c \
badblocks.c \
drive.c \
syslinux.c \
vhd.c \
rufus.rc

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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<<FS_FAT32)) {
selected_fs = FS_FAT32;
} else if (fs_mask & (1<<FS_FAT16)) {
@ -633,7 +633,7 @@ const GUID PARTITION_BASIC_DATA_GUID = { 0xebd0a0a2, 0xb9e5, 0x4433, {0x87, 0xc0
#endif
BOOL CreatePartition(HANDLE hDrive)
{
CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {0}};
CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {{0}}};
DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0};
BOOL r;
DWORD size;
@ -1354,8 +1354,10 @@ DWORD WINAPI ISOScanThread(LPVOID param)
safe_free(iso_path);
goto out;
}
uprintf("ISO label: '%s'\r\n Size: %lld bytes\r\n Has a >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<NB_OLD_C32; i++) {
@ -1922,7 +1924,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
}
fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem));
pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme));
if ((pt == PT_GPT) && ((!iso_report.has_efi) || ((fs > 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;
}
}

View File

@ -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)
{

View File

@ -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"

122
src/vhd.c Normal file
View File

@ -0,0 +1,122 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Virtual Disk Handling functions
* Copyright (c) 2013 Pete Batard <pete@akeo.ie>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}