1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[togo] Add Windows To Go support - part 1

* Add apply support to WIM image handling
* Requires Windows 8 or later (Windows 7+ for WIM API support but Windows 8+ for ISO mount)
* Also fix an issue for Windows 7 x64 EFI mode
This commit is contained in:
Pete Batard 2015-01-15 01:45:10 +00:00
parent 99c24d6c88
commit 295650a8b4
6 changed files with 245 additions and 43 deletions

View file

@ -1305,10 +1305,10 @@ DWORD WINAPI FormatThread(void* param)
LARGE_INTEGER li; LARGE_INTEGER li;
uint64_t wb; uint64_t wb;
uint8_t *buffer = NULL, *aligned_buffer; uint8_t *buffer = NULL, *aligned_buffer;
char *bb_msg, *guid_volume = NULL; char *bb_msg, *guid_volume = NULL, *mounted_iso;
char drive_name[] = "?:\\"; char drive_name[] = "?:\\";
char drive_letters[27]; char drive_letters[27];
char logfile[MAX_PATH], *userdir; char logfile[MAX_PATH], image[128], *userdir;
char wim_image[] = "?:\\sources\\install.wim"; char wim_image[] = "?:\\sources\\install.wim";
char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; char efi_dst[] = "?:\\efi\\boot\\bootx64.efi";
char kolibri_dst[] = "?:\\MTLD_F32"; char kolibri_dst[] = "?:\\MTLD_F32";
@ -1693,7 +1693,26 @@ DWORD WINAPI FormatThread(void* param)
UpdateProgress(OP_DOS, 0.0f); UpdateProgress(OP_DOS, 0.0f);
PrintInfoDebug(0, MSG_231); PrintInfoDebug(0, MSG_231);
drive_name[2] = 0; drive_name[2] = 0;
if (!ExtractISO(image_path, drive_name, FALSE)) { // TODO: Check that we have apply-wim support
if (HAS_TOGO(iso_report) && (Button_GetCheck(GetDlgItem(hMainDialog, IDC_WINDOWS_TO_GO)) == BST_CHECKED)) {
uprintf("Windows To Go mode selected");
mounted_iso = MountISO(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount ISO for Windows To Go installation");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
} else {
uprintf("Mounted ISO as '%s'", mounted_iso);
static_sprintf(image, "%s\\sources\\install.wim", mounted_iso);
if (!WimApplyImage(image, 1, drive_name)) {
uprintf("Failed to setup Windows To Go");
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
}
UnMountISO();
}
if (IS_ERROR(FormatStatus))
goto out;
} else if (!ExtractISO(image_path, drive_name, FALSE)) {
if (!IS_ERROR(FormatStatus)) if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
goto out; goto out;
@ -1706,7 +1725,8 @@ DWORD WINAPI FormatThread(void* param)
uprintf("Warning: loader installation failed - KolibriOS will not boot!\n"); uprintf("Warning: loader installation failed - KolibriOS will not boot!\n");
} }
} }
if ((bt == BT_UEFI) && (!iso_report.has_efi)) { // EFI mode selected, with no 'bootx64.efi' (bit #2) but Windows 7 x64's 'bootmgr.efi' (bit #0)
if ((bt == BT_UEFI) && (!(iso_report.has_efi & 4)) && (iso_report.has_efi & 1)) {
PrintInfoDebug(0, MSG_232); PrintInfoDebug(0, MSG_232);
wim_image[0] = drive_name[0]; wim_image[0] = drive_name[0];
efi_dst[0] = drive_name[0]; efi_dst[0] = drive_name[0];

View file

@ -290,6 +290,7 @@ static void print_extracted_file(char* psz_fullpath, int64_t i_file_length)
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE)); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE));
uprintf("Extracting: %s\n", psz_fullpath); uprintf("Extracting: %s\n", psz_fullpath);
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE)); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE));
// TODO: I don't think we need both of these...
SendMessageLU(GetDlgItem(hMainDialog, IDC_STATUS), SB_SETTEXTW, SBT_OWNERDRAW, psz_fullpath); SendMessageLU(GetDlgItem(hMainDialog, IDC_STATUS), SB_SETTEXTW, SBT_OWNERDRAW, psz_fullpath);
PrintStatus(0, MSG_000, psz_fullpath); // MSG_000 is "%s" PrintStatus(0, MSG_000, psz_fullpath); // MSG_000 is "%s"
// ISO9660 cannot handle backslashes // ISO9660 cannot handle backslashes

View file

@ -1960,8 +1960,6 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
break; break;
#ifdef RUFUS_TEST #ifdef RUFUS_TEST
case IDC_TEST: case IDC_TEST:
uprintf("Mounted ISO: '%s'", MountISO("D:\\ISOs\\archlinux-2012.08.04-dual.iso"));
#if 0
if (format_thid != NULL) { if (format_thid != NULL) {
return (INT_PTR)TRUE; return (INT_PTR)TRUE;
} }
@ -2009,7 +2007,6 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
} }
if (format_thid == NULL) if (format_thid == NULL)
format_op_in_progress = FALSE; format_op_in_progress = FALSE;
#endif
break; break;
#endif #endif
case IDC_LANG: case IDC_LANG:

View file

@ -411,8 +411,9 @@ extern char* insert_section_data(const char* filename, const char* section, cons
extern char* replace_in_token_data(const char* filename, const char* token, const char* src, const char* rep, BOOL dos2unix); extern char* replace_in_token_data(const char* filename, const char* token, const char* src, const char* rep, BOOL dos2unix);
extern char* replace_char(const char* src, const char c, const char* rep); extern char* replace_char(const char* src, const char c, const char* rep);
extern void parse_update(char* buf, size_t len); extern void parse_update(char* buf, size_t len);
extern BOOL WimExtractCheck(void); extern uint8_t WimExtractCheck(void);
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst); extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst);
extern BOOL WimApplyImage(const char* image, int index, const char* dst);
extern BOOL IsHDImage(const char* path); extern BOOL IsHDImage(const char* path);
extern BOOL AppendVHDFooter(const char* vhd_path); extern BOOL AppendVHDFooter(const char* vhd_path);
extern int IsHDD(DWORD DriveIndex, uint16_t vid, uint16_t pid, const char* strid); extern int IsHDD(DWORD DriveIndex, uint16_t vid, uint16_t pid, const char* strid);

View file

@ -32,7 +32,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 242, 376 IDD_DIALOG DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Rufus 2.0.0.573" CAPTION "Rufus 2.0.0.574"
FONT 8, "Segoe UI", 400, 0, 0x1 FONT 8, "Segoe UI", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,127,339,50,14 DEFPUSHBUTTON "Start",IDC_START,127,339,50,14
@ -157,7 +157,7 @@ END
IDD_DIALOG_XP DIALOGEX 12, 12, 242, 376 IDD_DIALOG_XP DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Rufus 2.0.0.573" CAPTION "Rufus 2.0.0.574"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,127,339,50,14 DEFPUSHBUTTON "Start",IDC_START,127,339,50,14
@ -283,7 +283,7 @@ END
IDD_DIALOG_RTL DIALOGEX 12, 12, 242, 376 IDD_DIALOG_RTL DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL EXSTYLE WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL
CAPTION "Rufus 2.0.0.573" CAPTION "Rufus 2.0.0.574"
FONT 8, "Segoe UI", 400, 0, 0x1 FONT 8, "Segoe UI", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,127,339,50,14 DEFPUSHBUTTON "Start",IDC_START,127,339,50,14
@ -415,7 +415,7 @@ END
IDD_DIALOG_RTL_XP DIALOGEX 12, 12, 242, 376 IDD_DIALOG_RTL_XP DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL EXSTYLE WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL
CAPTION "Rufus 2.0.0.573" CAPTION "Rufus 2.0.0.574"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,127,339,50,14 DEFPUSHBUTTON "Start",IDC_START,127,339,50,14
@ -669,8 +669,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,0,0,573 FILEVERSION 2,0,0,574
PRODUCTVERSION 2,0,0,573 PRODUCTVERSION 2,0,0,574
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -687,13 +687,13 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "2.0.0.573" VALUE "FileVersion", "2.0.0.574"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2015 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2015 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe" VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "2.0.0.573" VALUE "ProductVersion", "2.0.0.574"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

235
src/vhd.c
View file

@ -1,7 +1,7 @@
/* /*
* Rufus: The Reliable USB Formatting Utility * Rufus: The Reliable USB Formatting Utility
* Virtual Disk Handling functions * Virtual Disk Handling functions
* Copyright © 2013-2014 Pete Batard <pete@akeo.ie> * Copyright © 2013-2015 Pete Batard <pete@akeo.ie>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@
#include <time.h> #include <time.h>
#include "rufus.h" #include "rufus.h"
#include "resource.h"
#include "msapi_utf8.h" #include "msapi_utf8.h"
#include "drive.h" #include "drive.h"
#include "registry.h" #include "registry.h"
@ -98,11 +99,14 @@ typedef struct vhd_footer {
PF_TYPE_DECL(WINAPI, HANDLE, WIMCreateFile, (PWSTR, DWORD, DWORD, DWORD, DWORD, PDWORD)); PF_TYPE_DECL(WINAPI, HANDLE, WIMCreateFile, (PWSTR, DWORD, DWORD, DWORD, DWORD, PDWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMSetTemporaryPath, (HANDLE, PWSTR)); PF_TYPE_DECL(WINAPI, BOOL, WIMSetTemporaryPath, (HANDLE, PWSTR));
PF_TYPE_DECL(WINAPI, HANDLE, WIMLoadImage, (HANDLE, DWORD)); PF_TYPE_DECL(WINAPI, HANDLE, WIMLoadImage, (HANDLE, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMApplyImage, (HANDLE, PCWSTR, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMExtractImagePath, (HANDLE, PWSTR, PWSTR, DWORD)); PF_TYPE_DECL(WINAPI, BOOL, WIMExtractImagePath, (HANDLE, PWSTR, PWSTR, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMCloseHandle, (HANDLE)); PF_TYPE_DECL(WINAPI, BOOL, WIMCloseHandle, (HANDLE));
PF_TYPE_DECL(WINAPI, DWORD, WIMRegisterMessageCallback, (HANDLE, FARPROC, PVOID));
PF_TYPE_DECL(WINAPI, DWORD, WIMUnregisterMessageCallback, (HANDLE, FARPROC));
PF_TYPE_DECL(RPC_ENTRY, RPC_STATUS, UuidCreate, (UUID __RPC_FAR*)); PF_TYPE_DECL(RPC_ENTRY, RPC_STATUS, UuidCreate, (UUID __RPC_FAR*));
static BOOL has_wimgapi = FALSE, has_7z = FALSE; static uint8_t wim_flags = 0;
static char sevenzip_path[MAX_PATH]; static char sevenzip_path[MAX_PATH];
static const char conectix_str[] = VHD_FOOTER_COOKIE; static const char conectix_str[] = VHD_FOOTER_COOKIE;
@ -313,21 +317,36 @@ out:
return iso_report.is_bootable_img; return iso_report.is_bootable_img;
} }
// Find out if we have any way to extract WIM files on this platform #define WIM_HAS_API_EXTRACT 1
BOOL WimExtractCheck(void) #define WIM_HAS_7Z_EXTRACT 2
#define WIM_HAS_API_APPLY 4
#define WIM_HAS_EXTRACT(r) (r & (WIM_HAS_API_EXTRACT|WIM_HAS_7Z_EXTRACT))
// Find out if we have any way to extract/apply WIM files on this platform
// Returns a bitfield of the methods we can use (1 = Extract using wimgapi, 2 = Extract using 7-Zip, 4 = Apply using wimgapi)
uint8_t WimExtractCheck(void)
{ {
PF_INIT(WIMCreateFile, Wimgapi); PF_INIT(WIMCreateFile, Wimgapi);
PF_INIT(WIMSetTemporaryPath, Wimgapi); PF_INIT(WIMSetTemporaryPath, Wimgapi);
PF_INIT(WIMLoadImage, Wimgapi); PF_INIT(WIMLoadImage, Wimgapi);
PF_INIT(WIMApplyImage, Wimgapi);
PF_INIT(WIMExtractImagePath, Wimgapi); PF_INIT(WIMExtractImagePath, Wimgapi);
PF_INIT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT(WIMUnregisterMessageCallback, Wimgapi);
PF_INIT(WIMCloseHandle, Wimgapi); PF_INIT(WIMCloseHandle, Wimgapi);
has_wimgapi = (pfWIMCreateFile && pfWIMSetTemporaryPath && pfWIMLoadImage && pfWIMExtractImagePath && pfWIMCloseHandle); if (pfWIMCreateFile && pfWIMSetTemporaryPath && pfWIMLoadImage && pfWIMExtractImagePath && pfWIMCloseHandle)
has_7z = Get7ZipPath(); wim_flags |= WIM_HAS_API_EXTRACT;
if (Get7ZipPath())
wim_flags |= WIM_HAS_7Z_EXTRACT;
if ((wim_flags & WIM_HAS_API_EXTRACT) && pfWIMApplyImage && pfWIMRegisterMessageCallback && pfWIMUnregisterMessageCallback)
wim_flags |= WIM_HAS_API_APPLY;
uprintf("WIM extraction method(s) supported: %s%s%s\n", has_7z?"7z":(has_wimgapi?"":"NONE"), uprintf("WIM extraction method(s) supported: %s%s%s", (wim_flags & WIM_HAS_7Z_EXTRACT)?"7-Zip":
(has_wimgapi && has_7z)?", ":"", has_wimgapi?"wimgapi.dll":""); ((wim_flags & WIM_HAS_API_EXTRACT)?"":"NONE"),
return (has_wimgapi || has_7z); (WIM_HAS_EXTRACT(wim_flags))?", ":"", (wim_flags & WIM_HAS_API_EXTRACT)?"wimgapi.dll":"");
uprintf("WIM apply method supported: %s", (wim_flags & WIM_HAS_API_APPLY)?"wimgapi.dll":"NONE");
return wim_flags;
} }
@ -351,32 +370,32 @@ static BOOL WimExtractFile_API(const char* image, int index, const char* src, co
PF_INIT_OR_OUT(WIMExtractImagePath, Wimgapi); PF_INIT_OR_OUT(WIMExtractImagePath, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi); PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
uprintf("Opening: %s:[%d] (API)\n", image, index); uprintf("Opening: %s:[%d] (API)", image, index);
if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) { if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) {
uprintf(" Could not fetch temp path: %s\n", WindowsErrorString()); uprintf(" Could not fetch temp path: %s", WindowsErrorString());
goto out; goto out;
} }
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING, 0, 0, &dw); hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING, 0, 0, &dw);
if (hWim == NULL) { if (hWim == NULL) {
uprintf(" Could not access image: %s\n", WindowsErrorString()); uprintf(" Could not access image: %s", WindowsErrorString());
goto out; goto out;
} }
if (!pfWIMSetTemporaryPath(hWim, wtemp)) { if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
uprintf(" Could not set temp path: %s\n", WindowsErrorString()); uprintf(" Could not set temp path: %s", WindowsErrorString());
goto out; goto out;
} }
hImage = pfWIMLoadImage(hWim, (DWORD)index); hImage = pfWIMLoadImage(hWim, (DWORD)index);
if (hImage == NULL) { if (hImage == NULL) {
uprintf(" Could not set index: %s\n", WindowsErrorString()); uprintf(" Could not set index: %s", WindowsErrorString());
goto out; goto out;
} }
uprintf("Extracting: %s (From %s)\n", dst, src); uprintf("Extracting: %s (From %s)", dst, src);
if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) { if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) {
uprintf(" Could not extract file: %s\n", WindowsErrorString()); uprintf(" Could not extract file: %s", WindowsErrorString());
goto out; goto out;
} }
r = TRUE; r = TRUE;
@ -384,10 +403,10 @@ static BOOL WimExtractFile_API(const char* image, int index, const char* src, co
out: out:
if ((hImage != NULL) || (hWim != NULL)) { if ((hImage != NULL) || (hWim != NULL)) {
uprintf("Closing: %s\n", image); uprintf("Closing: %s", image);
}
if (hImage != NULL) pfWIMCloseHandle(hImage); if (hImage != NULL) pfWIMCloseHandle(hImage);
if (hWim != NULL) pfWIMCloseHandle(hWim); if (hWim != NULL) pfWIMCloseHandle(hWim);
}
safe_free(wimage); safe_free(wimage);
safe_free(wsrc); safe_free(wsrc);
safe_free(wdst); safe_free(wdst);
@ -403,7 +422,7 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con
char cmdline[MAX_PATH]; char cmdline[MAX_PATH];
char tmpdst[MAX_PATH]; char tmpdst[MAX_PATH];
uprintf("Opening: %s:[%d] (7-Zip)\n", image, index); uprintf("Opening: %s:[%d] (7-Zip)", image, index);
safe_strcpy(tmpdst, sizeof(tmpdst), dst); safe_strcpy(tmpdst, sizeof(tmpdst), dst);
for (i=safe_strlen(tmpdst); i>0; i--) { for (i=safe_strlen(tmpdst); i>0; i--) {
if (tmpdst[i] == '\\') if (tmpdst[i] == '\\')
@ -413,9 +432,9 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con
si.cb = sizeof(si); si.cb = sizeof(si);
safe_sprintf(cmdline, sizeof(cmdline), "7z -y e \"%s\" %d\\%s", image, index, src); safe_sprintf(cmdline, sizeof(cmdline), "7z -y e \"%s\" %d\\%s", image, index, src);
uprintf("Extracting: %s (From %s)\n", dst, src); uprintf("Extracting: %s (From %s)", dst, src);
if (!CreateProcessU(sevenzip_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, tmpdst, &si, &pi)) { if (!CreateProcessU(sevenzip_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, tmpdst, &si, &pi)) {
uprintf(" Could not launch 7z.exe: %s\n", WindowsErrorString()); uprintf(" Could not launch 7z.exe: %s", WindowsErrorString());
return FALSE; return FALSE;
} }
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
@ -425,12 +444,12 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con
safe_strcat(tmpdst, sizeof(tmpdst), "\\bootmgfw.efi"); safe_strcat(tmpdst, sizeof(tmpdst), "\\bootmgfw.efi");
if (_access(tmpdst, 0) == -1) { if (_access(tmpdst, 0) == -1) {
uprintf(" 7z.exe did not extract %s\n", tmpdst); uprintf(" 7z.exe did not extract %s", tmpdst);
return FALSE; return FALSE;
} }
// coverity[toctou] // coverity[toctou]
if (rename(tmpdst, dst) != 0) { if (rename(tmpdst, dst) != 0) {
uprintf(" Could not rename %s to %s\n", tmpdst, dst); uprintf(" Could not rename %s to %s", tmpdst, dst);
return FALSE; return FALSE;
} }
@ -440,13 +459,177 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con
// Extract a file from a WIM image // Extract a file from a WIM image
BOOL WimExtractFile(const char* image, int index, const char* src, const char* dst) BOOL WimExtractFile(const char* image, int index, const char* src, const char* dst)
{ {
if ((!has_wimgapi) && (!has_7z) && (!WimExtractCheck())) if ((wim_flags == 0) && (!WIM_HAS_EXTRACT(WimExtractCheck())))
return FALSE; return FALSE;
if ((image == NULL) || (src == NULL) || (dst == NULL)) if ((image == NULL) || (src == NULL) || (dst == NULL))
return FALSE; return FALSE;
// Prefer 7-Zip as, unsurprisingly, it's faster than the Microsoft way, // Prefer 7-Zip as, unsurprisingly, it's faster than the Microsoft way,
// but allow fallback if 7-Zip doesn't succeed // but allow fallback if 7-Zip doesn't succeed
return ( (has_7z && WimExtractFile_7z(image, index, src, dst)) return ( ((wim_flags & WIM_HAS_7Z_EXTRACT) && WimExtractFile_7z(image, index, src, dst))
|| (has_wimgapi && WimExtractFile_API(image, index, src, dst)) ); || ((wim_flags & WIM_HAS_API_EXTRACT) && WimExtractFile_API(image, index, src, dst)) );
}
// Apply image functionality
static const char *_image, *_dst;
static int _index;
// From http://msdn.microsoft.com/en-us/library/windows/desktop/dd834960.aspx
// as well as http://www.msfn.org/board/topic/150700-wimgapi-wimmountimage-progressbar/
enum WIMMessage {
WIM_MSG = WM_APP + 0x1476,
WIM_MSG_TEXT,
WIM_MSG_PROGRESS, // Indicates an update in the progress of an image application.
WIM_MSG_PROCESS, // Enables the caller to prevent a file or a directory from being captured or applied.
WIM_MSG_SCANNING, // Indicates that volume information is being gathered during an image capture.
WIM_MSG_SETRANGE, // Indicates the number of files that will be captured or applied.
WIM_MSG_SETPOS, // Indicates the number of files that have been captured or applied.
WIM_MSG_STEPIT, // Indicates that a file has been either captured or applied.
WIM_MSG_COMPRESS, // Enables the caller to prevent a file resource from being compressed during a capture.
WIM_MSG_ERROR, // Alerts the caller that an error has occurred while capturing or applying an image.
WIM_MSG_ALIGNMENT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_RETRY, // Sent when the file is being reapplied because of a network timeout.
WIM_MSG_SPLIT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_INFO, // Sent when an info message is available.
WIM_MSG_WARNING, // Sent when a warning message is available.
WIM_MSG_CHK_PROCESS,
WIM_MSG_SUCCESS = 0x00000000,
WIM_MSG_ABORT_IMAGE = 0xFFFFFFFF
};
#define INVALID_CALLBACK_VALUE 0xFFFFFFFF
// Progress callback
DWORD WINAPI WimProgressCallback(DWORD dwMsgId, WPARAM wParam, LPARAM lParam, PVOID pvIgnored)
{
PBOOL pbCancel = NULL;
char* str = NULL;
const char* level = NULL;
switch (dwMsgId) {
case WIM_MSG_PROGRESS:
uprintf(" %d%% completed", (DWORD)wParam);
UpdateProgress(OP_DOS, 1.0f*(DWORD)wParam);
break;
case WIM_MSG_PROCESS:
// The amount of files processed is a bit overwhelming, and displaying it all slows us down
//#define WIM_DISPLAY_INDIVIDUAL_FILES
#if WIM_DISPLAY_INDIVIDUAL_FILES
str = wchar_to_utf8((PWSTR)wParam);
uprintf("Applying: '%s'", str);
PrintStatus(0, MSG_000, str); // MSG_000 is "%s"
#endif
if (IS_ERROR(FormatStatus)) {
pbCancel = (PBOOL)lParam;
*pbCancel = TRUE;
}
break;
case WIM_MSG_RETRY:
level = "retry";
// fall through
case WIM_MSG_INFO:
if (level == NULL) level = "info";
// fall through
case WIM_MSG_WARNING:
if (level == NULL) level = "warning";
// fall through
case WIM_MSG_ERROR:
if (level == NULL) level = "error";
str = wchar_to_utf8((PWSTR)wParam);
SetLastError((DWORD)lParam);
uprintf("Apply %s: %s [err = %d]\n", level, str, WindowsErrorString());
break;
}
safe_free(str);
return IS_ERROR(FormatStatus)?WIM_MSG_ABORT_IMAGE:WIM_MSG_SUCCESS;
}
// Apply a WIM image using wimgapi.dll (Windows 7 or later)
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd851944.aspx
// To get progress, we must run this call within its own thread
static DWORD WINAPI WimApplyImageThread(LPVOID param)
{
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* wdst = utf8_to_wchar(_dst);
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi);
PF_INIT_OR_OUT(WIMLoadImage, Wimgapi);
PF_INIT_OR_OUT(WIMApplyImage, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
uprintf("Opening: %s:[%d]", _image, _index);
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf(" Could not set progress callback: %s", WindowsErrorString());
goto out;
}
if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) {
uprintf(" Could not fetch temp path: %s", WindowsErrorString());
goto out;
}
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING, 0, 0, &dw);
if (hWim == NULL) {
uprintf(" Could not access image: %s", WindowsErrorString());
goto out;
}
if (!pfWIMSetTemporaryPath(hWim, wtemp)) {
uprintf(" Could not set temp path: %s", WindowsErrorString());
goto out;
}
hImage = pfWIMLoadImage(hWim, (DWORD)_index);
if (hImage == NULL) {
uprintf(" Could not set index: %s", WindowsErrorString());
goto out;
}
uprintf("Applying image...");
if (!pfWIMApplyImage(hImage, wdst, 0)) {
uprintf(" Could not apply image: %s", WindowsErrorString());
goto out;
}
r = TRUE;
UpdateProgress(OP_FINALIZE, -1.0f);
out:
if ((hImage != NULL) || (hWim != NULL)) {
uprintf("Closing: %s", _image);
if (hImage != NULL) pfWIMCloseHandle(hImage);
if (hWim != NULL) pfWIMCloseHandle(hWim);
}
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
safe_free(wimage);
safe_free(wdst);
ExitThread((DWORD)r);
}
BOOL WimApplyImage(const char* image, int index, const char* dst)
{
_image = image;
_index = index;
_dst = dst;
HANDLE handle;
DWORD dw = 0;
handle = CreateThread(NULL, 0, WimApplyImageThread, NULL, 0, NULL);
if (handle == NULL) {
uprintf("Unable to start apply-image thread");
return FALSE;
}
WaitForSingleObject(handle, INFINITE);
GetExitCodeThread(handle, &dw);
return (BOOL)dw;
} }