Browse Source

[wue] switch to using wimlib for all WIM operations

* This should greatly improves performance when patching boot.wim for WUE or creating Windows To Go drives.
* This also allows us to discard all the bulky (and limited) native WIM API function calls.
* Also add some UTF-8 wrappers to wimlib and a GetTempDirNameU() function call.
* Also revert to the old ssize_t definition in unistd.h, since trying to be too smart throws static analysers off...
pull/2746/head
Pete Batard 3 months ago
parent
commit
2470c56c7e
No known key found for this signature in database GPG Key ID: 38E0CF5E69EDD671
  1. 2
      src/Makefile.am
  2. 2
      src/Makefile.in
  3. 3
      src/format.c
  4. 11
      src/msapi_utf8.h
  5. 9
      src/msvc-missing/unistd.h
  6. 43
      src/rufus.c
  7. 10
      src/rufus.rc
  8. 773
      src/vhd.c
  9. 47
      src/vhd.h
  10. 73
      src/wimlib/wimlib.h
  11. 286
      src/wue.c

2
src/Makefile.am

@ -20,7 +20,7 @@ AM_V_WINDRES = $(AM_V_WINDRES_$(V))
rufus_SOURCES = badblocks.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.c hash.c icon.c iso.c \
localization.c net.c parser.c pki.c process.c re.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c ui.c vhd.c wue.c xml.c
rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio -I$(srcdir)/wimlib -I$(srcdir)/../res $(AM_CFLAGS) \
-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus
-DEXT2_FLAT_INCLUDES=0 -D_RUFUS -DSOLUTION=rufus
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows -L $(srcdir)/../.mingw
rufus_LDADD = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a syslinux/win/libwin.a \
libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a wimlib/libwim.a $(NONVULNERABLE_LIBS) $(VULNERABLE_LIBS)

2
src/Makefile.in

@ -289,7 +289,7 @@ rufus_SOURCES = badblocks.c dev.c dos.c dos_locale.c drive.c format.c format_ext
localization.c net.c parser.c pki.c process.c re.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c ui.c vhd.c wue.c xml.c
rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio -I$(srcdir)/wimlib -I$(srcdir)/../res $(AM_CFLAGS) \
-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus
-DEXT2_FLAT_INCLUDES=0 -D_RUFUS -DSOLUTION=rufus
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows -L $(srcdir)/../.mingw
rufus_LDADD = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a syslinux/win/libwin.a \

3
src/format.c

@ -73,7 +73,6 @@ static unsigned int sec_buf_pos = 0;
extern const int nb_steps[FS_MAX];
extern const char* md5sum_name[2];
extern uint32_t dur_mins, dur_secs;
extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing;
extern BOOL write_as_image, use_vds, write_as_esp, is_vds_available, has_ffu_support, use_rufus_mbr;
extern char* archive_path;
@ -1960,7 +1959,7 @@ DWORD WINAPI FormatThread(void* param)
ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_PATCH));
} else {
efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = '\\';
if (!WimExtractFile(img_report.wininst_path[0], 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst, FALSE)) {
if (!WimExtractFile(img_report.wininst_path[0], 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst)) {
uprintf("Failed to setup Win7 EFI boot");
ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_PATCH));
}

11
src/msapi_utf8.h

@ -750,6 +750,17 @@ static __inline DWORD GetTempFileNameU(char* lpPathName, char* lpPrefixString, U
return ret;
}
static __inline DWORD GetTempDirNameU(char* lpPathName, char* lpPrefixString, UINT uUnique, char* lpTempDirName)
{
DWORD ret = GetTempFileNameU(lpPathName, lpPrefixString, uUnique, lpTempDirName);
if (ret != 0) {
DeleteFileU(lpTempDirName);
if (!CreateDirectoryU(lpTempDirName, NULL))
ret = 0;
}
return ret;
}
static __inline DWORD GetModuleFileNameU(HMODULE hModule, char* lpFilename, DWORD nSize)
{
DWORD ret = 0, err = ERROR_INVALID_DATA;

9
src/msvc-missing/unistd.h

@ -24,10 +24,11 @@ typedef unsigned short mode_t;
#ifndef _SSIZE_T_DEFINED
#define _SSIZE_T_DEFINED
#undef ssize_t
/* From https://awesomekling.github.io/How-SerenityOS-declares-ssize_t/ */
#define unsigned signed
typedef size_t ssize_t;
#undef unsigned
#ifdef _WIN64
typedef __int64 ssize_t;
#else
typedef int ssize_t;
#endif /* _WIN64 */
#endif /* _SSIZE_T_DEFINED */
/* ext2fs needs this, which we picked from libcdio-driver/filemode.h */

43
src/rufus.c

@ -90,7 +90,7 @@ static char szTimer[12] = "00:00:00";
static unsigned int timer;
static char uppercase_select[2][64], uppercase_start[64], uppercase_close[64], uppercase_cancel[64];
extern HANDLE update_check_thread, wim_thread;
extern HANDLE update_check_thread;
extern HIMAGELIST hUpImageList, hDownImageList;
extern BOOL enable_iso, enable_joliet, enable_rockridge, enable_extra_hashes, is_bootloader_revoked;
extern BOOL validate_md5sum, cpu_has_sha1_accel, cpu_has_sha256_accel;
@ -1303,7 +1303,7 @@ DWORD WINAPI ImageScanThread(LPVOID param)
// coverity[swapped_arguments]
if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, tmp_path) != 0) {
// Only look at index 1 for now. If people complain, we may look for more.
if (WimExtractFile(image_path, 1, "Windows\\Boot\\EFI\\bootmgr.efi", tmp_path, TRUE)) {
if (WimExtractFile(image_path, 1, "Windows\\Boot\\EFI\\bootmgr.efi", tmp_path)) {
arch = FindArch(tmp_path);
if (arch != 0) {
uprintf(" Image contains a%s %s EFI boot manager",
@ -1548,12 +1548,6 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
MessageBoxExU(hMainDialog, lmprintf(MSG_091), lmprintf(MSG_090), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
goto out;
}
if (HAS_WIN7_EFI(img_report) && (!WimExtractCheck(FALSE))) {
// Your platform cannot extract files from WIM archives => download 7-zip?
if (MessageBoxExU(hMainDialog, lmprintf(MSG_102), lmprintf(MSG_101), MB_YESNO | MB_ICONERROR | MB_IS_RTL, selected_langid) == IDYES)
ShellExecuteA(hMainDialog, "open", SEVENZIP_URL, NULL, NULL, SW_SHOWNORMAL);
goto out;
}
} else if ( ((fs_type == FS_NTFS) && !HAS_WINDOWS(img_report) && !HAS_GRUB(img_report) &&
(!HAS_SYSLINUX(img_report) || (SL_MAJOR(img_report.sl_version) <= 5)))
|| ((IS_FAT(fs_type)) && (!HAS_SYSLINUX(img_report)) && (!allow_dual_uefi_bios) && !IS_EFI_BOOTABLE(img_report) &&
@ -3825,39 +3819,10 @@ relaunch:
}
#if defined(_DEBUG) || defined(TEST) || defined(ALPHA)
extern int TestHashes(void);
#include "wimlib.h"
// Ctrl-T => Alternate Test mode that doesn't require a full rebuild
if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN)))
&& (msg.wParam == 'T')) {
int r;
WIMStruct* wim;
r = wimlib_open_wim(L"D:\\Incoming\\Win11_24H2_EnglishInternational_x64.iso|sources/boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim);
// r = wimlib_open_wim(L"C:\\tmp\\test.iso|sources/boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim);
// r = wimlib_open_wim(L"C:\\tmp\\boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim);
if (r == 0) {
int image;
wchar_t* xml;
size_t xml_size;
wimlib_print_header(wim);
if (wimlib_get_xml_data(wim, (void**) & xml, &xml_size) == 0) {
xml[(xml_size / sizeof(wchar_t)) - 1] = L'\0';
wuprintf(L"%s", xml);
free(xml);
}
image = wimlib_resolve_image(wim, L"1");
uprintf("image = %d", image);
const wchar_t* fn = L"Windows/win.ini";
r = wimlib_extract_paths(wim, image, L"C:\\tmp\\wim", &fn, 1, WIMLIB_EXTRACT_FLAG_NORPFIX |
WIMLIB_EXTRACT_FLAG_GLOB_PATHS | WIMLIB_EXTRACT_FLAG_STRICT_GLOB | WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE);
if (r == 0)
uprintf("Successfully extracted '%S'", fn);
else
uprintf("Failed to extract '%S': %d", fn, r);
wimlib_free(wim);
} else {
uprintf("Failed to open WIM: %d", r);
}
// TestHashes();
TestHashes();
continue;
}
#endif
@ -3886,8 +3851,6 @@ extern int TestHashes(void);
WriteSetting32(SETTING_DEFAULT_THREAD_PRIORITY, default_thread_priority - THREAD_PRIORITY_ABOVE_NORMAL);
if (format_thread != NULL)
SetThreadPriority(format_thread, default_thread_priority);
if (wim_thread != NULL)
SetThreadPriority(wim_thread, default_thread_priority);
}
PrintStatus(STATUS_MSG_TIMEOUT, MSG_318, default_thread_priority);
continue;

10
src/rufus.rc

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.8.2239"
CAPTION "Rufus 4.8.2240"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -407,8 +407,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,8,2239,0
PRODUCTVERSION 4,8,2239,0
FILEVERSION 4,8,2240,0
PRODUCTVERSION 4,8,2240,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -426,13 +426,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.8.2239"
VALUE "FileVersion", "4.8.2240"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2025 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.8.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.8.2239"
VALUE "ProductVersion", "4.8.2240"
END
END
BLOCK "VarFileInfo"

773
src/vhd.c

@ -1,7 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Virtual Disk Handling functions
* Copyright © 2013-2024 Pete Batard <pete@akeo.ie>
* Copyright © 2013-2025 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
@ -41,56 +41,19 @@
#include "msapi_utf8.h"
#include "drive.h"
#include "wimlib.h"
#include "registry.h"
#include "bled/bled.h"
// WIM API Prototypes
PF_TYPE_DECL(WINAPI, HANDLE, WIMCreateFile, (PWSTR, DWORD, DWORD, DWORD, DWORD, PDWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMSetTemporaryPath, (HANDLE, PWSTR));
PF_TYPE_DECL(WINAPI, HANDLE, WIMLoadImage, (HANDLE, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMMountImage, (PCWSTR, PCWSTR, DWORD, PCWSTR));
PF_TYPE_DECL(WINAPI, BOOL, WIMUnmountImage, (PCWSTR, PCWSTR, DWORD, BOOL));
PF_TYPE_DECL(WINAPI, BOOL, WIMApplyImage, (HANDLE, PCWSTR, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMExtractImagePath, (HANDLE, PWSTR, PWSTR, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMGetImageInformation, (HANDLE, PVOID, PDWORD));
PF_TYPE_DECL(WINAPI, BOOL, WIMCloseHandle, (HANDLE));
PF_TYPE_DECL(WINAPI, DWORD, WIMRegisterMessageCallback, (HANDLE, FARPROC, PVOID));
PF_TYPE_DECL(WINAPI, DWORD, WIMUnregisterMessageCallback, (HANDLE, FARPROC));
typedef struct {
int index;
BOOL commit;
const char* image;
const char* dst;
} mount_params_t;
uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
HANDLE wim_thread = NULL;
extern int default_thread_priority;
extern char* save_image_type;
extern BOOL ignore_boot_marker, has_ffu_support;
extern RUFUS_DRIVE rufus_drive[MAX_DRIVES];
extern HANDLE format_thread;
static uint8_t wim_flags = 0;
static uint32_t progress_report_mask;
static uint64_t progress_offset = 0, progress_total = 100;
static wchar_t wmount_path[MAX_PATH] = { 0 }, wmount_track[MAX_PATH] = { 0 };
static char sevenzip_path[MAX_PATH], physical_path[128] = "";
static char physical_path[128] = "";
static int progress_op = OP_FILE_COPY, progress_msg = MSG_267;
static BOOL count_files;
static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
static BOOL Get7ZipPath(void)
{
if ( (GetRegistryKeyStr(REGKEY_HKCU, "Software\\7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path)))
|| (GetRegistryKeyStr(REGKEY_HKLM, "Software\\7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) ) {
static_strcat(sevenzip_path, "\\7z.exe");
return (_accessU(sevenzip_path, 0) != -1);
}
return FALSE;
}
typedef struct {
const char* ext;
uint8_t type;
@ -232,681 +195,115 @@ out:
}
// WIM operations progress callback
DWORD WINAPI WimProgressCallback(DWORD dwMsgId, WPARAM wParam, LPARAM lParam, PVOID pvIgnored)
static enum wimlib_progress_status WimProgressFunc(enum wimlib_progress_msg msg_type,
union wimlib_progress_info* info, void* progctx)
{
PBOOL pbCancel = NULL;
PWIN32_FIND_DATA pFileData;
const char* level = NULL;
uint64_t size;
static BOOL init[3] = { 0 };
if IS_ERROR(ErrorStatus)
return WIMLIB_PROGRESS_STATUS_ABORT;
switch (dwMsgId) {
case WIM_MSG_PROGRESS:
// The default WIM progress is useless for apply (freezes at 95%, which is usually when
// only half the files have been processed), so we only use it for mounting/unmounting.
if (!(progress_report_mask & WIM_REPORT_PROGRESS))
break;
UpdateProgressWithInfo(progress_op, progress_msg, progress_offset + wParam, progress_total);
switch (msg_type) {
case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
memset(init, 0, sizeof(init));
uprintf("Applying image %d (\"%S\") from '%S' to '%S'",
info->extract.image, info->extract.image_name,
info->extract.wimfile_name, info->extract.target);
break;
case WIM_MSG_PROCESS:
if (!(progress_report_mask & WIM_REPORT_PROCESS))
break;
// The amount of files processed is overwhelming (16k+ for a typical image),
// and trying to display it *WILL* slow us down, so we don't.
#if 0
uprintf("%S", (PWSTR)wParam);
PrintStatus(0, MSG_000, str); // MSG_000 is "%s"
#endif
if (count_files) {
wim_nb_files++;
} else {
// At the end of an actual apply, the WIM API re-lists a bunch of directories it already processed,
// so, even as we try to compensate, we might end up with more entries than counted - ignore those.
if (wim_proc_files < wim_nb_files)
wim_proc_files++;
else
wim_extra_files++;
UpdateProgressWithInfo(progress_op, progress_msg, wim_proc_files, wim_nb_files);
case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
if (!init[0]) {
uprintf("Creating file structure...");
init[0] = TRUE;
uprint_progress(0, 0);
}
// Halt on error
if (IS_ERROR(ErrorStatus)) {
pbCancel = (PBOOL)lParam;
*pbCancel = TRUE;
break;
UpdateProgressWithInfoUpTo(98, progress_op, progress_msg, info->extract.current_file_count, info->extract.end_file_count * 6);
uprint_progress(info->extract.current_file_count, info->extract.end_file_count);
break;
case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
if (!init[1]) {
uprintf("\nExtracting file data...");
init[1] = TRUE;
uprint_progress(0, 0);
}
UpdateProgressWithInfoUpTo(98, progress_op, progress_msg, info->extract.total_bytes + (4 * info->extract.completed_bytes), info->extract.total_bytes * 6);
uprint_progress(info->extract.completed_bytes, info->extract.total_bytes);
break;
case WIM_MSG_FILEINFO:
if (!(progress_report_mask & WIM_REPORT_FILEINFO))
break;
pFileData = (PWIN32_FIND_DATA)lParam;
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
uprintf("Creating: %S", (PWSTR)wParam);
} else {
size = (((uint64_t)pFileData->nFileSizeHigh) << 32) + pFileData->nFileSizeLow;
uprintf("Extracting: %S (%s)", (PWSTR)wParam, SizeToHumanReadable(size, FALSE, FALSE));
case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
if (!init[2]) {
uprintf("\nApplying metadata to files...");
init[2] = TRUE;
uprint_progress(0, 0);
}
UpdateProgressWithInfoUpTo(98, progress_op, progress_msg, info->extract.current_file_count + (5 * info->extract.end_file_count), info->extract.end_file_count * 6);
uprint_progress(info->extract.current_file_count, info->extract.end_file_count);
if (info->extract.current_file_count >= info->extract.end_file_count)
uprintf("\n");
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";
SetLastError((DWORD)lParam);
uprintf("WIM processing %s: %S [%s]\n", level, (PWSTR)wParam, WindowsErrorString());
default:
break;
}
return IS_ERROR(ErrorStatus) ? WIM_MSG_ABORT_IMAGE : WIM_MSG_SUCCESS;
}
// 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(BOOL bSilent)
{
PF_INIT(WIMCreateFile, Wimgapi);
PF_INIT(WIMSetTemporaryPath, Wimgapi);
PF_INIT(WIMLoadImage, Wimgapi);
PF_INIT(WIMApplyImage, Wimgapi);
PF_INIT(WIMExtractImagePath, Wimgapi);
PF_INIT(WIMGetImageInformation, Wimgapi);
PF_INIT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT(WIMUnregisterMessageCallback, Wimgapi);
PF_INIT(WIMCloseHandle, Wimgapi);
if (pfWIMCreateFile && pfWIMSetTemporaryPath && pfWIMLoadImage && pfWIMExtractImagePath && pfWIMCloseHandle)
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;
suprintf("WIM extraction method(s) supported: %s%s%s", (wim_flags & WIM_HAS_7Z_EXTRACT)?"7-Zip":
((wim_flags & WIM_HAS_API_EXTRACT)?"":"NONE"),
(WIM_HAS_EXTRACT(wim_flags) == (WIM_HAS_API_EXTRACT|WIM_HAS_7Z_EXTRACT))?", ":
"", (wim_flags & WIM_HAS_API_EXTRACT)?"wimgapi.dll":"");
suprintf("WIM apply method supported: %s", (wim_flags & WIM_HAS_API_APPLY)?"wimgapi.dll":"NONE");
return wim_flags;
}
//
// Looks like Microsoft's idea of "mount" for WIM images involves the creation
// of a as many virtual junctions as there exist directories on the image...
// So, yeah, this is both very slow and wasteful of space.
//
// NB: You can see mounted WIMs, along with their mountpoint, by checking:
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WIMMount\Mounted Images
// You can also mount/unmount images from an elevated prompt with something like:
// dism /mount-image [/readonly] /imagefile:F:\sources\boot.wim /index:2 /mountdir:C:\test\offline
// dism /unmount-image /discard /mountdir:C:\test\offline
//
static DWORD WINAPI WimMountImageThread(LPVOID param)
{
BOOL r = FALSE;
wconvert(temp_dir);
mount_params_t* mp = (mount_params_t*)param;
wchar_t* wimage = utf8_to_wchar(mp->image);
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMMountImage, Wimgapi);
PF_INIT_OR_OUT(WIMUnmountImage, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
if (wmount_path[0] != 0) {
uprintf("WimMountImage: An image is already mounted. Trying to unmount it...");
if (pfWIMUnmountImage(wmount_path, wimage, mp->index, FALSE))
uprintf("WimMountImage: Successfully unmounted existing image..");
else
goto out;
}
if (GetTempFileNameW(wtemp_dir, L"Ruf", 0, wmount_path) == 0) {
uprintf("WimMountImage: Can not generate mount directory: %s", WindowsErrorString());
goto out;
}
DeleteFileW(wmount_path);
if (!CreateDirectoryW(wmount_path, 0)) {
uprintf("WimMountImage: Can not create mount directory '%S': %s", wmount_path, WindowsErrorString());
goto out;
}
if (GetTempFileNameW(wtemp_dir, L"Ruf", 0, wmount_track) == 0) {
uprintf("WimMountImage: Can not generate tracking directory: %s", WindowsErrorString());
goto out;
}
DeleteFileW(wmount_track);
if (!CreateDirectoryW(wmount_track, 0)) {
uprintf("WimMountImage: Can not create tracking directory '%S': %s", wmount_track, WindowsErrorString());
goto out;
}
progress_report_mask = WIM_REPORT_PROGRESS;
progress_op = OP_PATCH;
progress_msg = MSG_325;
progress_offset = 1;
progress_total = PATCH_PROGRESS_TOTAL;
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf("WimMountImage: Could not set progress callback: %s", WindowsErrorString());
goto out;
}
r = pfWIMMountImage(wmount_path, wimage, mp->index, wmount_track);
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
if (!r) {
uprintf("Could not mount '%S [%d]' on '%S': %s", wimage, mp->index, wmount_path, WindowsErrorString());
goto out;
}
uprintf("Mounted '%S [%d]' on '%S'", wimage, mp->index, wmount_path);
out:
if (!r) {
if (wmount_track[0] != 0) {
RemoveDirectoryW(wmount_track);
wmount_track[0] = 0;
}
if (wmount_path[0] != 0) {
RemoveDirectoryW(wmount_path);
wmount_path[0] = 0;
}
}
wfree(temp_dir);
safe_free(wimage);
ExitThread((DWORD)r);
}
// Returns the temporary mount path on success, NULL on error.
// Returned path must be freed by the caller.
char* WimMountImage(const char* image, int index)
{
char* mount_path = NULL;
DWORD dw = 0;
mount_params_t mp = { 0 };
mp.image = image;
mp.index = index;
// Try to unmount an existing stale image, if there is any
mount_path = WimGetExistingMountPoint(image, index);
if (mount_path != NULL) {
uprintf("WARNING: Found stale '%s [%d]' image mounted on '%s' - Attempting to unmount it...",
image, index, mount_path);
utf8_to_wchar_no_alloc(mount_path, wmount_path, ARRAYSIZE(wmount_path));
wmount_track[0] = 0;
WimUnmountImage(image, index, FALSE);
}
wim_thread = CreateThread(NULL, 0, WimMountImageThread, &mp, 0, NULL);
if (wim_thread == NULL) {
uprintf("Unable to start mount-image thread");
return NULL;
}
SetThreadPriority(wim_thread, default_thread_priority);
WaitForSingleObject(wim_thread, INFINITE);
if (!GetExitCodeThread(wim_thread, &dw))
dw = 0;
wim_thread = NULL;
return (dw) ? wchar_to_utf8(wmount_path) : NULL;
}
static DWORD WINAPI WimUnmountImageThread(LPVOID param)
{
BOOL r = FALSE;
mount_params_t* mp = (mount_params_t*)param;
wchar_t* wimage = utf8_to_wchar(mp->image);
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMUnmountImage, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
if (wmount_path[0] == 0) {
uprintf("WimUnmountImage: No image is mounted");
goto out;
}
progress_report_mask = WIM_REPORT_PROGRESS;
progress_op = OP_PATCH;
progress_msg = MSG_325;
progress_offset = 105;
progress_total = PATCH_PROGRESS_TOTAL;
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf("WimUnmountImage: Could not set progress callback: %s", WindowsErrorString());
goto out;
}
r = pfWIMUnmountImage(wmount_path, wimage, mp->index, mp->commit);
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
if (!r) {
uprintf("Could not unmount '%S': %s", wmount_path, WindowsErrorString());
goto out;
}
uprintf("Unmounted '%S [%d]'", wmount_path, mp->index);
if (wmount_track[0] != 0 && !RemoveDirectoryW(wmount_track))
uprintf("Could not delete '%S' : %s", wmount_track, WindowsErrorString());
wmount_track[0] = 0;
if (wmount_path[0] != 0 && !RemoveDirectoryW(wmount_path))
uprintf("Could not delete '%S' : %s", wmount_path, WindowsErrorString());
wmount_path[0] = 0;
out:
safe_free(wimage);
ExitThread((DWORD)r);
}
BOOL WimUnmountImage(const char* image, int index, BOOL commit)
{
DWORD dw = 0;
mount_params_t mp = { 0 };
mp.image = image;
mp.index = index;
mp.commit = commit;
wim_thread = CreateThread(NULL, 0, WimUnmountImageThread, &mp, 0, NULL);
if (wim_thread == NULL) {
uprintf("Unable to start unmount-image thread");
return FALSE;
}
SetThreadPriority(wim_thread, default_thread_priority);
WaitForSingleObject(wim_thread, INFINITE);
if (!GetExitCodeThread(wim_thread, &dw))
dw = 0;
wim_thread = NULL;
return dw;
}
// Get the existing mount point (if any) for the image + index passed as parameters.
// Needed because Windows refuses to mount two images with the same path/index even
// if the previous has become stale or deleted. This situation may occur if the user
// force-closed Rufus when 'boot.wim' was mounted, thus leaving them unable to mount
// 'boot.wim' for subsequent sessions unless they invoke dism /Unmount-Wim manually.
// This basically parses HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WIMMount\Mounted Images
// to see if an instance exists with the image/index passed as parameter and returns
// the mount point of this image if found, or NULL otherwise.
char* WimGetExistingMountPoint(const char* image, int index)
{
static char path[MAX_PATH];
char class[MAX_PATH] = "", guid[40], key_name[MAX_PATH];
HKEY hKey;
DWORD dw = 0, i, k, nb_subkeys = 0, class_size;
DWORD cbMaxSubKey, cchMaxClass, cValues, cchMaxValue;
DWORD cbMaxValueData, cbSecurityDescriptor;
FILETIME ftLastWriteTime;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\WIMMount\\Mounted Images",
0, KEY_READ, &hKey) != ERROR_SUCCESS)
return NULL;
class_size = sizeof(class);
RegQueryInfoKeyA(hKey, class, &class_size, NULL, &nb_subkeys,
&cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue,
&cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime);
for (k = 0; k < nb_subkeys; k++) {
dw = sizeof(guid);
if (RegEnumKeyExA(hKey, k, guid, &dw, NULL, NULL, NULL,
&ftLastWriteTime) == ERROR_SUCCESS) {
static_sprintf(key_name, "SOFTWARE\\Microsoft\\WIMMount\\Mounted Images\\%s\\WIM Path", guid);
if (GetRegistryKeyStr(HKEY_LOCAL_MACHINE, key_name, path, sizeof(path)) &&
(stricmp(path, image) != 0))
continue;
static_sprintf(key_name, "SOFTWARE\\Microsoft\\WIMMount\\Mounted Images\\%s\\Image Index", guid);
if (GetRegistryKey32(HKEY_LOCAL_MACHINE, key_name, &i) && (i != (DWORD)index))
continue;
path[0] = 0;
static_sprintf(key_name, "SOFTWARE\\Microsoft\\WIMMount\\Mounted Images\\%s\\Mount Path", guid);
if (GetRegistryKeyStr(HKEY_LOCAL_MACHINE, key_name, path, sizeof(path)))
break;
}
}
if (k >= nb_subkeys)
path[0] = 0;
RegCloseKey(hKey);
return (path[0] == 0) ? NULL: path;
}
// Extract a file from a WIM image using wimgapi.dll
// NB: if you want progress from a WIM callback, you must run the WIM API call in its own thread
// (which we don't do here) as it won't work otherwise. Thanks go to Erwan for figuring this out!
BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst, BOOL bSilent)
{
static char* index_name = "[1].xml";
BOOL r = FALSE;
DWORD dw = 0;
HANDLE hWim = NULL;
HANDLE hImage = NULL;
HANDLE hFile = 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);
wchar_t* wim_info;
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);
suprintf("Opening: %s:[%d] (API)", image, index);
if (GetTempPathW(ARRAYSIZE(wtemp), wtemp) == 0) {
uprintf(" Could not fetch temp path: %s", WindowsErrorString());
goto out;
}
// Thanks to dism++ for figuring out that you can use UNDOCUMENTED FLAG 0x20000000
// to open newer install.wim/install.esd images, without running into obnoxious error:
// [0x0000000B] An attempt was made to load a program with an incorrect format.
// No thanks to Microsoft for NOT DOCUMENTING THEIR UTTER BULLSHIT with the WIM API!
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING,
(img_report.wininst_version >= SPECIAL_WIM_VERSION) ? WIM_UNDOCUMENTED_BULLSHIT : 0, 0, NULL);
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;
}
suprintf("Extracting: %s (From %s)", dst, src);
if (safe_strcmp(src, index_name) == 0) {
if (!pfWIMGetImageInformation(hWim, &wim_info, &dw) || (dw == 0)) {
uprintf(" Could not access WIM info: %s", WindowsErrorString());
goto out;
}
hFile = CreateFileW(wdst, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == INVALID_HANDLE_VALUE) || (!WriteFile(hFile, wim_info, dw, &dw, NULL))) {
suprintf(" Could not extract file: %s", WindowsErrorString());
goto out;
}
} else {
hImage = pfWIMLoadImage(hWim, (DWORD)index);
if (hImage == NULL) {
uprintf(" Could not set index: %s", WindowsErrorString());
goto out;
}
if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) {
suprintf(" Could not extract file: %s", WindowsErrorString());
goto out;
}
}
r = TRUE;
out:
if ((hImage != NULL) || (hWim != NULL)) {
suprintf("Closing: %s", image);
if (hImage != NULL) pfWIMCloseHandle(hImage);
if (hWim != NULL) pfWIMCloseHandle(hWim);
}
safe_closehandle(hFile);
safe_free(wimage);
safe_free(wsrc);
safe_free(wdst);
return r;
return WIMLIB_PROGRESS_STATUS_CONTINUE;
}
// Extract a file from a WIM image using 7-Zip
BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst, BOOL bSilent)
// Extract a file from a WIM image. Returns the allocated path of the extracted file or NULL on error.
BOOL WimExtractFile(const char* image, int index, const char* src, const char* dst)
{
int n;
size_t i;
char cmdline[MAX_PATH];
char tmpdst[MAX_PATH];
char index_prefix[] = "#\\";
suprintf("Opening: %s:[%d] (7-Zip)", image, index);
int r = 1;
WIMStruct* wim;
char tmp[MAX_PATH] = "", *p;
if ((image == NULL) || (src == NULL) || (dst == NULL))
return FALSE;
// If you shove more than 9 images in a WIM, don't come complaining
// that this breaks!
index_prefix[0] = '0' + index;
suprintf("Extracting: %s (From %s)", dst, src);
// 7z has a quirk where the image index MUST be specified if a
// WIM has multiple indexes, but it MUST be removed if there is
// only one image. Because of this (and because 7z will not
// return an error code if it can't extract the file), we need
// to issue 2 passes. See github issue #680.
for (n = 0; n < 2; n++) {
static_strcpy(tmpdst, dst);
for (i = strlen(tmpdst) - 1; (i > 0) && (tmpdst[i] != '\\') && (tmpdst[i] != '/'); i--);
tmpdst[i] = 0;
static_sprintf(cmdline, "\"%s\" -y e \"%s\" %s%s", sevenzip_path,
image, (n == 0) ? index_prefix : "", src);
if (RunCommand(cmdline, tmpdst, FALSE) != 0) {
uprintf(" Could not launch 7z.exe: %s", WindowsErrorString());
return FALSE;
}
for (i = safe_strlen(src); (i > 0) && (src[i] != '\\') && (src[i] != '/'); i--);
if (i == 0)
static_strcat(tmpdst, "\\");
static_strcat(tmpdst, &src[i]);
if (_access(tmpdst, 0) == 0)
// File was extracted => move on
break;
}
if (n >= 2) {
suprintf(" 7z.exe did not extract %s", tmpdst);
return FALSE;
}
// coverity[toctou]
if (!MoveFileExU(tmpdst, dst, MOVEFILE_REPLACE_EXISTING)) {
uprintf(" Could not rename %s to %s: %s", tmpdst, dst, WindowsErrorString());
return FALSE;
}
return TRUE;
}
// Extract a file from a WIM image
BOOL WimExtractFile(const char* image, int index, const char* src, const char* dst, BOOL bSilent)
{
if ((wim_flags == 0) && (!WIM_HAS_EXTRACT(WimExtractCheck(TRUE))))
return FALSE;
if ((image == NULL) || (src == NULL) || (dst == NULL))
return FALSE;
// Prefer 7-Zip as, unsurprisingly, it's faster than the Microsoft way,
// but allow fallback if 7-Zip doesn't succeed
return ( ((wim_flags & WIM_HAS_7Z_EXTRACT) && WimExtractFile_7z(image, index, src, dst, bSilent))
|| ((wim_flags & WIM_HAS_API_EXTRACT) && WimExtractFile_API(image, index, src, dst, bSilent)) );
}
/// <summary>
/// Find if a specific index belongs to a WIM image.
/// </summary>
/// <param name="image">The path to the WIM file.</param>
/// <param name="index">The (non-zero) value of the index to check.</param>
/// <returns>TRUE if the index was found in the image, FALSE otherwise.</returns>
BOOL WimIsValidIndex(const char* image, int index)
{
int i = 1, cur_index;
BOOL r = FALSE;
DWORD dw = 0;
HANDLE hWim = NULL;
HANDLE hFile = NULL;
char xml_file[MAX_PATH] = { 0 };
char* str;
wchar_t* wimage = utf8_to_wchar(image);
wchar_t* wim_info;
PF_INIT_OR_OUT(WIMCreateFile, Wimgapi);
PF_INIT_OR_OUT(WIMGetImageInformation, Wimgapi);
PF_INIT_OR_OUT(WIMCloseHandle, Wimgapi);
// Zero indexes are invalid
if (index == 0)
goto out;
hWim = pfWIMCreateFile(wimage, WIM_GENERIC_READ, WIM_OPEN_EXISTING,
(img_report.wininst_version >= SPECIAL_WIM_VERSION) ? WIM_UNDOCUMENTED_BULLSHIT : 0, 0, NULL);
if (hWim == NULL) {
uprintf(" Could not access image: %s", WindowsErrorString());
goto out;
}
if (!pfWIMGetImageInformation(hWim, &wim_info, &dw) || (dw == 0)) {
uprintf(" Could not access WIM info: %s", WindowsErrorString());
goto out;
}
if ((GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, xml_file) == 0) || (xml_file[0] == 0))
static_strcpy(xml_file, ".\\RufVXml.tmp");
DeleteFileU(xml_file);
hFile = CreateFileU(xml_file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == INVALID_HANDLE_VALUE) || (!WriteFile(hFile, wim_info, dw, &dw, NULL)))
goto out;
while ((str = get_token_data_file_indexed("IMAGE INDEX", xml_file, i)) != NULL) {
cur_index = atoi(str);
safe_free(str);
if (cur_index == index) {
r = TRUE;
break;
assert(strrchr(src, '\\') != NULL);
assert(strrchr(dst, '\\') != NULL);
if (strrchr(src, '\\') == NULL || strrchr(dst, '\\') == NULL)
goto out;
p = strrchr(dst, '\\');
*p = '\0';
wimlib_global_init(0);
wimlib_set_print_errors(true);
r = wimlib_open_wimU(image, 0, &wim);
if (r == 0) {
r = wimlib_extract_pathsU(wim, index, dst, &src, 1, WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE);
wimlib_free(wim);
static_strcpy(tmp, dst);
static_strcat(tmp, strrchr(src, '\\'));
*p = '\\';
if (!MoveFileExU(tmp, dst, MOVEFILE_REPLACE_EXISTING)) {
uprintf(" Could not rename %s to %s: %s", tmp, dst, WindowsErrorString());
r = 1;
}
i++;
}
out:
if (hWim != NULL)
pfWIMCloseHandle(hWim);
safe_closehandle(hFile);
if (xml_file[0] != 0)
DeleteFileU(xml_file);
safe_free(wimage);
return r;
}
// Apply a WIM image using wimgapi.dll
// https://docs.microsoft.com/en-us/previous-versions/msdn10/dd851944(v=msdn.10)
// To get progress, we must run this call within its own thread
static DWORD WINAPI WimApplyImageThread(LPVOID param)
{
BOOL r = FALSE;
HANDLE hWim = NULL;
HANDLE hImage = NULL;
wchar_t wtemp[MAX_PATH] = { 0 };
mount_params_t* mp = (mount_params_t*)param;
wchar_t* wimage = utf8_to_wchar(mp->image);
wchar_t* wdst = utf8_to_wchar(mp->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]", mp->image, mp->index);
progress_report_mask = WIM_REPORT_PROCESS | WIM_REPORT_FILEINFO;
progress_op = OP_FILE_COPY;
progress_msg = MSG_267;
progress_offset = 0;
progress_total = 100;
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,
(img_report.wininst_version >= SPECIAL_WIM_VERSION) ? WIM_UNDOCUMENTED_BULLSHIT : 0, 0, NULL);
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)mp->index);
if (hImage == NULL) {
uprintf(" Could not set index: %s", WindowsErrorString());
goto out;
}
uprintf("Applying Windows image...");
UpdateProgressWithInfoInit(NULL, TRUE);
// Run a first pass using WIM_FLAG_NO_APPLY to count the files
wim_nb_files = 0;
wim_proc_files = 0;
wim_extra_files = 0;
count_files = TRUE;
if (!pfWIMApplyImage(hImage, wdst, WIM_FLAG_NO_APPLY)) {
uprintf(" Could not count the files to apply: %s", WindowsErrorString());
goto out;
}
// The latest Windows 10 ISOs have a ~17.5% discrepancy between the number of
// files and directories actually applied vs. the ones counted when not applying.
// Therefore, we add a 'safe' 20% to our counted files to compensate for yet
// another dismal Microsoft progress reporting API...
wim_nb_files += wim_nb_files / 5;
count_files = FALSE;
// Actual apply
if (!pfWIMApplyImage(hImage, wdst, WIM_FLAG_FILEINFO)) {
uprintf(" Could not apply image: %s", WindowsErrorString());
goto out;
}
// Ensure that we'll pick if need to readjust our 20% above from user reports
if (wim_extra_files > 0)
uprintf("Notice: An extra %d files and directories were applied, from the %d expected",
wim_extra_files, wim_nb_files);
// Re-use extra files as the final progress step
wim_extra_files = (wim_nb_files - wim_proc_files) / 3;
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files + wim_extra_files, wim_nb_files);
r = TRUE;
wimlib_global_cleanup();
out:
if ((hImage != NULL) || (hWim != NULL)) {
uprintf("Closing: %s", mp->image);
if (hImage != NULL) pfWIMCloseHandle(hImage);
if (hWim != NULL) pfWIMCloseHandle(hWim);
}
if (pfWIMUnregisterMessageCallback != NULL)
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
safe_free(wimage);
safe_free(wdst);
ExitThread((DWORD)r);
return (r == 0);
}
BOOL WimApplyImage(const char* image, int index, const char* dst)
{
DWORD dw = 0;
mount_params_t mp = { 0 };
mp.image = image;
mp.index = index;
mp.dst = dst;
wim_thread = CreateThread(NULL, 0, WimApplyImageThread, &mp, 0, NULL);
if (wim_thread == NULL) {
uprintf("Unable to start apply-image thread");
return FALSE;
int r = 1;
WIMStruct* wim;
wimlib_global_init(0);
wimlib_set_print_errors(true);
uprintf("Opening: %s:[%d]", image, index);
r = wimlib_open_wimU(image, 0, &wim);
if (r == 0) {
progress_op = OP_FILE_COPY;
progress_msg = MSG_267;
wimlib_register_progress_function(wim, WimProgressFunc, NULL);
r = wimlib_extract_imageU(wim, index, dst, 0);
wimlib_free(wim);
} else {
uprintf("Failed to open '%s': Wimlib error %d", image, r);
}
SetThreadPriority(wim_thread, default_thread_priority);
WaitForSingleObject(wim_thread, INFINITE);
if (!GetExitCodeThread(wim_thread, &dw))
dw = 0;
wim_thread = NULL;
return dw;
wimlib_global_cleanup();
return (r == 0);
}
// Mount an ISO or a VHD/VHDX image and provide its size

47
src/vhd.h

@ -1,7 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Virtual Disk Handling definitions and prototypes
* Copyright © 2022-2024 Pete Batard <pete@akeo.ie>
* Copyright © 2022-2025 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
@ -29,34 +29,16 @@
#pragma once
#define WIM_MAGIC 0x0000004D4957534DULL // "MSWIM\0\0\0"
#define WIM_HAS_API_EXTRACT 1
#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))
#define SECONDS_SINCE_JAN_1ST_2000 946684800
#define INVALID_CALLBACK_VALUE 0xFFFFFFFF
#define WIM_FLAG_RESERVED 0x00000001
#define WIM_FLAG_VERIFY 0x00000002
#define WIM_FLAG_INDEX 0x00000004
#define WIM_FLAG_NO_APPLY 0x00000008
#define WIM_FLAG_NO_DIRACL 0x00000010
#define WIM_FLAG_NO_FILEACL 0x00000020
#define WIM_FLAG_SHARE_WRITE 0x00000040
#define WIM_FLAG_FILEINFO 0x00000080
#define WIM_FLAG_NO_RP_FIX 0x00000100
// Bitmask for the kind of progress we want to report in the WIM progress callback
#define WIM_REPORT_PROGRESS 0x00000001
#define WIM_REPORT_PROCESS 0x00000002
#define WIM_REPORT_FILEINFO 0x00000004
// Someone's going to have to explain to me why trying to define ANY static inline function
// calls in wimlib.h in DEBUG produces "C2143: syntax error: missing ';' before '{'" with
// MSVC, UNLESS we add the following 2 include statements here (in no particular order).
// WHAT. THE. ACTUAL. FUCK?!?!?!
#if defined(_MSC_VER) && defined(_DEBUG)
#include "msapi_utf8.h"
#include "wimlib.h"
#endif
#define WIM_GENERIC_READ GENERIC_READ
#define WIM_OPEN_EXISTING OPEN_EXISTING
#define WIM_UNDOCUMENTED_BULLSHIT 0x20000000
#define WIM_MAGIC 0x0000004D4957534DULL // "MSWIM\0\0\0"
#define MBR_SIZE 512 // Might need to review this once we see bootable 4k systems
@ -127,15 +109,8 @@ enum WIMMessage {
WIM_MSG_ABORT_IMAGE = -1
};
extern uint8_t WimExtractCheck(BOOL bSilent);
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst, BOOL bSilent);
extern BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst, BOOL bSilent);
extern BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst, BOOL bSilent);
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 char* WimMountImage(const char* image, int index);
extern BOOL WimUnmountImage(const char* image, int index, BOOL commit);
extern char* WimGetExistingMountPoint(const char* image, int index);
extern BOOL WimIsValidIndex(const char* image, int index);
extern int8_t IsBootableImage(const char* path);
extern char* VhdMountImageAndGetSize(const char* path, uint64_t* disksize);
#define VhdMountImage(path) VhdMountImageAndGetSize(path, NULL)

73
src/wimlib/wimlib.h

@ -400,6 +400,10 @@
#include <stdint.h>
#include <time.h>
#ifdef _RUFUS
#include "msapi_utf8.h"
#endif
#ifdef BUILDING_WIMLIB
/*
* On i386, gcc assumes that the stack is 16-byte aligned at function entry.
@ -2755,6 +2759,22 @@ wimlib_add_tree(WIMStruct *wim, int image,
const wimlib_tchar *fs_source_path,
const wimlib_tchar *wim_target_path, int add_flags);
#ifdef _RUFUS
static __inline int
wimlib_add_treeU(WIMStruct *wim, int image,
const char *fs_source_path,
const char *wim_target_path, int add_flags)
{
int r;
wconvert(fs_source_path);
wconvert(wim_target_path);
r = wimlib_add_tree(wim, image, wfs_source_path, wwim_target_path, add_flags);
wfree(fs_source_path);
wfree(wim_target_path);
return r;
}
#endif
/**
* @ingroup G_creating_and_opening_wims
*
@ -3020,6 +3040,19 @@ WIMLIBAPI int
wimlib_extract_image(WIMStruct *wim, int image,
const wimlib_tchar *target, int extract_flags);
#ifdef _RUFUS
static __inline int
wimlib_extract_imageU(WIMStruct *wim, int image,
const char *target, int extract_flags)
{
int r;
wconvert(target);
r = wimlib_extract_image(wim, image, wtarget, extract_flags);
wfree(target);
return r;
}
#endif
/**
* @ingroup G_extracting_wims
*
@ -3182,6 +3215,33 @@ wimlib_extract_paths(WIMStruct *wim,
size_t num_paths,
int extract_flags);
#ifdef _RUFUS
static __inline int
wimlib_extract_pathsU(WIMStruct* wim,
int image,
const char* target,
const char* const* paths,
size_t num_paths,
int extract_flags)
{
int r = WIMLIB_ERR_NOMEM;
wconvert(target);
wimlib_tchar** wpaths = calloc(num_paths, sizeof(wimlib_tchar*));
if (!wpaths)
goto out;
for (size_t i = 0; i < num_paths; i++)
wpaths[i] = utf8_to_wchar(paths[i]);
r = wimlib_extract_paths(wim, image, wtarget, (const wimlib_tchar* const*)wpaths, num_paths, extract_flags);
out:
for (size_t i = 0; wpaths && i < num_paths; i++)
free(wpaths[i]);
free(wpaths);
wfree(target);
return r;
}
#endif
/**
* @ingroup G_wim_information
*
@ -3739,6 +3799,19 @@ wimlib_open_wim(const wimlib_tchar *wim_file,
int open_flags,
WIMStruct **wim_ret);
#ifdef _RUFUS
static __inline int
wimlib_open_wimU(const char* wim_file,
int open_flags,
WIMStruct** wim_ret)
{
wconvert(wim_file);
int r = wimlib_open_wim(wwim_file, open_flags, wim_ret);
wfree(wim_file);
return r;
}
#endif
/**
* @ingroup G_creating_and_opening_wims
*

286
src/wue.c

@ -1,7 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Windows User Experience
* Copyright © 2022-2024 Pete Batard <pete@akeo.ie>
* Copyright © 2022-2025 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
@ -28,6 +28,7 @@
#include "xml.h"
#include "drive.h"
#include "format.h"
#include "wimlib.h"
#include "missing.h"
#include "resource.h"
#include "registry.h"
@ -48,7 +49,6 @@ int unattend_xml_mask = UNATTEND_DEFAULT_SELECTION_MASK;
char *unattend_xml_path = NULL, unattend_username[MAX_USERNAME_LENGTH];
BOOL is_bootloader_revoked = FALSE;
extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
extern BOOL validate_md5sum;
extern uint64_t md5sum_totalbytes;
extern StrArray modified_files;
@ -422,7 +422,7 @@ out:
/// <param name="xml">The index XML data.</param>
/// <param name="xml_len">The length of the index XML data.</param>
/// <param name="index">The index of the occurrence to look for.</param>
static void PopulateWindowsVersionFromXml(const char* xml, size_t xml_len, int index)
static void PopulateWindowsVersionFromXml(const wchar_t* xml, size_t xml_len, int index)
{
char* val;
ezxml_t pxml = ezxml_parse_str((char*)xml, xml_len);
@ -469,57 +469,42 @@ static void PopulateWindowsVersionFromXml(const char* xml, size_t xml_len, int i
/// <summary>
/// Populate the img_report Window version from an install[.wim|.esd].
/// ISO if needed. Requires Windows 8 or later.
/// </summary>
/// <param name="">(none)</param>
/// <returns>TRUE on success, FALSE if we couldn't populate the version.</returns>
BOOL PopulateWindowsVersion(void)
{
char *mounted_iso, mounted_image_path[128];
char xml_file[MAX_PATH] = "", *xml;
int r;
char wim_path[MAX_PATH] = "";
wchar_t* xml = NULL;
size_t xml_len;
WIMStruct* wim;
memset(&img_report.win_version, 0, sizeof(img_report.win_version));
if ((WindowsVersion.Version < WINDOWS_8) || ((WimExtractCheck(TRUE) & 4) == 0))
return FALSE;
// If we're not using a straight install.wim, we need to mount the ISO to access it
static_strcpy(wim_path, image_path);
if (!img_report.is_windows_img) {
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount Windows ISO for build number detection");
return FALSE;
}
static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[0][2]);
static_strcat(wim_path, "|");
static_strcat(wim_path, &img_report.wininst_path[0][3]);
}
// Now take a look at the XML file in install.wim to list our versions
if ((GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, xml_file) == 0) || (xml_file[0] == 0)) {
// Last ditch effort to get a tmp file - just extract it to the current directory
static_strcpy(xml_file, ".\\RufVXml.tmp");
}
// GetTempFileName() may leave a file behind
DeleteFileU(xml_file);
// Must use the Windows WIM API as 7z messes up the XML
if (!WimExtractFile_API(img_report.is_windows_img ? image_path : mounted_image_path,
0, "[1].xml", xml_file, TRUE)) {
uprintf("Could not acquire WIM index");
r = wimlib_open_wimU(wim_path, 0, &wim);
if (r != 0) {
uprintf("Could not open WIM: Error %d", r);
goto out;
}
xml_len = read_file(xml_file, (uint8_t**)&xml);
if (xml_len == 0) {
uprintf("Could not read WIM index XML");
r = wimlib_get_xml_data(wim, (void**)&xml, &xml_len);
if (r != 0) {
uprintf("Could not read WIM XML index: Error %d", r);
goto out;
}
PopulateWindowsVersionFromXml(xml, xml_len, 1);
out:
DeleteFileU(xml_file);
if (!img_report.is_windows_img)
VhdUnmountImage();
free(xml);
wimlib_free(wim);
return ((img_report.win_version.major != 0) && (img_report.win_version.build != 0));
}
@ -557,22 +542,20 @@ BOOL CopySKUSiPolicy(const char* drive_name)
/// <returns>-2 on user cancel, -1 on other error, >=0 on success.</returns>
int SetWinToGoIndex(void)
{
int i;
char* mounted_iso, mounted_image_path[128];
int i, r;
WIMStruct* wim = NULL;
char* install_names[MAX_WININST];
char xml_file[MAX_PATH] = "", *xml = NULL;
wchar_t wim_path[MAX_PATH] = L"", *xml = NULL;
size_t xml_len;
StrArray version_name, version_index;
StrArray version_name = { 0 }, version_index = { 0 };
BOOL bNonStandard = FALSE;
ezxml_t index = NULL, image = NULL;
// Sanity checks
wintogo_index = -1;
wininst_index = 0;
if ((WindowsVersion.Version < WINDOWS_8) || ((WimExtractCheck(FALSE) & 4) == 0) ||
(ComboBox_GetCurItemData(hFileSystem) != FS_NTFS)) {
if (ComboBox_GetCurItemData(hFileSystem) != FS_NTFS)
return -1;
}
// If we have multiple windows install images, ask the user the one to use
if (img_report.wininst_index > 1) {
@ -585,34 +568,21 @@ int SetWinToGoIndex(void)
wininst_index = 0;
}
// If we're not using a straight install.wim, we need to mount the ISO to access it
utf8_to_wchar_no_alloc(image_path, wim_path, ARRAYSIZE(wim_path));
if (!img_report.is_windows_img) {
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount ISO for Windows To Go selection");
return -1;
}
static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
wcscat(wim_path, L"|");
utf8_to_wchar_no_alloc(&img_report.wininst_path[wininst_index][2],
&wim_path[wcslen(wim_path)], (int)ARRAYSIZE(wim_path) - wcslen(wim_path));
}
// Now take a look at the XML file in install.wim to list our versions
if ((GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, xml_file) == 0) || (xml_file[0] == 0)) {
// Last ditch effort to get a tmp file - just extract it to the current directory
static_strcpy(xml_file, ".\\RufVXml.tmp");
}
// GetTempFileName() may leave a file behind
DeleteFileU(xml_file);
// Must use the Windows WIM API as 7z messes up the XML
if (!WimExtractFile_API(img_report.is_windows_img ? image_path : mounted_image_path,
0, "[1].xml", xml_file, FALSE)) {
uprintf("Could not acquire WIM index");
r = wimlib_open_wim(wim_path, 0, &wim);
if (r != 0) {
uprintf("Could not open WIM: %d", r);
goto out;
}
xml_len = read_file(xml_file, (uint8_t**)&xml);
if (xml_len == 0) {
uprintf("Could not read WIM XML");
r = wimlib_get_xml_data(wim, (void**)&xml, &xml_len);
if (r != 0) {
uprintf("Could not read WIM XML index: %d", r);
goto out;
}
@ -671,15 +641,13 @@ int SetWinToGoIndex(void)
Notification(MSG_INFO, NULL, &more_info, lmprintf(MSG_128, "Windows To Go"), lmprintf(MSG_133));
}
}
StrArrayDestroy(&version_name);
StrArrayDestroy(&version_index);
out:
ezxml_free(index);
StrArrayDestroy(&version_name);
StrArrayDestroy(&version_index);
free(xml);
DeleteFileU(xml_file);
if (!img_report.is_windows_img)
VhdUnmountImage();
ezxml_free(index);
wimlib_free(wim);
return wintogo_index;
}
@ -695,7 +663,7 @@ out:
/// <returns>TRUE on success, FALSE on error.</returns>
BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
{
char *mounted_iso, *ms_efi = NULL, mounted_image_path[128], cmd[MAX_PATH];
char *ms_efi = NULL, wim_path[MAX_PATH], cmd[MAX_PATH];
ULONG cluster_size;
uprintf("Windows To Go mode selected");
@ -705,28 +673,19 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
return FALSE;
}
static_strcpy(wim_path, image_path);
if (!img_report.is_windows_img) {
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount ISO for Windows To Go installation");
ErrorStatus = RUFUS_ERROR(APPERR(ERROR_ISO_EXTRACT));
return FALSE;
}
static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
uprintf("Mounted ISO as '%s'", mounted_iso);
static_strcat(wim_path, "|");
static_strcat(wim_path, &img_report.wininst_path[wininst_index][3]);
}
// Now we use the WIM API to apply that image
if (!WimApplyImage(img_report.is_windows_img ? image_path : mounted_image_path, wintogo_index, drive_name)) {
if (!WimApplyImage(wim_path, wintogo_index, drive_name)) {
uprintf("Failed to apply Windows To Go image");
if (!IS_ERROR(ErrorStatus))
ErrorStatus = RUFUS_ERROR(APPERR(ERROR_ISO_EXTRACT));
if (!img_report.is_windows_img)
VhdUnmountImage();
return FALSE;
}
if (!img_report.is_windows_img)
VhdUnmountImage();
if (use_esp) {
uprintf("Setting up EFI System Partition");
@ -745,7 +704,7 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
// completely neuter it) and IVdsDiskPartitionMF::FormatPartitionEx(), which is what you are supposed to
// use for ESPs, explicitly states: "This method cannot be used to format removable media."
if (!FormatPartition(DriveIndex, SelectedDrive.Partition[partition_index[PI_ESP]].Offset, cluster_size, FS_FAT32, "",
FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT)) {
FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT | FP_NO_PROGRESS)) {
uprintf("Could not format EFI System Partition");
return FALSE;
}
@ -778,7 +737,7 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
CopySKUSiPolicy((use_esp) ? ms_efi : drive_name);
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files + 2 * wim_extra_files, wim_nb_files);
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, 99, 100);
// Setting internal drives offline for Windows To Go is crucial if, for instance, you are using ReFS
// on Windows 10 (therefore ReFS v3.4) and don't want a Windows 11 To Go boot to automatically
@ -800,7 +759,7 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
uprintf(cmd);
RunCommand(cmd, sysnative_dir, usb_debug);
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_nb_files, wim_nb_files);
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, 100, 100);
if (use_esp) {
Sleep(200);
@ -819,22 +778,25 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
BOOL ApplyWindowsCustomization(char drive_letter, int flags)
// NB: Work with a copy of unattend_xml_flags as a paremeter since we will modify it.
{
BOOL r = FALSE, is_hive_mounted = FALSE;
int i, wim_index = 2;
BOOL r = FALSE, is_hive_mounted = FALSE, update_boot_wim = FALSE;
int i, wim_index = 2, wuc_index = 0;
const char* offline_hive_name = "RUFUS_OFFLINE_HIVE";
char boot_wim_path[] = "?:\\sources\\boot.wim", key_path[64];
char tmp_dir[2][MAX_PATH] = { "", "" };
char appraiserres_dll_src[] = "?:\\sources\\appraiserres.dll";
char appraiserres_dll_dst[] = "?:\\sources\\appraiserres.bak";
char setup_exe[] = "?:\\setup.exe";
char setup_dll[] = "?:\\setup.dll";
char md5sum_path[] = "?:\\md5sum.txt";
char *mount_path = NULL, path[MAX_PATH];
char path[MAX_PATH];
uint8_t* buf = NULL;
uint16_t setup_arch;
HKEY hKey = NULL, hSubKey = NULL;
LSTATUS status;
DWORD dwDisp, dwVal = 1, dwSize;
FILE* fd_md5sum;
WIMStruct* wim;
struct wimlib_update_command wuc[2] = { 0 };
assert(unattend_xml_path != NULL);
uprintf("Applying Windows customization:");
@ -912,30 +874,43 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
}
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 0, PATCH_PROGRESS_TOTAL);
// We only need to mount boot.wim if we have windowsPE data to deal with. If
// not, we can just copy our unattend.xml in \sources\$OEM$\$$\Panther\.
// We also need to mount it if we use the 'Windows UEFI CA 2023' signed bootloaders.
// We only need to alter boot.wim if we have windowsPE data to deal with.
// If not, we can just copy our unattend.xml in \sources\$OEM$\$$\Panther\.
// We also need to do so if we use the 'Windows UEFI CA 2023' signed bootloaders.
if (flags & UNATTEND_WINPE_SETUP_MASK || flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
if (validate_md5sum)
md5sum_totalbytes -= _filesizeU(boot_wim_path);
uprintf("Mounting '%s[%d]'...", boot_wim_path, wim_index);
// Some "unofficial" ISOs have a modified boot.wim that doesn't have Windows Setup at index 2...
if (!WimIsValidIndex(boot_wim_path, wim_index)) {
// TODO: we could try to look for "Microsoft Windows Setup" in the XML DESCRIPTION to locate the index
wimlib_global_init(0);
wimlib_set_print_errors(true);
update_boot_wim = (wimlib_open_wimU(boot_wim_path, WIMLIB_OPEN_FLAG_WRITE_ACCESS, &wim) == 0);
if (!update_boot_wim) {
uprintf("Could not open '%s'", boot_wim_path);
goto out;
}
// Setup image should be index 2
if (wimlib_resolve_image(wim, L"2") != 2) {
uprintf("WARNING: This image appears to be an UNOFFICIAL Windows ISO!");
uprintf("Rufus recommends that you only use OFFICIAL retail Microsoft Windows images, such as");
uprintf("the ones that can be downloaded through the download facility of this application.");
wim_index = 1;
}
mount_path = WimMountImage(boot_wim_path, wim_index);
if (mount_path == NULL)
goto out;
}
if (flags & UNATTEND_SECUREBOOT_TPM_MINRAM) {
char tmp_path[MAX_PATH];
const char* reg_path = "Windows\\System32\\config\\SYSTEM";
if (GetTempDirNameU(temp_dir, APPLICATION_NAME, 0, tmp_dir[0]) == 0) {
uprintf("WARNING: Could not create temp dir for registry changes");
goto copy_unattend;
}
static_sprintf(tmp_path, "%s\\SYSTEM", tmp_dir[0]);
// Try to create the registry keys directly, and fallback to using unattend
// if that fails (which the Windows Store version is expected to do).
static_sprintf(path, "%s\\Windows\\System32\\config\\SYSTEM", mount_path);
if (!MountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name, path)) {
if (wimlib_extract_pathsU(wim, wim_index, tmp_dir[0], &reg_path, 1,
WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE) != 0 ||
!MountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name, tmp_path)) {
uprintf("Falling back to creating the registry keys through unattend.xml");
goto copy_unattend;
}
@ -968,6 +943,11 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
}
uprintf("Created 'HKLM\\SYSTEM\\Setup\\LabConfig\\%s' registry key", bypass_name[i]);
}
wuc[wuc_index].op = WIMLIB_UPDATE_OP_ADD;
wuc[wuc_index].add.fs_source_path = utf8_to_wchar(tmp_path);
wuc[wuc_index].add.wim_target_path = L"Windows\\System32\\config\\SYSTEM";
wuc_index++;
// We were successfull in creating the keys so disable the windowsPE section from unattend.xml
// We do this by replacing '<settings pass="windowsPE">' with '<settings pass="disabled">'
// (provided that the registry key creation was the only item for this pass)
@ -989,14 +969,13 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
// If we have a windowsPE section, copy the answer files to the root of boot.wim as
// Autounattend.xml. This also results in that file being automatically copied over
// to %WINDIR%\Panther\unattend.xml for later passes processing.
if_not_assert(mount_path != NULL)
goto out;
static_sprintf(path, "%s\\Autounattend.xml", mount_path);
if (!CopyFileU(unattend_xml_path, path, TRUE)) {
uprintf("Could not create boot.wim 'Autounattend.xml': %s", WindowsErrorString());
if_not_assert(update_boot_wim)
goto out;
}
uprintf("Added 'Autounattend.xml' to '%s'", boot_wim_path);
wuc[wuc_index].op = WIMLIB_UPDATE_OP_ADD;
wuc[wuc_index].add.fs_source_path = utf8_to_wchar(unattend_xml_path);
wuc[wuc_index].add.wim_target_path = L"Autounattend.xml";
uprintf("Added '%S' to '%s'", wuc[wuc_index].add.wim_target_path, boot_wim_path);
wuc_index++;
} else {
// If there is no windowsPE section in our unattend, then copying it as Autounattend.xml on
// the root of boot.wim will not work as Windows Setup does *NOT* carry Autounattend.xml into
@ -1021,57 +1000,50 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
}
if (flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
if_not_assert(mount_path != NULL)
StrArray files;
char tmp_dir2[MAX_PATH], *rep;
const char* efi_ex_path = "Windows\\Boot\\EFI_EX";
if_not_assert(update_boot_wim)
goto out;
if (GetTempDirNameU(temp_dir, APPLICATION_NAME, 0, tmp_dir[1]) == 0) {
uprintf("WARNING: Could not create temp dir for 2023 signed UEFI bootloaders");
goto out;
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\bootmgfw_EX.efi", mount_path);
if (!PathFileExistsU(path)) {
uprintf("Could not find 2023 signed UEFI bootloader - Ignoring option");
}
if (wimlib_extract_pathsU(wim, wim_index, tmp_dir[1], &efi_ex_path, 1,
WIMLIB_EXTRACT_FLAG_NO_ACLS | WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE) != 0) {
uprintf("Could not find 2023 signed UEFI bootloaders - Ignoring option");
} else {
char path2[MAX_PATH], *rep;
StrArray files, dirs;
static_strcat(tmp_dir[1], "\\EFI");
static_sprintf(tmp_dir2, "%s_EX", tmp_dir[1]);
MoveFileU(tmp_dir2, tmp_dir[1]);
StrArrayCreate(&files, 64);
ListDirectoryContent(&files, tmp_dir[1], LIST_DIR_TYPE_FILE | LIST_DIR_TYPE_RECURSIVE);
for (i = 0; i < (int)files.Index; i++) {
rep = remove_substr(files.String[i], "_EX");
assert(rep != NULL);
if (!MoveFileU(files.String[i], rep))
uprintf("WARNING: Could not rename '%s': %s", files.String[i], WindowsErrorString());
safe_free(rep);
}
StrArrayDestroy(&files);
// Replace /EFI/Boot/boot###.efi
for (i = 1; i < ARRAYSIZE(efi_archname); i++) {
static_sprintf(path2, "%c:\\efi\\boot\\boot%s.efi", drive_letter, efi_archname[i]);
if (!PathFileExistsA(path2))
static_sprintf(tmp_dir2, "%c:\\efi\\boot\\boot%s.efi", drive_letter, efi_archname[i]);
if (!PathFileExistsA(tmp_dir2))
continue;
if (!CopyFileU(path, path2, FALSE))
static_sprintf(path, "%s\\bootmgfw.efi", tmp_dir[1]);
if (!CopyFileU(path, tmp_dir2, FALSE))
uprintf("WARNING: Could not replace 'boot%s.efi': %s", efi_archname[i], WindowsErrorString());
break;
}
// Replace /bootmgr.efi
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\bootmgr_EX.efi", mount_path);
static_sprintf(path2, "%c:\\bootmgr.efi", drive_letter);
if (!CopyFileU(path, path2, FALSE))
static_sprintf(path, "%s\\bootmgr.efi", tmp_dir[1]);
if (!CopyFileU(path, tmp_dir2, FALSE))
uprintf("WARNING: Could not replace 'bootmgr.efi': %s", WindowsErrorString());
// Microsoft "secures" the Windows\Boot\ dir through their SUPER OBNOXIOUS AND
// WORTHLESS use of DACLs + read-only flags, so we first need to re-take control
// of all directories under there recursively.
StrArrayCreate(&dirs, 64);
StrArrayCreate(&files, 64);
static_sprintf(path, "%s\\Windows\\Boot\\", mount_path);
StrArrayAdd(&dirs, path, TRUE);
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\", mount_path);
StrArrayAdd(&dirs, path, TRUE);
ListDirectoryContent(&dirs, path, LIST_DIR_TYPE_DIRECTORY | LIST_DIR_TYPE_RECURSIVE);
for (i = 0; i < (int)dirs.Index; i++) {
rep = remove_substr(dirs.String[i], "_EX");
assert(rep != NULL);
TakeOwnership(rep);
safe_free(rep);
}
// Now that we should be able to write to the destination directories, copy the content.
ListDirectoryContent(&files, path, LIST_DIR_TYPE_FILE | LIST_DIR_TYPE_RECURSIVE);
for (i = 0; i < (int)files.Index; i++) {
rep = remove_substr(files.String[i], "_EX");
assert(rep != NULL);
TakeOwnership(rep);
if (!CopyFileU(files.String[i], rep, FALSE) && rep != NULL)
uprintf("WARNING: Could not replace '%s': %s", &rep[strlen(mount_path) + 1], WindowsErrorString());
safe_free(rep);
}
StrArrayDestroy(&dirs);
StrArrayDestroy(&files);
uprintf("Replaced EFI bootloader files with 'Windows UEFI CA 2023' signed versions");
if (wimlib_add_treeU(wim, wim_index, tmp_dir[1], "Windows\\Boot\\EFI", 0))
uprintf("WARNING: Could not replace EFI bootloader files with 'Windows UEFI CA 2023' versions");
else
uprintf("Replaced EFI bootloader files with 'Windows UEFI CA 2023' signed versions");
}
}
@ -1086,15 +1058,25 @@ out:
UnmountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name);
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 104, PATCH_PROGRESS_TOTAL);
}
if (mount_path) {
uprintf("Unmounting '%s[%d]'...", boot_wim_path, wim_index);
WimUnmountImage(boot_wim_path, wim_index, TRUE);
if (update_boot_wim) {
uprintf("Updating '%s[%d]'...", boot_wim_path, wim_index);
if (wimlib_update_image(wim, wim_index, wuc, wuc_index, 0) != 0 ||
wimlib_overwrite(wim, WIMLIB_WRITE_FLAG_RECOMPRESS, 0) != 0) {
uprintf("Error: Failed to update %s", boot_wim_path);
r = FALSE;
}
for (i = 0; i < ARRAYSIZE(tmp_dir); i++)
if (tmp_dir[i][0])
SHDeleteDirectoryExU(NULL, tmp_dir[i], FOF_NO_UI);
for (i = 0; i < wuc_index; i++)
free(wuc[i].add.fs_source_path);
wimlib_free(wim);
wimlib_global_cleanup();
if (validate_md5sum) {
md5sum_totalbytes += _filesizeU(boot_wim_path);
StrArrayAdd(&modified_files, boot_wim_path, TRUE);
}
UpdateProgressWithInfo(OP_PATCH, MSG_325, PATCH_PROGRESS_TOTAL, PATCH_PROGRESS_TOTAL);
}
free(mount_path);
return r;
}
Loading…
Cancel
Save