mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-28 13:44:15 -04:00
[wue] move WUE, Windows To Go and WinPE calls to their own source
This commit is contained in:
parent
c34cbab3b5
commit
a19828c9d1
12 changed files with 882 additions and 742 deletions
560
src/format.c
560
src/format.c
|
@ -44,6 +44,7 @@
|
|||
#include "localization.h"
|
||||
|
||||
#include "br.h"
|
||||
#include "wue.h"
|
||||
#include "fat16.h"
|
||||
#include "fat32.h"
|
||||
#include "ntfs.h"
|
||||
|
@ -65,18 +66,14 @@ const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT",
|
|||
DWORD FormatStatus = 0, LastWriteError = 0;
|
||||
badblocks_report report = { 0 };
|
||||
static float format_percent = 0.0f;
|
||||
static int task_number = 0;
|
||||
static int task_number = 0, actual_fs_type;
|
||||
static unsigned int sec_buf_pos = 0;
|
||||
extern const int nb_steps[FS_MAX];
|
||||
extern uint32_t dur_mins, dur_secs;
|
||||
extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
|
||||
static int actual_fs_type, wintogo_index = -1, wininst_index = 0;
|
||||
extern int unattend_xml_flags;
|
||||
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;
|
||||
extern const grub_patch_t grub_patch[2];
|
||||
extern char* unattend_xml_path;
|
||||
extern const char* bypass_name[4];
|
||||
uint8_t *grub2_buf = NULL, *sec_buf = NULL;
|
||||
long grub2_len;
|
||||
|
||||
|
@ -664,7 +661,7 @@ out:
|
|||
return r;
|
||||
}
|
||||
|
||||
static BOOL FormatPartition(DWORD DriveIndex, uint64_t PartitionOffset, DWORD UnitAllocationSize, USHORT FSType, LPCSTR Label, DWORD Flags)
|
||||
BOOL FormatPartition(DWORD DriveIndex, uint64_t PartitionOffset, DWORD UnitAllocationSize, USHORT FSType, LPCSTR Label, DWORD Flags)
|
||||
{
|
||||
if ((DriveIndex < 0x80) || (DriveIndex > 0x100) || (FSType >= FS_MAX) ||
|
||||
((UnitAllocationSize != 0) && (!IS_POWER_OF_2(UnitAllocationSize)))) {
|
||||
|
@ -1110,557 +1107,6 @@ BOOL WritePBR(HANDLE hLogicalVolume)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup WinPE for bootable USB
|
||||
*/
|
||||
static BOOL SetupWinPE(char drive_letter)
|
||||
{
|
||||
char src[64], dst[32];
|
||||
const char* basedir[3] = { "i386", "amd64", "minint" };
|
||||
const char* patch_str_org[2] = { "\\minint\\txtsetup.sif", "\\minint\\system32\\" };
|
||||
const char* patch_str_rep[2][2] = { { "\\i386\\txtsetup.sif", "\\i386\\system32\\" } ,
|
||||
{ "\\amd64\\txtsetup.sif", "\\amd64\\system32\\" } };
|
||||
const char *win_nt_bt_org = "$win_nt$.~bt";
|
||||
const char *rdisk_zero = "rdisk(0)";
|
||||
const LARGE_INTEGER liZero = { {0, 0} };
|
||||
char setupsrcdev[64];
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
DWORD i, j, size, rw_size, index = 0;
|
||||
BOOL r = FALSE;
|
||||
char* buffer = NULL;
|
||||
|
||||
if ((img_report.winpe & WINPE_AMD64) == WINPE_AMD64)
|
||||
index = 1;
|
||||
else if ((img_report.winpe & WINPE_MININT) == WINPE_MININT)
|
||||
index = 2;
|
||||
// Allow other values than harddisk 1, as per user choice for disk ID
|
||||
static_sprintf(setupsrcdev, "SetupSourceDevice = \"\\device\\harddisk%d\\partition1\"",
|
||||
ComboBox_GetCurSel(hDiskID));
|
||||
// Copy of ntdetect.com in root
|
||||
static_sprintf(src, "%c:\\%s\\ntdetect.com", toupper(drive_letter), basedir[2*(index/2)]);
|
||||
static_sprintf(dst, "%c:\\ntdetect.com", toupper(drive_letter));
|
||||
CopyFileA(src, dst, TRUE);
|
||||
if (!img_report.uses_minint) {
|
||||
// Create a copy of txtsetup.sif, as we want to keep the i386/amd64 files unmodified
|
||||
static_sprintf(src, "%c:\\%s\\txtsetup.sif", toupper(drive_letter), basedir[index]);
|
||||
static_sprintf(dst, "%c:\\txtsetup.sif", toupper(drive_letter));
|
||||
if (!CopyFileA(src, dst, TRUE)) {
|
||||
uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
|
||||
}
|
||||
if (insert_section_data(dst, "[SetupData]", setupsrcdev, FALSE) == NULL) {
|
||||
uprintf("Failed to add SetupSourceDevice in %s\n", dst);
|
||||
goto out;
|
||||
}
|
||||
uprintf("Successfully added '%s' to %s\n", setupsrcdev, dst);
|
||||
}
|
||||
|
||||
static_sprintf(src, "%c:\\%s\\setupldr.bin", toupper(drive_letter), basedir[2*(index/2)]);
|
||||
static_sprintf(dst, "%c:\\BOOTMGR", toupper(drive_letter));
|
||||
if (!CopyFileA(src, dst, TRUE)) {
|
||||
uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
|
||||
}
|
||||
|
||||
// \minint with /minint option doesn't require further processing => return true
|
||||
// \minint and no \i386 without /minint is unclear => return error
|
||||
if (img_report.winpe&WINPE_MININT) {
|
||||
if (img_report.uses_minint) {
|
||||
uprintf("Detected \\minint directory with /minint option: nothing to patch\n");
|
||||
r = TRUE;
|
||||
} else if (!(img_report.winpe&(WINPE_I386|WINPE_AMD64))) {
|
||||
uprintf("Detected \\minint directory only but no /minint option: not sure what to do\n");
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
// At this stage we only handle \i386
|
||||
handle = CreateFileA(dst, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
uprintf("Could not open %s for patching: %s\n", dst, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
size = GetFileSize(handle, NULL);
|
||||
if (size == INVALID_FILE_SIZE) {
|
||||
uprintf("Could not get size for file %s: %s\n", dst, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
buffer = (char*)malloc(size);
|
||||
if (buffer == NULL)
|
||||
goto out;
|
||||
if ((!ReadFile(handle, buffer, size, &rw_size, NULL)) || (size != rw_size)) {
|
||||
uprintf("Could not read file %s: %s\n", dst, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
if (!SetFilePointerEx(handle, liZero, NULL, FILE_BEGIN)) {
|
||||
uprintf("Could not rewind file %s: %s\n", dst, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Patch setupldr.bin
|
||||
uprintf("Patching file %s\n", dst);
|
||||
// Remove CRC check for 32 bit part of setupldr.bin from Win2k3
|
||||
if ((size > 0x2061) && (buffer[0x2060] == 0x74) && (buffer[0x2061] == 0x03)) {
|
||||
buffer[0x2060] = 0xeb;
|
||||
buffer[0x2061] = 0x1a;
|
||||
uprintf(" 0x00002060: 0x74 0x03 -> 0xEB 0x1A (disable Win2k3 CRC check)\n");
|
||||
}
|
||||
for (i=1; i<size-32; i++) {
|
||||
for (j=0; j<ARRAYSIZE(patch_str_org); j++) {
|
||||
if (safe_strnicmp(&buffer[i], patch_str_org[j], strlen(patch_str_org[j])-1) == 0) {
|
||||
assert(index < 2);
|
||||
uprintf(" 0x%08X: '%s' -> '%s'\n", i, &buffer[i], patch_str_rep[index][j]);
|
||||
strcpy(&buffer[i], patch_str_rep[index][j]);
|
||||
i += (DWORD)max(strlen(patch_str_org[j]), strlen(patch_str_rep[index][j])); // in case org is a substring of rep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!img_report.uses_minint) {
|
||||
// Additional setupldr.bin/bootmgr patching
|
||||
for (i=0; i<size-32; i++) {
|
||||
// rdisk(0) -> rdisk(#) disk masquerading
|
||||
// NB: only the first one seems to be needed
|
||||
if (safe_strnicmp(&buffer[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) {
|
||||
buffer[i+6] = 0x30 + ComboBox_GetCurSel(hDiskID);
|
||||
uprintf(" 0x%08X: '%s' -> 'rdisk(%c)'\n", i, rdisk_zero, buffer[i+6]);
|
||||
}
|
||||
// $WIN_NT$_~BT -> i386/amd64
|
||||
if (safe_strnicmp(&buffer[i], win_nt_bt_org, strlen(win_nt_bt_org)-1) == 0) {
|
||||
uprintf(" 0x%08X: '%s' -> '%s%s'\n", i, &buffer[i], basedir[index], &buffer[i+strlen(win_nt_bt_org)]);
|
||||
strcpy(&buffer[i], basedir[index]);
|
||||
// This ensures that we keep the terminator backslash
|
||||
buffer[i+strlen(basedir[index])] = buffer[i+strlen(win_nt_bt_org)];
|
||||
buffer[i+strlen(basedir[index])+1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!WriteFileWithRetry(handle, buffer, size, &rw_size, WRITE_RETRIES)) {
|
||||
uprintf("Could not write patched file: %s\n", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
r = TRUE;
|
||||
|
||||
out:
|
||||
safe_closehandle(handle);
|
||||
safe_free(buffer);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Checks which versions of Windows are available in an install image
|
||||
// to set our extraction index. Asks the user to select one if needed.
|
||||
// Returns -2 on user cancel, -1 on other error, >=0 on success.
|
||||
int SetWinToGoIndex(void)
|
||||
{
|
||||
char *mounted_iso, *val, mounted_image_path[128];
|
||||
char xml_file[MAX_PATH] = "";
|
||||
char *install_names[MAX_WININST];
|
||||
StrArray version_name, version_index;
|
||||
int i;
|
||||
BOOL bNonStandard = FALSE;
|
||||
|
||||
// Sanity checks
|
||||
wintogo_index = -1;
|
||||
wininst_index = 0;
|
||||
if ((nWindowsVersion < WINDOWS_8) || ((WimExtractCheck(FALSE) & 4) == 0) ||
|
||||
(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) {
|
||||
for (i = 0; i < img_report.wininst_index; i++)
|
||||
install_names[i] = &img_report.wininst_path[i][2];
|
||||
wininst_index = _log2(SelectionDialog(BS_AUTORADIOBUTTON, lmprintf(MSG_130),
|
||||
lmprintf(MSG_131), install_names, img_report.wininst_index, 1));
|
||||
if (wininst_index < 0)
|
||||
return -2;
|
||||
if (wininst_index >= MAX_WININST)
|
||||
wininst_index = 0;
|
||||
}
|
||||
|
||||
// If we're not using a straight install.wim, we need to mount the ISO to access it
|
||||
if (!img_report.is_windows_img) {
|
||||
mounted_iso = MountISO(image_path);
|
||||
if (mounted_iso == NULL) {
|
||||
uprintf("Could not mount ISO for Windows To Go selection");
|
||||
return FALSE;
|
||||
}
|
||||
static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
|
||||
}
|
||||
|
||||
// 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");
|
||||
goto out;
|
||||
}
|
||||
|
||||
StrArrayCreate(&version_name, 16);
|
||||
StrArrayCreate(&version_index, 16);
|
||||
for (i = 0; StrArrayAdd(&version_index, get_token_data_file_indexed("IMAGE INDEX", xml_file, i + 1), FALSE) >= 0; i++) {
|
||||
// Some people are apparently creating *unofficial* Windows ISOs that don't have DISPLAYNAME elements.
|
||||
// If we are parsing such an ISO, try to fall back to using DESCRIPTION. Of course, since we don't use
|
||||
// a formal XML parser, if an ISO mixes entries with both DISPLAYNAME and DESCRIPTION and others with
|
||||
// only DESCRIPTION, the version names we report will be wrong.
|
||||
// But hey, there's only so far I'm willing to go to help people who, not content to have demonstrated
|
||||
// their utter ignorance on development matters, are also trying to lecture experienced developers
|
||||
// about specific "noob mistakes"... that don't exist in the code they are trying to criticize.
|
||||
if (StrArrayAdd(&version_name, get_token_data_file_indexed("DISPLAYNAME", xml_file, i + 1), FALSE) < 0) {
|
||||
bNonStandard = TRUE;
|
||||
if (StrArrayAdd(&version_name, get_token_data_file_indexed("DESCRIPTION", xml_file, i + 1), FALSE) < 0) {
|
||||
uprintf("Warning: Could not find a description for image index %d", i + 1);
|
||||
StrArrayAdd(&version_name, "Unknown Windows Version", TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bNonStandard)
|
||||
uprintf("Warning: Nonstandard Windows image (missing <DISPLAYNAME> entries)");
|
||||
|
||||
if (i > 1)
|
||||
// NB: _log2 returns -2 if SelectionDialog() returns negative (user cancelled)
|
||||
i = _log2(SelectionDialog(BS_AUTORADIOBUTTON,
|
||||
lmprintf(MSG_291), lmprintf(MSG_292), version_name.String, i, 1)) + 1;
|
||||
if (i < 0)
|
||||
wintogo_index = -2; // Cancelled by the user
|
||||
else if (i == 0)
|
||||
wintogo_index = 1;
|
||||
else
|
||||
wintogo_index = atoi(version_index.String[i - 1]);
|
||||
if (i > 0) {
|
||||
// Get the version data from the XML index
|
||||
val = get_token_data_file_indexed("MAJOR", xml_file, i);
|
||||
img_report.win_version.major = (uint16_t)safe_atoi(val);
|
||||
free(val);
|
||||
val = get_token_data_file_indexed("MINOR", xml_file, i);
|
||||
img_report.win_version.minor = (uint16_t)safe_atoi(val);
|
||||
free(val);
|
||||
val = get_token_data_file_indexed("BUILD", xml_file, i);
|
||||
img_report.win_version.build = (uint16_t)safe_atoi(val);
|
||||
free(val);
|
||||
val = get_token_data_file_indexed("SPBUILD", xml_file, i);
|
||||
img_report.win_version.revision = (uint16_t)safe_atoi(val);
|
||||
free(val);
|
||||
if ((img_report.win_version.major == 10) && (img_report.win_version.build > 20000))
|
||||
img_report.win_version.major = 11;
|
||||
// If we couldn't obtain the major and build, we have a problem
|
||||
if (img_report.win_version.major == 0 || img_report.win_version.build == 0)
|
||||
uprintf("Warning: Could not obtain version information from XML index (Nonstandard Windows image?)");
|
||||
uprintf("Will use '%s' (Build: %d, Index %s) for Windows To Go",
|
||||
version_name.String[i - 1], img_report.win_version.build, version_index.String[i - 1]);
|
||||
// Need Windows 10 Creator Update or later for boot on REMOVABLE to work
|
||||
if ((img_report.win_version.build < 15000) && (SelectedDrive.MediaType != FixedMedia)) {
|
||||
if (MessageBoxExU(hMainDialog, lmprintf(MSG_098), lmprintf(MSG_190),
|
||||
MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES)
|
||||
wintogo_index = -2;
|
||||
}
|
||||
// Display a notice about WppRecorder.sys for 1809 ISOs
|
||||
if (img_report.win_version.build == 17763) {
|
||||
notification_info more_info;
|
||||
more_info.id = MORE_INFO_URL;
|
||||
more_info.url = WPPRECORDER_MORE_INFO_URL;
|
||||
Notification(MSG_INFO, NULL, &more_info, lmprintf(MSG_128, "Windows To Go"), lmprintf(MSG_133));
|
||||
}
|
||||
}
|
||||
StrArrayDestroy(&version_name);
|
||||
StrArrayDestroy(&version_index);
|
||||
|
||||
out:
|
||||
DeleteFileU(xml_file);
|
||||
if (!img_report.is_windows_img)
|
||||
UnMountISO();
|
||||
return wintogo_index;
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/jj721578(v=ws.11)
|
||||
// As opposed to the technet guide above, we don't set internal drives offline,
|
||||
// due to people wondering why they can't see them by default and we also use
|
||||
// bcdedit rather than 'unattend.xml' to disable the recovery environment.
|
||||
static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
|
||||
{
|
||||
char *mounted_iso, *ms_efi = NULL, mounted_image_path[128], cmd[MAX_PATH];
|
||||
ULONG cluster_size;
|
||||
|
||||
uprintf("Windows To Go mode selected");
|
||||
// Additional sanity checks
|
||||
if ((use_esp) && (SelectedDrive.MediaType != FixedMedia) && (nWindowsBuildNumber < 15000)) {
|
||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!img_report.is_windows_img) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
uprintf("Failed to apply Windows To Go image");
|
||||
if (!IS_ERROR(FormatStatus))
|
||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
|
||||
if (!img_report.is_windows_img)
|
||||
UnMountISO();
|
||||
return FALSE;
|
||||
}
|
||||
if (!img_report.is_windows_img)
|
||||
UnMountISO();
|
||||
|
||||
if (use_esp) {
|
||||
uprintf("Setting up EFI System Partition");
|
||||
// According to Ubuntu (https://bugs.launchpad.net/ubuntu/+source/partman-efi/+bug/811485) you want to use FAT32.
|
||||
// However, you have to be careful that the cluster size needs to be greater or equal to the sector size, which
|
||||
// in turn has an impact on the minimum EFI partition size we can create (see ms_efi_size_MB in drive.c)
|
||||
if (SelectedDrive.SectorSize <= 1024)
|
||||
cluster_size = 1024;
|
||||
else if (SelectedDrive.SectorSize <= 4096)
|
||||
cluster_size = 4096;
|
||||
else // Go for broke
|
||||
cluster_size = (ULONG)SelectedDrive.SectorSize;
|
||||
// Boy do you *NOT* want to specify a label here, and spend HOURS figuring out why your EFI partition cannot boot...
|
||||
// Also, we use the Large FAT32 facility Microsoft APIs are *UTTERLY USELESS* for achieving what we want:
|
||||
// VDS cannot list ESP volumes (talk about allegedly improving on the old disk and volume APIs, only to
|
||||
// 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, partition_offset[PI_ESP], cluster_size, FS_FAT32, "",
|
||||
FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT)) {
|
||||
uprintf("Could not format EFI System Partition");
|
||||
return FALSE;
|
||||
}
|
||||
Sleep(200);
|
||||
// Need to have the ESP mounted to invoke bcdboot
|
||||
ms_efi = AltMountVolume(DriveIndex, partition_offset[PI_ESP], FALSE);
|
||||
if (ms_efi == NULL) {
|
||||
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// We invoke the 'bcdboot' command from the host, as the one from the drive produces problems (#558)
|
||||
// and of course, we couldn't invoke an ARM64 'bcdboot' binary on an x86 host anyway...
|
||||
// Also, since Rufus should (usually) be running as a 32 bit app, on 64 bit systems, we need to use
|
||||
// 'C:\Windows\Sysnative' and not 'C:\Windows\System32' to invoke bcdboot, as 'C:\Windows\System32'
|
||||
// will get converted to 'C:\Windows\SysWOW64' behind the scenes, and there is no bcdboot.exe there.
|
||||
uprintf("Enabling boot using command:");
|
||||
static_sprintf(cmd, "%s\\bcdboot.exe %s\\Windows /v /f %s /s %s", sysnative_dir, drive_name,
|
||||
HAS_BOOTMGR_BIOS(img_report) ? (HAS_BOOTMGR_EFI(img_report) ? "ALL" : "BIOS") : "UEFI",
|
||||
(use_esp)?ms_efi:drive_name);
|
||||
uprintf(cmd);
|
||||
if (RunCommand(cmd, sysnative_dir, usb_debug) != 0) {
|
||||
// Try to continue... but report a failure
|
||||
uprintf("Failed to enable boot");
|
||||
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
|
||||
}
|
||||
|
||||
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files + 2 * wim_extra_files, wim_nb_files);
|
||||
|
||||
// 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
|
||||
// "upgrade" the ReFS version on all drives to v3.7, thereby preventing you from being able to mount
|
||||
// those volumes back on Windows 10 ever again. Yes, I have been stung by this Microsoft bullshit!
|
||||
// See: https://gist.github.com/0xbadfca11/da0598e47dd643d933dc#Mountability
|
||||
if (unattend_xml_flags & UNATTEND_OFFLINE_INTERNAL_DRIVES) {
|
||||
uprintf("Setting the target's internal drives offline using command:");
|
||||
// This applies the "offlineServicing" section of the unattend.xml (while ignoring the other sections)
|
||||
static_sprintf(cmd, "dism /Image:%s\\ /Apply-Unattend:%s", drive_name, unattend_xml_path);
|
||||
uprintf(cmd);
|
||||
RunCommand(cmd, NULL, usb_debug);
|
||||
}
|
||||
|
||||
uprintf("Disabling use of the Windows Recovery Environment using command:");
|
||||
static_sprintf(cmd, "%s\\bcdedit.exe /store %s\\EFI\\Microsoft\\Boot\\BCD /set {default} recoveryenabled no",
|
||||
sysnative_dir, (use_esp) ? ms_efi : drive_name);
|
||||
uprintf(cmd);
|
||||
RunCommand(cmd, sysnative_dir, usb_debug);
|
||||
|
||||
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_nb_files, wim_nb_files);
|
||||
|
||||
if (use_esp) {
|
||||
Sleep(200);
|
||||
AltUnmountVolume(ms_efi, FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add unattend.xml to 'sources\boot.wim' (install) or 'Windows\Panther\' (Windows To Go)
|
||||
* NB: Work with a copy of unattend_xml_flags as a paremeter since we will modify it.
|
||||
*/
|
||||
BOOL ApplyWindowsCustomization(char drive_letter, int flags)
|
||||
{
|
||||
BOOL r = FALSE, is_hive_mounted = FALSE;
|
||||
int i;
|
||||
const int wim_index = 2;
|
||||
const char* offline_hive_name = "RUFUS_OFFLINE_HIVE";
|
||||
char boot_wim_path[] = "?:\\sources\\boot.wim", key_path[64];
|
||||
char appraiserres_dll_src[] = "?:\\sources\\appraiserres.dll";
|
||||
char appraiserres_dll_dst[] = "?:\\sources\\appraiserres.bak";
|
||||
char *mount_path = NULL, path[MAX_PATH];
|
||||
HKEY hKey = NULL, hSubKey = NULL;
|
||||
LSTATUS status;
|
||||
DWORD dwDisp, dwVal = 1;
|
||||
|
||||
assert(unattend_xml_path != NULL);
|
||||
uprintf("Applying Windows customization:");
|
||||
if (flags & UNATTEND_WINDOWS_TO_GO) {
|
||||
static_sprintf(path, "%c:\\Windows\\Panther", drive_letter);
|
||||
if (!CreateDirectoryA(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
uprintf("Could not create '%s' : %s", path, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
static_sprintf(path, "%c:\\Windows\\Panther\\unattend.xml", drive_letter);
|
||||
if (!CopyFileA(unattend_xml_path, path, TRUE)) {
|
||||
uprintf("Could not create '%s' : %s", path, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
uprintf("Added '%s'", path);
|
||||
} else {
|
||||
boot_wim_path[0] = drive_letter;
|
||||
if (flags & UNATTEND_WINPE_SETUP_MASK) {
|
||||
// Create a backup of sources\appraiserres.dll and then create an empty file to
|
||||
// allow in-place upgrades without TPM/SB. Note that we need to create an empty,
|
||||
// appraiserres.dll otherwise setup.exe extracts its own.
|
||||
appraiserres_dll_src[0] = drive_letter;
|
||||
appraiserres_dll_dst[0] = drive_letter;
|
||||
if (!MoveFileExU(appraiserres_dll_src, appraiserres_dll_dst, MOVEFILE_REPLACE_EXISTING))
|
||||
uprintf("Could not rename '%s': %s", appraiserres_dll_src, WindowsErrorString());
|
||||
else
|
||||
CloseHandle(CreateFileU(appraiserres_dll_src, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
uprintf("Renamed '%s' → '%s'", appraiserres_dll_src, appraiserres_dll_dst);
|
||||
}
|
||||
|
||||
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\.
|
||||
if (flags & UNATTEND_WINPE_SETUP_MASK) {
|
||||
uprintf("Mounting '%s'...", boot_wim_path);
|
||||
mount_path = WimMountImage(boot_wim_path, wim_index);
|
||||
if (mount_path == NULL)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (flags & UNATTEND_SECUREBOOT_TPM_MINRAM) {
|
||||
// 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)) {
|
||||
uprintf("Falling back to creating the registry keys through unattend.xml");
|
||||
goto copy_unattend;
|
||||
}
|
||||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 101, PATCH_PROGRESS_TOTAL);
|
||||
is_hive_mounted = TRUE;
|
||||
|
||||
static_sprintf(key_path, "%s\\Setup", offline_hive_name);
|
||||
status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key_path, 0, KEY_READ | KEY_CREATE_SUB_KEY, &hKey);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
SetLastError(status);
|
||||
uprintf("Could not open 'HKLM\\SYSTEM\\Setup' registry key: %s", WindowsErrorString());
|
||||
goto copy_unattend;
|
||||
}
|
||||
|
||||
status = RegCreateKeyExA(hKey, "LabConfig", 0, NULL, 0,
|
||||
KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY, NULL, &hSubKey, &dwDisp);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
SetLastError(status);
|
||||
uprintf("Could not create 'HKLM\\SYSTEM\\Setup\\LabConfig' registry key: %s", WindowsErrorString());
|
||||
goto copy_unattend;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAYSIZE(bypass_name); i++) {
|
||||
status = RegSetValueExA(hSubKey, bypass_name[i], 0, REG_DWORD, (LPBYTE)&dwVal, sizeof(DWORD));
|
||||
if (status != ERROR_SUCCESS) {
|
||||
SetLastError(status);
|
||||
uprintf("Could not set 'HKLM\\SYSTEM\\Setup\\LabConfig\\%s' registry key: %s",
|
||||
bypass_name[i], WindowsErrorString());
|
||||
goto copy_unattend;
|
||||
}
|
||||
uprintf("Created 'HKLM\\SYSTEM\\Setup\\LabConfig\\%s' registry key", bypass_name[i]);
|
||||
}
|
||||
// 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)
|
||||
if ((flags & UNATTEND_WINPE_SETUP_MASK) == UNATTEND_SECUREBOOT_TPM_MINRAM) {
|
||||
if (replace_in_token_data(unattend_xml_path, "<settings", "windowsPE", "disabled", FALSE) == NULL)
|
||||
uprintf("Warning: Could not disable 'windowsPE' pass from unattend.xml");
|
||||
// Remove the flags, since we accomplished the registry creation outside of unattend.
|
||||
flags &= ~UNATTEND_SECUREBOOT_TPM_MINRAM;
|
||||
} else {
|
||||
// TODO: If we add other tasks besides LabConfig reg keys, we'll need to figure out how
|
||||
// to comment out the <RunSynchronous> entries from windowsPE (and only windowsPE).
|
||||
assert(FALSE);
|
||||
}
|
||||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 102, PATCH_PROGRESS_TOTAL);
|
||||
}
|
||||
|
||||
copy_unattend:
|
||||
if (flags & UNATTEND_WINPE_SETUP_MASK) {
|
||||
// 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.
|
||||
assert(mount_path != NULL);
|
||||
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());
|
||||
goto out;
|
||||
}
|
||||
uprintf("Added 'Autounattend.xml' to '%s'", boot_wim_path);
|
||||
} 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
|
||||
// %WINDIR%\Panther\unattend.xml then (See: https://github.com/pbatard/rufus/issues/1981).
|
||||
// So instead, copy it to \sources\$OEM$\$$\Panther\unattend.xml on the media, as the content
|
||||
// of \sources\$OEM$\$$\* will get copied into %WINDIR%\ during the file copy phase.
|
||||
static_sprintf(path, "%c:\\sources\\$OEM$\\$$\\Panther", drive_letter);
|
||||
i = SHCreateDirectoryExA(NULL, path, NULL);
|
||||
if (i != ERROR_SUCCESS) {
|
||||
SetLastError(i);
|
||||
uprintf("Error: Could not create directory '%s': %s", path, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
static_sprintf(path, "%c:\\sources\\$OEM$\\$$\\Panther\\unattend.xml", drive_letter);
|
||||
if (!CopyFileU(unattend_xml_path, path, TRUE)) {
|
||||
uprintf("Could not create '%s': %s", path, WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
uprintf("Created '%s'", path);
|
||||
}
|
||||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 103, PATCH_PROGRESS_TOTAL);
|
||||
}
|
||||
r = TRUE;
|
||||
|
||||
out:
|
||||
if (hSubKey != NULL)
|
||||
RegCloseKey(hSubKey);
|
||||
if (hKey != NULL)
|
||||
RegCloseKey(hKey);
|
||||
if (is_hive_mounted) {
|
||||
UnmountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name);
|
||||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 104, PATCH_PROGRESS_TOTAL);
|
||||
}
|
||||
if (mount_path) {
|
||||
uprintf("Unmounting '%s'...", boot_wim_path, wim_index);
|
||||
WimUnmountImage(boot_wim_path, wim_index);
|
||||
UpdateProgressWithInfo(OP_PATCH, MSG_325, PATCH_PROGRESS_TOTAL, PATCH_PROGRESS_TOTAL);
|
||||
}
|
||||
free(mount_path);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void update_progress(const uint64_t processed_bytes)
|
||||
{
|
||||
// NB: We don't really care about resetting this value to UINT64_MAX for a new pass.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue