mirror of
				https://github.com/pbatard/rufus.git
				synced 2024-08-14 23:57:05 +00:00 
			
		
		
		
	Compare commits
	
		
			2 commits
		
	
	
		
			c34cbab3b5
			...
			01dbb3d957
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 01dbb3d957 | ||
|  | a19828c9d1 | 
					 13 changed files with 883 additions and 743 deletions
				
			
		
							
								
								
									
										2
									
								
								.github/workflows/coverity.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/coverity.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -23,7 +23,7 @@ env: | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   Coverity-Build: |   Coverity-Build: | ||||||
|     runs-on: windows-2022 |     runs-on: windows-latest | ||||||
| 
 | 
 | ||||||
|     steps: |     steps: | ||||||
|     - name: Checkout repository |     - name: Checkout repository | ||||||
|  |  | ||||||
|  | @ -373,6 +373,7 @@ | ||||||
|     <ClCompile Include="..\src\dev.c" /> |     <ClCompile Include="..\src\dev.c" /> | ||||||
|     <ClCompile Include="..\src\ui.c" /> |     <ClCompile Include="..\src\ui.c" /> | ||||||
|     <ClCompile Include="..\src\vhd.c" /> |     <ClCompile Include="..\src\vhd.c" /> | ||||||
|  |     <ClCompile Include="..\src\wue.c" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="..\res\grub2\grub2_version.h" /> |     <ClInclude Include="..\res\grub2\grub2_version.h" /> | ||||||
|  | @ -405,6 +406,7 @@ | ||||||
|     <ClInclude Include="..\src\ui.h" /> |     <ClInclude Include="..\src\ui.h" /> | ||||||
|     <ClInclude Include="..\src\ui_data.h" /> |     <ClInclude Include="..\src\ui_data.h" /> | ||||||
|     <ClInclude Include="..\src\winio.h" /> |     <ClInclude Include="..\src\winio.h" /> | ||||||
|  |     <ClInclude Include="..\src\wue.h" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Manifest Include="..\src\rufus.manifest" /> |     <Manifest Include="..\src\rufus.manifest" /> | ||||||
|  |  | ||||||
|  | @ -90,6 +90,9 @@ | ||||||
|     <ClCompile Include="..\src\re.c"> |     <ClCompile Include="..\src\re.c"> | ||||||
|       <Filter>Source Files</Filter> |       <Filter>Source Files</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\src\wue.c"> | ||||||
|  |       <Filter>Source Files</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="..\src\rufus.h"> |     <ClInclude Include="..\src\rufus.h"> | ||||||
|  | @ -182,6 +185,9 @@ | ||||||
|     <ClInclude Include="..\res\grub2\grub2_version.h"> |     <ClInclude Include="..\res\grub2\grub2_version.h"> | ||||||
|       <Filter>Header Files</Filter> |       <Filter>Header Files</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="..\src\wue.h"> | ||||||
|  |       <Filter>Header Files</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <None Include="..\res\rufus.ico"> |     <None Include="..\res\rufus.ico"> | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ AM_V_WINDRES   = $(AM_V_WINDRES_$(V)) | ||||||
| 	$(AM_V_WINDRES) $(AM_RCFLAGS) -i $< -o $@ | 	$(AM_V_WINDRES) $(AM_RCFLAGS) -i $< -o $@ | ||||||
| 
 | 
 | ||||||
| rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.c icon.c iso.c localization.c \ | rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.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 | 	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 | ||||||
| rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \ | rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \ | ||||||
| 	-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus | 	-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus | ||||||
| rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows -L ../.mingw | rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows -L ../.mingw | ||||||
|  |  | ||||||
|  | @ -98,7 +98,7 @@ am_rufus_OBJECTS = rufus-badblocks.$(OBJEXT) rufus-checksum.$(OBJEXT) \ | ||||||
| 	rufus-rufus.$(OBJEXT) rufus-smart.$(OBJEXT) \
 | 	rufus-rufus.$(OBJEXT) rufus-smart.$(OBJEXT) \
 | ||||||
| 	rufus-stdfn.$(OBJEXT) rufus-stdio.$(OBJEXT) \
 | 	rufus-stdfn.$(OBJEXT) rufus-stdio.$(OBJEXT) \
 | ||||||
| 	rufus-stdlg.$(OBJEXT) rufus-syslinux.$(OBJEXT) \
 | 	rufus-stdlg.$(OBJEXT) rufus-syslinux.$(OBJEXT) \
 | ||||||
| 	rufus-ui.$(OBJEXT) rufus-vhd.$(OBJEXT) | 	rufus-ui.$(OBJEXT) rufus-vhd.$(OBJEXT) rufus-wue.$(OBJEXT) | ||||||
| rufus_OBJECTS = $(am_rufus_OBJECTS) | rufus_OBJECTS = $(am_rufus_OBJECTS) | ||||||
| am__DEPENDENCIES_1 = | am__DEPENDENCIES_1 = | ||||||
| rufus_DEPENDENCIES = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a \
 | rufus_DEPENDENCIES = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a \
 | ||||||
|  | @ -282,7 +282,7 @@ AM_V_WINDRES_1 = $(WINDRES) | ||||||
| AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) | AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) | ||||||
| AM_V_WINDRES = $(AM_V_WINDRES_$(V)) | AM_V_WINDRES = $(AM_V_WINDRES_$(V)) | ||||||
| rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.c icon.c iso.c localization.c \
 | rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.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 | 	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 | ||||||
| 
 | 
 | ||||||
| rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \
 | rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \
 | ||||||
| 	-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus | 	-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus | ||||||
|  | @ -495,6 +495,12 @@ rufus-vhd.o: vhd.c | ||||||
| rufus-vhd.obj: vhd.c | rufus-vhd.obj: vhd.c | ||||||
| 	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-vhd.obj `if test -f 'vhd.c'; then $(CYGPATH_W) 'vhd.c'; else $(CYGPATH_W) '$(srcdir)/vhd.c'; fi` | 	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-vhd.obj `if test -f 'vhd.c'; then $(CYGPATH_W) 'vhd.c'; else $(CYGPATH_W) '$(srcdir)/vhd.c'; fi` | ||||||
| 
 | 
 | ||||||
|  | rufus-wue.o: wue.c | ||||||
|  | 	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-wue.o `test -f 'wue.c' || echo '$(srcdir)/'`wue.c | ||||||
|  | 
 | ||||||
|  | rufus-wue.obj: wue.c | ||||||
|  | 	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-wue.obj `if test -f 'wue.c'; then $(CYGPATH_W) 'wue.c'; else $(CYGPATH_W) '$(srcdir)/wue.c'; fi` | ||||||
|  | 
 | ||||||
| # This directory's subdirectories are mostly independent; you can cd
 | # This directory's subdirectories are mostly independent; you can cd
 | ||||||
| # into them and run 'make' without going through this Makefile.
 | # into them and run 'make' without going through this Makefile.
 | ||||||
| # To change the values of 'make' variables: instead of editing Makefiles,
 | # To change the values of 'make' variables: instead of editing Makefiles,
 | ||||||
|  |  | ||||||
							
								
								
									
										560
									
								
								src/format.c
									
										
									
									
									
								
							
							
						
						
									
										560
									
								
								src/format.c
									
										
									
									
									
								
							|  | @ -44,6 +44,7 @@ | ||||||
| #include "localization.h" | #include "localization.h" | ||||||
| 
 | 
 | ||||||
| #include "br.h" | #include "br.h" | ||||||
|  | #include "wue.h" | ||||||
| #include "fat16.h" | #include "fat16.h" | ||||||
| #include "fat32.h" | #include "fat32.h" | ||||||
| #include "ntfs.h" | #include "ntfs.h" | ||||||
|  | @ -65,18 +66,14 @@ const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", | ||||||
| DWORD FormatStatus = 0, LastWriteError = 0; | DWORD FormatStatus = 0, LastWriteError = 0; | ||||||
| badblocks_report report = { 0 }; | badblocks_report report = { 0 }; | ||||||
| static float format_percent = 0.0f; | 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; | static unsigned int sec_buf_pos = 0; | ||||||
| extern const int nb_steps[FS_MAX]; | extern const int nb_steps[FS_MAX]; | ||||||
| extern uint32_t dur_mins, dur_secs; | extern uint32_t dur_mins, dur_secs; | ||||||
| extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files; | 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 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 BOOL write_as_image, use_vds, write_as_esp, is_vds_available; | ||||||
| extern const grub_patch_t grub_patch[2]; | 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; | uint8_t *grub2_buf = NULL, *sec_buf = NULL; | ||||||
| long grub2_len; | long grub2_len; | ||||||
| 
 | 
 | ||||||
|  | @ -664,7 +661,7 @@ out: | ||||||
| 	return r; | 	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) || | 	if ((DriveIndex < 0x80) || (DriveIndex > 0x100) || (FSType >= FS_MAX) || | ||||||
| 		((UnitAllocationSize != 0) && (!IS_POWER_OF_2(UnitAllocationSize)))) { | 		((UnitAllocationSize != 0) && (!IS_POWER_OF_2(UnitAllocationSize)))) { | ||||||
|  | @ -1110,557 +1107,6 @@ BOOL WritePBR(HANDLE hLogicalVolume) | ||||||
| 	return FALSE; | 	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) | 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.
 | 	// NB: We don't really care about resetting this value to UINT64_MAX for a new pass.
 | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ | ||||||
|  * You should have received a copy of the GNU General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <winioctl.h>	// for MEDIA_TYPE | #include <winioctl.h>	// for MEDIA_TYPE | ||||||
| 
 | 
 | ||||||
|  | @ -112,3 +114,5 @@ typedef BOOLEAN (WINAPI* EnableVolumeCompression_t)( | ||||||
| BOOL WritePBR(HANDLE hLogicalDrive); | BOOL WritePBR(HANDLE hLogicalDrive); | ||||||
| BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | ||||||
| BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | ||||||
|  | BOOL FormatPartition(DWORD DriveIndex, uint64_t PartitionOffset, DWORD UnitAllocationSize, USHORT FSType, LPCSTR Label, DWORD Flags); | ||||||
|  | DWORD WINAPI FormatThread(void* param); | ||||||
|  |  | ||||||
|  | @ -115,11 +115,39 @@ static __inline int _log2(register int val) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Remap bits from a byte according to an 8x8 bit matrix
 | /// <summary>
 | ||||||
|  | /// Remaps bits from a byte according to an 8x8 bit matrix.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="src">The byte to remap.</param>
 | ||||||
|  | /// <param name="map">An 8-byte array where each byte has a single bit set to the position to remap to.</param>
 | ||||||
|  | /// <param name="reverse">Indicates whether the reverse mapping operation should be applied.</param>
 | ||||||
|  | /// <returns>The remapped byte data.</returns>
 | ||||||
| static __inline uint8_t remap8(uint8_t src, uint8_t* map, const BOOL reverse) | static __inline uint8_t remap8(uint8_t src, uint8_t* map, const BOOL reverse) | ||||||
| { | { | ||||||
| 	uint8_t i, m = 1, r = 0; | 	uint8_t i, m = 1, r = 0; | ||||||
| 	for (i = 0, m = 1; i < 8; i++, m <<= 1) { | 	for (i = 0, m = 1; i < (sizeof(src) * 8); i++, m <<= 1) { | ||||||
|  | 		if (reverse) { | ||||||
|  | 			if (src & map[i]) | ||||||
|  | 				r |= m; | ||||||
|  | 		} else { | ||||||
|  | 			if (src & m) | ||||||
|  | 				r |= map[i]; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Remaps bits from a 16-bit word according to a 16x16 bit matrix.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="src">The word to remap.</param>
 | ||||||
|  | /// <param name="map">A 16-word array where each word has a single bit set to the position to remap to.</param>
 | ||||||
|  | /// <param name="reverse">Indicates whether the reverse mapping operation should be applied.</param>
 | ||||||
|  | /// <returns>The remapped byte data.</returns>
 | ||||||
|  | static __inline uint16_t remap16(uint16_t src, uint16_t* map, const BOOL reverse) | ||||||
|  | { | ||||||
|  | 	uint16_t i, m = 1, r = 0; | ||||||
|  | 	for (i = 0, m = 1; i < (sizeof(src) * 8); i++, m <<= 1) { | ||||||
| 		if (reverse) { | 		if (reverse) { | ||||||
| 			if (src & map[i]) | 			if (src & map[i]) | ||||||
| 				r |= m; | 				r |= m; | ||||||
|  |  | ||||||
							
								
								
									
										186
									
								
								src/rufus.c
									
										
									
									
									
								
							
							
						
						
									
										186
									
								
								src/rufus.c
									
										
									
									
									
								
							|  | @ -39,6 +39,7 @@ | ||||||
| #include <delayimp.h> | #include <delayimp.h> | ||||||
| 
 | 
 | ||||||
| #include "rufus.h" | #include "rufus.h" | ||||||
|  | #include "format.h" | ||||||
| #include "missing.h" | #include "missing.h" | ||||||
| #include "resource.h" | #include "resource.h" | ||||||
| #include "msapi_utf8.h" | #include "msapi_utf8.h" | ||||||
|  | @ -46,6 +47,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "ui.h" | #include "ui.h" | ||||||
| #include "re.h" | #include "re.h" | ||||||
|  | #include "wue.h" | ||||||
| #include "drive.h" | #include "drive.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "bled/bled.h" | #include "bled/bled.h" | ||||||
|  | @ -78,7 +80,7 @@ static BOOL app_changed_label = FALSE; | ||||||
| static BOOL allowed_filesystem[FS_MAX] = { 0 }; | static BOOL allowed_filesystem[FS_MAX] = { 0 }; | ||||||
| static int64_t last_iso_blocking_status; | static int64_t last_iso_blocking_status; | ||||||
| static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNOWN; | static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNOWN; | ||||||
| static int image_index = 0, select_index = 0, unattend_xml_mask = UNATTEND_DEFAULT_SELECTION_MASK; | static int image_index = 0, select_index = 0; | ||||||
| static RECT relaunch_rc = { -65536, -65536, 0, 0}; | static RECT relaunch_rc = { -65536, -65536, 0, 0}; | ||||||
| static UINT uMBRChecked = BST_UNCHECKED; | static UINT uMBRChecked = BST_UNCHECKED; | ||||||
| static HANDLE format_thread = NULL; | static HANDLE format_thread = NULL; | ||||||
|  | @ -125,7 +127,7 @@ BOOL write_as_image = FALSE, write_as_esp = FALSE, use_vds = FALSE, ignore_boot_ | ||||||
| BOOL appstore_version = FALSE, is_vds_available = TRUE; | BOOL appstore_version = FALSE, is_vds_available = TRUE; | ||||||
| float fScale = 1.0f; | float fScale = 1.0f; | ||||||
| int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0; | int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0; | ||||||
| int default_fs, fs_type, boot_type, partition_type, target_type, unattend_xml_flags = 0; | int default_fs, fs_type, boot_type, partition_type, target_type; | ||||||
| int force_update = 0, default_thread_priority = THREAD_PRIORITY_ABOVE_NORMAL; | int force_update = 0, default_thread_priority = THREAD_PRIORITY_ABOVE_NORMAL; | ||||||
| char szFolderPath[MAX_PATH], app_dir[MAX_PATH], system_dir[MAX_PATH], temp_dir[MAX_PATH], sysnative_dir[MAX_PATH]; | char szFolderPath[MAX_PATH], app_dir[MAX_PATH], system_dir[MAX_PATH], temp_dir[MAX_PATH], sysnative_dir[MAX_PATH]; | ||||||
| char app_data_dir[MAX_PATH], user_dir[MAX_PATH]; | char app_data_dir[MAX_PATH], user_dir[MAX_PATH]; | ||||||
|  | @ -133,12 +135,11 @@ char embedded_sl_version_str[2][12] = { "?.??", "?.??" }; | ||||||
| char embedded_sl_version_ext[2][32]; | char embedded_sl_version_ext[2][32]; | ||||||
| char ClusterSizeLabel[MAX_CLUSTER_SIZES][64]; | char ClusterSizeLabel[MAX_CLUSTER_SIZES][64]; | ||||||
| char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path; | char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path; | ||||||
| char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *unattend_xml_path = NULL; | char *archive_path = NULL, image_option_txt[128], *fido_url = NULL; | ||||||
| StrArray BlockingProcess, ImageList; | StrArray BlockingProcess, ImageList; | ||||||
| // Number of steps for each FS for FCC_STRUCTURE_PROGRESS
 | // Number of steps for each FS for FCC_STRUCTURE_PROGRESS
 | ||||||
| const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 }; | const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 }; | ||||||
| const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" }; | const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" }; | ||||||
| const char* bypass_name[] = { "BypassTPMCheck", "BypassSecureBootCheck", "BypassRAMCheck" }; |  | ||||||
| RUFUS_DRIVE rufus_drive[MAX_DRIVES] = { 0 }; | RUFUS_DRIVE rufus_drive[MAX_DRIVES] = { 0 }; | ||||||
| 
 | 
 | ||||||
| // TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG,
 | // TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG,
 | ||||||
|  | @ -1175,7 +1176,12 @@ static void UpdateImage(BOOL update_image_option_only) | ||||||
| 	IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, imop_win_sel)); | 	IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, imop_win_sel)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint8_t FindArch(const char* filename) | /// <summary>
 | ||||||
|  | /// Parse a PE executable file and return its CPU architecture.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="path">The path of the PE executable to parse.</param>
 | ||||||
|  | /// <returns>An enum ArchType value (as defined in rufus.h)</returns>
 | ||||||
|  | static uint8_t FindArch(const char* path) | ||||||
| { | { | ||||||
| 	uint8_t ret = ARCH_UNKNOWN; | 	uint8_t ret = ARCH_UNKNOWN; | ||||||
| 	HANDLE hFile = NULL, hFileMapping = NULL; | 	HANDLE hFile = NULL, hFileMapping = NULL; | ||||||
|  | @ -1184,9 +1190,9 @@ static uint8_t FindArch(const char* filename) | ||||||
| 	// PE headers, so we don't need to care about using PIMAGE_NT_HEADERS[32|64]
 | 	// PE headers, so we don't need to care about using PIMAGE_NT_HEADERS[32|64]
 | ||||||
| 	PIMAGE_NT_HEADERS pImageNTHeader = NULL; | 	PIMAGE_NT_HEADERS pImageNTHeader = NULL; | ||||||
| 
 | 
 | ||||||
| 	hFile = CreateFileU(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | 	hFile = CreateFileU(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | ||||||
| 	if (hFile == NULL) { | 	if (hFile == NULL) { | ||||||
| 		uprintf("FindArch: Could not open file '%s': %s", filename, WindowsErrorString()); | 		uprintf("FindArch: Could not open file '%s': %s", path, WindowsErrorString()); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1250,172 +1256,6 @@ out: | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// <summary>
 |  | ||||||
| /// Create an installation answer file containing the sections specified by the flags.
 |  | ||||||
| /// </summary>
 |  | ||||||
| /// <param name="arch">The processor architecture of the Windows image being used.</param>
 |  | ||||||
| /// <param name="flags">A bitmask representing the sections to enable.
 |  | ||||||
| /// See "Windows User Experience flags and masks" from in rufus.h</param>
 |  | ||||||
| /// <returns>The path of a newly created answer file on success or NULL on error.</returns>
 |  | ||||||
| static char* CreateUnattendXml(int arch, int flags) |  | ||||||
| { |  | ||||||
| 	static char path[MAX_PATH]; |  | ||||||
| 	FILE* fd; |  | ||||||
| 	int i, order; |  | ||||||
| 	const char* xml_arch_names[5] = { "x86", "amd64", "arm", "arm64" }; |  | ||||||
| 	unattend_xml_flags = flags; |  | ||||||
| 	if (arch < ARCH_X86_32 || arch >= ARCH_ARM_64 || flags == 0) |  | ||||||
| 		return NULL; |  | ||||||
| 	arch--; |  | ||||||
| 	// coverity[swapped_arguments]
 |  | ||||||
| 	if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, path) == 0) |  | ||||||
| 		return NULL; |  | ||||||
| 	fd = fopen(path, "w"); |  | ||||||
| 	if (fd == NULL) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	fprintf(fd, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); |  | ||||||
| 	fprintf(fd, "<unattend xmlns=\"urn:schemas-microsoft-com:unattend\">\n"); |  | ||||||
| 
 |  | ||||||
| 	// This part produces the unbecoming display of a command prompt window during initial setup as well
 |  | ||||||
| 	// as alters the layout and options of the initial Windows installer screens, which may scare users.
 |  | ||||||
| 	// So, in format.c, we'll try to insert the registry keys directly and drop this section. However,
 |  | ||||||
| 	// because Microsoft prevents Store apps from editing an offline registry, we do need this fallback.
 |  | ||||||
| 	if (flags & UNATTEND_WINPE_SETUP_MASK) { |  | ||||||
| 		order = 1; |  | ||||||
| 		fprintf(fd, "  <settings pass=\"windowsPE\">\n"); |  | ||||||
| 		fprintf(fd, "    <component name=\"Microsoft-Windows-Setup\" processorArchitecture=\"%s\" language=\"neutral\" " |  | ||||||
| 			"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |  | ||||||
| 			"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); |  | ||||||
| 		// WinPE will complain if we don't provide a product key. *Any* product key. This is soooo idiotic...
 |  | ||||||
| 		fprintf(fd, "      <UserData>\n"); |  | ||||||
| 		fprintf(fd, "        <ProductKey>\n"); |  | ||||||
| 		fprintf(fd, "          <Key />\n"); |  | ||||||
| 		fprintf(fd, "        </ProductKey>\n"); |  | ||||||
| 		fprintf(fd, "      </UserData>\n"); |  | ||||||
| 		if (flags & UNATTEND_SECUREBOOT_TPM_MINRAM) { |  | ||||||
| 			fprintf(fd, "      <RunSynchronous>\n"); |  | ||||||
| 			for (i = 0; i < ARRAYSIZE(bypass_name); i++) { |  | ||||||
| 				fprintf(fd, "        <RunSynchronousCommand wcm:action=\"add\">\n"); |  | ||||||
| 				fprintf(fd, "          <Order>%d</Order>\n", order++); |  | ||||||
| 				fprintf(fd, "          <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v %s /t REG_DWORD /d 1 /f</Path>\n", bypass_name[i]); |  | ||||||
| 				fprintf(fd, "        </RunSynchronousCommand>\n"); |  | ||||||
| 			} |  | ||||||
| 			fprintf(fd, "      </RunSynchronous>\n"); |  | ||||||
| 		} |  | ||||||
| 		fprintf(fd, "    </component>\n"); |  | ||||||
| 		fprintf(fd, "  </settings>\n"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (flags & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) { |  | ||||||
| 		order = 1; |  | ||||||
| 		fprintf(fd, "  <settings pass=\"specialize\">\n"); |  | ||||||
| 		fprintf(fd, "    <component name=\"Microsoft-Windows-Deployment\" processorArchitecture=\"%s\" language=\"neutral\" " |  | ||||||
| 			"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |  | ||||||
| 			"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); |  | ||||||
| 		fprintf(fd, "      <RunSynchronous>\n"); |  | ||||||
| 		// This part was picked from https://github.com/AveYo/MediaCreationTool.bat/blob/main/bypass11/AutoUnattend.xml
 |  | ||||||
| 		if (flags & UNATTEND_NO_ONLINE_ACCOUNT) { |  | ||||||
| 			fprintf(fd, "        <RunSynchronousCommand wcm:action=\"add\">\n"); |  | ||||||
| 			fprintf(fd, "          <Order>%d</Order>\n", order++); |  | ||||||
| 			fprintf(fd, "          <Path>reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f</Path>\n"); |  | ||||||
| 			fprintf(fd, "        </RunSynchronousCommand>\n"); |  | ||||||
| 		} |  | ||||||
| 		fprintf(fd, "      </RunSynchronous>\n"); |  | ||||||
| 		fprintf(fd, "    </component>\n"); |  | ||||||
| 		fprintf(fd, "  </settings>\n"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (flags & UNATTEND_OOBE_MASK) { |  | ||||||
| 		order = 1; |  | ||||||
| 		fprintf(fd, "  <settings pass=\"oobeSystem\">\n"); |  | ||||||
| 		if (flags & UNATTEND_OOBE_SHELL_SETUP_MASK) { |  | ||||||
| 			fprintf(fd, "    <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"%s\" language=\"neutral\" " |  | ||||||
| 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |  | ||||||
| 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); |  | ||||||
| 			// https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-oobe-protectyourpc
 |  | ||||||
| 			// It is really super insidous of Microsoft to call this option "ProtectYourPC", when it's really only about
 |  | ||||||
| 			// data collection. But of course, if it was called "AllowDataCollection", everyone would turn it off...
 |  | ||||||
| 			if (flags & UNATTEND_NO_DATA_COLLECTION) { |  | ||||||
| 				fprintf(fd, "      <OOBE>\n"); |  | ||||||
| 				fprintf(fd, "        <ProtectYourPC>3</ProtectYourPC>\n"); |  | ||||||
| 				fprintf(fd, "      </OOBE>\n"); |  | ||||||
| 			} |  | ||||||
| 			if (flags & UNATTEND_DUPLICATE_USER) { |  | ||||||
| 				order = 1; |  | ||||||
| 				char username[128] = { 0 }; |  | ||||||
| 				DWORD size = sizeof(username); |  | ||||||
| 				if (GetUserNameU(username, &size) && username[0] != 0) { |  | ||||||
| 					// If we create a local account in unattend.xml, then we can get Windows 11
 |  | ||||||
| 					// 22H2 to skip MSA even if the network is connected during installation.
 |  | ||||||
| 					fprintf(fd, "      <UserAccounts>\n"); |  | ||||||
| 					fprintf(fd, "        <LocalAccounts>\n"); |  | ||||||
| 					fprintf(fd, "          <LocalAccount wcm:action=\"add\">\n"); |  | ||||||
| 					fprintf(fd, "            <Name>%s</Name>\n", username); |  | ||||||
| 					fprintf(fd, "            <DisplayName>%s</DisplayName>\n", username); |  | ||||||
| 					fprintf(fd, "            <Group>Administrators;Power Users</Group>\n"); |  | ||||||
| 					// Sets an empty password for the account (which, in Microsoft's convoluted ways,
 |  | ||||||
| 					// needs to be initialized to the Base64 encoded UTF-16 string "Password").
 |  | ||||||
| 					// The use of an empty password has both the advantage of not having to ask users
 |  | ||||||
| 					// to type in a password in Rufus (which they might be weary of) as well as allowing
 |  | ||||||
| 					// automated logon during setup.
 |  | ||||||
| 					fprintf(fd, "            <Password>\n"); |  | ||||||
| 					fprintf(fd, "              <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>\n"); |  | ||||||
| 					fprintf(fd, "              <PlainText>false</PlainText>\n"); |  | ||||||
| 					fprintf(fd, "            </Password>\n"); |  | ||||||
| 					fprintf(fd, "          </LocalAccount>\n"); |  | ||||||
| 					fprintf(fd, "        </LocalAccounts>\n"); |  | ||||||
| 					fprintf(fd, "      </UserAccounts>\n"); |  | ||||||
| 					// Since we set a blank password, we'll ask the user to change it at next logon.
 |  | ||||||
| 					fprintf(fd, "      <FirstLogonCommands>\n"); |  | ||||||
| 					fprintf(fd, "        <SynchronousCommand wcm:action=\"add\">\n"); |  | ||||||
| 					fprintf(fd, "          <Order>%d</Order>\n", order++); |  | ||||||
| 					fprintf(fd, "          <CommandLine>net user "%s" /logonpasswordchg:yes</CommandLine>\n", username); |  | ||||||
| 					fprintf(fd, "        </SynchronousCommand>\n"); |  | ||||||
| 					fprintf(fd, "      </FirstLogonCommands>\n"); |  | ||||||
| 				} else { |  | ||||||
| 					uprintf("Warning: Could not retreive current user name. Local Account was not created"); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			fprintf(fd, "    </component>\n"); |  | ||||||
| 		} |  | ||||||
| 		if (flags & UNATTEND_OOBE_INTERNATIONAL_MASK) { |  | ||||||
| 			fprintf(fd, "    <component name=\"Microsoft-Windows-International-Core\" processorArchitecture=\"%s\" language=\"neutral\" " |  | ||||||
| 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |  | ||||||
| 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); |  | ||||||
| 			// What a frigging mess retreiving and trying to match the various locales
 |  | ||||||
| 			// Microsoft has made. And, *NO*, the new User Language Settings have not
 |  | ||||||
| 			// improved things in the slightest. They made it much worse for developers!
 |  | ||||||
| 			fprintf(fd, "      <InputLocale>%s</InputLocale>\n", |  | ||||||
| 				ReadRegistryKeyStr(REGKEY_HKCU, "Keyboard Layout\\Preload\\1")); |  | ||||||
| 			fprintf(fd, "      <SystemLocale>%s</SystemLocale>\n", ToLocaleName(GetSystemDefaultLCID())); |  | ||||||
| 			fprintf(fd, "      <UserLocale>%s</UserLocale>\n", ToLocaleName(GetUserDefaultLCID())); |  | ||||||
| 			fprintf(fd, "      <UILanguage>%s</UILanguage>\n", ToLocaleName(GetUserDefaultUILanguage())); |  | ||||||
| 			fprintf(fd, "      <UILanguageFallback>%s</UILanguageFallback>\n", |  | ||||||
| 				// NB: Officially, this is a REG_MULTI_SZ string
 |  | ||||||
| 				ReadRegistryKeyStr(REGKEY_HKLM, "SYSTEM\\CurrentControlSet\\Control\\Nls\\Language\\InstallLanguageFallback")); |  | ||||||
| 			fprintf(fd, "    </component>\n"); |  | ||||||
| 		} |  | ||||||
| 		fprintf(fd, "  </settings>\n"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (flags & UNATTEND_OFFLINE_SERVICING_MASK) { |  | ||||||
| 		fprintf(fd, "  <settings pass=\"offlineServicing\">\n"); |  | ||||||
| 		if (flags & UNATTEND_OFFLINE_INTERNAL_DRIVES) { |  | ||||||
| 			fprintf(fd, "    <component name=\"Microsoft-Windows-PartitionManager\" processorArchitecture=\"%s\" language=\"neutral\" " |  | ||||||
| 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |  | ||||||
| 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); |  | ||||||
| 			fprintf(fd, "      <SanPolicy>4</SanPolicy>\n"); |  | ||||||
| 			fprintf(fd, "    </component>\n"); |  | ||||||
| 		} |  | ||||||
| 		fprintf(fd, "  </settings>\n"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fprintf(fd, "</unattend>\n"); |  | ||||||
| 	fclose(fd); |  | ||||||
| 	return path; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // The scanning process can be blocking for message processing => use a thread
 | // The scanning process can be blocking for message processing => use a thread
 | ||||||
| DWORD WINAPI ImageScanThread(LPVOID param) | DWORD WINAPI ImageScanThread(LPVOID param) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -637,7 +637,6 @@ extern char* WimMountImage(const char* image, int index); | ||||||
| extern BOOL WimUnmountImage(const char* image, int index); | extern BOOL WimUnmountImage(const char* image, int index); | ||||||
| extern int8_t IsBootableImage(const char* path); | extern int8_t IsBootableImage(const char* path); | ||||||
| extern BOOL AppendVHDFooter(const char* vhd_path); | extern BOOL AppendVHDFooter(const char* vhd_path); | ||||||
| extern int SetWinToGoIndex(void); |  | ||||||
| extern int IsHDD(DWORD DriveIndex, uint16_t vid, uint16_t pid, const char* strid); | extern int IsHDD(DWORD DriveIndex, uint16_t vid, uint16_t pid, const char* strid); | ||||||
| extern char* GetSignatureName(const char* path, const char* country_code, BOOL bSilent); | extern char* GetSignatureName(const char* path, const char* country_code, BOOL bSilent); | ||||||
| extern uint64_t GetSignatureTimeStamp(const char* path); | extern uint64_t GetSignatureTimeStamp(const char* path); | ||||||
|  | @ -670,7 +669,6 @@ extern HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAcce | ||||||
| 	DWORD dwFlagsAndAttributes, LONGLONG fileSize); | 	DWORD dwFlagsAndAttributes, LONGLONG fileSize); | ||||||
| #define GetTextWidth(hDlg, id) GetTextSize(GetDlgItem(hDlg, id), NULL).cx | #define GetTextWidth(hDlg, id) GetTextSize(GetDlgItem(hDlg, id), NULL).cx | ||||||
| 
 | 
 | ||||||
| DWORD WINAPI FormatThread(void* param); |  | ||||||
| DWORD WINAPI SaveImageThread(void* param); | DWORD WINAPI SaveImageThread(void* param); | ||||||
| DWORD WINAPI SumThread(void* param); | DWORD WINAPI SumThread(void* param); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							|  | @ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | ||||||
| IDD_DIALOG DIALOGEX 12, 12, 232, 326 | IDD_DIALOG DIALOGEX 12, 12, 232, 326 | ||||||
| STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | ||||||
| EXSTYLE WS_EX_ACCEPTFILES | EXSTYLE WS_EX_ACCEPTFILES | ||||||
| CAPTION "Rufus 3.20.1921" | CAPTION "Rufus 3.20.1923" | ||||||
| FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | ||||||
| BEGIN | BEGIN | ||||||
|     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP |     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP | ||||||
|  | @ -395,8 +395,8 @@ END | ||||||
| // | // | ||||||
| 
 | 
 | ||||||
| VS_VERSION_INFO VERSIONINFO | VS_VERSION_INFO VERSIONINFO | ||||||
|  FILEVERSION 3,20,1921,0 |  FILEVERSION 3,20,1923,0 | ||||||
|  PRODUCTVERSION 3,20,1921,0 |  PRODUCTVERSION 3,20,1923,0 | ||||||
|  FILEFLAGSMASK 0x3fL |  FILEFLAGSMASK 0x3fL | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
|  FILEFLAGS 0x1L |  FILEFLAGS 0x1L | ||||||
|  | @ -414,13 +414,13 @@ BEGIN | ||||||
|             VALUE "Comments", "https://rufus.ie" |             VALUE "Comments", "https://rufus.ie" | ||||||
|             VALUE "CompanyName", "Akeo Consulting" |             VALUE "CompanyName", "Akeo Consulting" | ||||||
|             VALUE "FileDescription", "Rufus" |             VALUE "FileDescription", "Rufus" | ||||||
|             VALUE "FileVersion", "3.20.1921" |             VALUE "FileVersion", "3.20.1923" | ||||||
|             VALUE "InternalName", "Rufus" |             VALUE "InternalName", "Rufus" | ||||||
|             VALUE "LegalCopyright", "© 2011-2022 Pete Batard (GPL v3)" |             VALUE "LegalCopyright", "© 2011-2022 Pete Batard (GPL v3)" | ||||||
|             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" |             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" | ||||||
|             VALUE "OriginalFilename", "rufus-3.20.exe" |             VALUE "OriginalFilename", "rufus-3.20.exe" | ||||||
|             VALUE "ProductName", "Rufus" |             VALUE "ProductName", "Rufus" | ||||||
|             VALUE "ProductVersion", "3.20.1921" |             VALUE "ProductVersion", "3.20.1923" | ||||||
|         END |         END | ||||||
|     END |     END | ||||||
|     BLOCK "VarFileInfo" |     BLOCK "VarFileInfo" | ||||||
|  |  | ||||||
							
								
								
									
										778
									
								
								src/wue.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										778
									
								
								src/wue.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,778 @@ | ||||||
|  | /*
 | ||||||
|  |  * Rufus: The Reliable USB Formatting Utility | ||||||
|  |  * Windows User Experience | ||||||
|  |  * Copyright © 2022 Pete Batard <pete@akeo.ie> | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <windows.h> | ||||||
|  | #include <windowsx.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #include "rufus.h" | ||||||
|  | #include "drive.h" | ||||||
|  | #include "format.h" | ||||||
|  | #include "missing.h" | ||||||
|  | #include "resource.h" | ||||||
|  | #include "registry.h" | ||||||
|  | #include "msapi_utf8.h" | ||||||
|  | #include "localization.h" | ||||||
|  | 
 | ||||||
|  |  /* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */ | ||||||
|  | #ifdef _CRTDBG_MAP_ALLOC | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <crtdbg.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | const char* bypass_name[] = { "BypassTPMCheck", "BypassSecureBootCheck", "BypassRAMCheck" }; | ||||||
|  | 
 | ||||||
|  | int unattend_xml_flags = 0, wintogo_index = -1, wininst_index = 0; | ||||||
|  | int unattend_xml_mask = UNATTEND_DEFAULT_SELECTION_MASK; | ||||||
|  | char* unattend_xml_path = NULL; | ||||||
|  | 
 | ||||||
|  | extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files; | ||||||
|  | 
 | ||||||
|  |  /// <summary>
 | ||||||
|  |  /// Create an installation answer file containing the sections specified by the flags.
 | ||||||
|  |  /// </summary>
 | ||||||
|  |  /// <param name="arch">The processor architecture of the Windows image being used.</param>
 | ||||||
|  |  /// <param name="flags">A bitmask representing the sections to enable.
 | ||||||
|  |  /// See "Windows User Experience flags and masks" from rufus.h</param>
 | ||||||
|  |  /// <returns>The path of a newly created answer file on success or NULL on error.</returns>
 | ||||||
|  | char* CreateUnattendXml(int arch, int flags) | ||||||
|  | { | ||||||
|  | 	static char path[MAX_PATH]; | ||||||
|  | 	FILE* fd; | ||||||
|  | 	int i, order; | ||||||
|  | 	const char* xml_arch_names[5] = { "x86", "amd64", "arm", "arm64" }; | ||||||
|  | 	unattend_xml_flags = flags; | ||||||
|  | 	if (arch < ARCH_X86_32 || arch > ARCH_ARM_64 || flags == 0) | ||||||
|  | 		return NULL; | ||||||
|  | 	arch--; | ||||||
|  | 	// coverity[swapped_arguments]
 | ||||||
|  | 	if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, path) == 0) | ||||||
|  | 		return NULL; | ||||||
|  | 	fd = fopen(path, "w"); | ||||||
|  | 	if (fd == NULL) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	fprintf(fd, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); | ||||||
|  | 	fprintf(fd, "<unattend xmlns=\"urn:schemas-microsoft-com:unattend\">\n"); | ||||||
|  | 
 | ||||||
|  | 	// This part produces the unbecoming display of a command prompt window during initial setup as well
 | ||||||
|  | 	// as alters the layout and options of the initial Windows installer screens, which may scare users.
 | ||||||
|  | 	// So, in format.c, we'll try to insert the registry keys directly and drop this section. However,
 | ||||||
|  | 	// because Microsoft prevents Store apps from editing an offline registry, we do need this fallback.
 | ||||||
|  | 	if (flags & UNATTEND_WINPE_SETUP_MASK) { | ||||||
|  | 		order = 1; | ||||||
|  | 		fprintf(fd, "  <settings pass=\"windowsPE\">\n"); | ||||||
|  | 		fprintf(fd, "    <component name=\"Microsoft-Windows-Setup\" processorArchitecture=\"%s\" language=\"neutral\" " | ||||||
|  | 			"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||||||
|  | 			"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); | ||||||
|  | 		// WinPE will complain if we don't provide a product key. *Any* product key. This is soooo idiotic...
 | ||||||
|  | 		fprintf(fd, "      <UserData>\n"); | ||||||
|  | 		fprintf(fd, "        <ProductKey>\n"); | ||||||
|  | 		fprintf(fd, "          <Key />\n"); | ||||||
|  | 		fprintf(fd, "        </ProductKey>\n"); | ||||||
|  | 		fprintf(fd, "      </UserData>\n"); | ||||||
|  | 		if (flags & UNATTEND_SECUREBOOT_TPM_MINRAM) { | ||||||
|  | 			fprintf(fd, "      <RunSynchronous>\n"); | ||||||
|  | 			for (i = 0; i < ARRAYSIZE(bypass_name); i++) { | ||||||
|  | 				fprintf(fd, "        <RunSynchronousCommand wcm:action=\"add\">\n"); | ||||||
|  | 				fprintf(fd, "          <Order>%d</Order>\n", order++); | ||||||
|  | 				fprintf(fd, "          <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v %s /t REG_DWORD /d 1 /f</Path>\n", bypass_name[i]); | ||||||
|  | 				fprintf(fd, "        </RunSynchronousCommand>\n"); | ||||||
|  | 			} | ||||||
|  | 			fprintf(fd, "      </RunSynchronous>\n"); | ||||||
|  | 		} | ||||||
|  | 		fprintf(fd, "    </component>\n"); | ||||||
|  | 		fprintf(fd, "  </settings>\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (flags & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) { | ||||||
|  | 		order = 1; | ||||||
|  | 		fprintf(fd, "  <settings pass=\"specialize\">\n"); | ||||||
|  | 		fprintf(fd, "    <component name=\"Microsoft-Windows-Deployment\" processorArchitecture=\"%s\" language=\"neutral\" " | ||||||
|  | 			"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||||||
|  | 			"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); | ||||||
|  | 		fprintf(fd, "      <RunSynchronous>\n"); | ||||||
|  | 		// This part was picked from https://github.com/AveYo/MediaCreationTool.bat/blob/main/bypass11/AutoUnattend.xml
 | ||||||
|  | 		if (flags & UNATTEND_NO_ONLINE_ACCOUNT) { | ||||||
|  | 			fprintf(fd, "        <RunSynchronousCommand wcm:action=\"add\">\n"); | ||||||
|  | 			fprintf(fd, "          <Order>%d</Order>\n", order++); | ||||||
|  | 			fprintf(fd, "          <Path>reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f</Path>\n"); | ||||||
|  | 			fprintf(fd, "        </RunSynchronousCommand>\n"); | ||||||
|  | 		} | ||||||
|  | 		fprintf(fd, "      </RunSynchronous>\n"); | ||||||
|  | 		fprintf(fd, "    </component>\n"); | ||||||
|  | 		fprintf(fd, "  </settings>\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (flags & UNATTEND_OOBE_MASK) { | ||||||
|  | 		order = 1; | ||||||
|  | 		fprintf(fd, "  <settings pass=\"oobeSystem\">\n"); | ||||||
|  | 		if (flags & UNATTEND_OOBE_SHELL_SETUP_MASK) { | ||||||
|  | 			fprintf(fd, "    <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"%s\" language=\"neutral\" " | ||||||
|  | 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||||||
|  | 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); | ||||||
|  | 			// https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-oobe-protectyourpc
 | ||||||
|  | 			// It is really super insidous of Microsoft to call this option "ProtectYourPC", when it's really only about
 | ||||||
|  | 			// data collection. But of course, if it was called "AllowDataCollection", everyone would turn it off...
 | ||||||
|  | 			if (flags & UNATTEND_NO_DATA_COLLECTION) { | ||||||
|  | 				fprintf(fd, "      <OOBE>\n"); | ||||||
|  | 				fprintf(fd, "        <ProtectYourPC>3</ProtectYourPC>\n"); | ||||||
|  | 				fprintf(fd, "      </OOBE>\n"); | ||||||
|  | 			} | ||||||
|  | 			if (flags & UNATTEND_DUPLICATE_USER) { | ||||||
|  | 				order = 1; | ||||||
|  | 				char username[128] = { 0 }; | ||||||
|  | 				DWORD size = sizeof(username); | ||||||
|  | 				if (GetUserNameU(username, &size) && username[0] != 0) { | ||||||
|  | 					// If we create a local account in unattend.xml, then we can get Windows 11
 | ||||||
|  | 					// 22H2 to skip MSA even if the network is connected during installation.
 | ||||||
|  | 					fprintf(fd, "      <UserAccounts>\n"); | ||||||
|  | 					fprintf(fd, "        <LocalAccounts>\n"); | ||||||
|  | 					fprintf(fd, "          <LocalAccount wcm:action=\"add\">\n"); | ||||||
|  | 					fprintf(fd, "            <Name>%s</Name>\n", username); | ||||||
|  | 					fprintf(fd, "            <DisplayName>%s</DisplayName>\n", username); | ||||||
|  | 					fprintf(fd, "            <Group>Administrators;Power Users</Group>\n"); | ||||||
|  | 					// Sets an empty password for the account (which, in Microsoft's convoluted ways,
 | ||||||
|  | 					// needs to be initialized to the Base64 encoded UTF-16 string "Password").
 | ||||||
|  | 					// The use of an empty password has both the advantage of not having to ask users
 | ||||||
|  | 					// to type in a password in Rufus (which they might be weary of) as well as allowing
 | ||||||
|  | 					// automated logon during setup.
 | ||||||
|  | 					fprintf(fd, "            <Password>\n"); | ||||||
|  | 					fprintf(fd, "              <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>\n"); | ||||||
|  | 					fprintf(fd, "              <PlainText>false</PlainText>\n"); | ||||||
|  | 					fprintf(fd, "            </Password>\n"); | ||||||
|  | 					fprintf(fd, "          </LocalAccount>\n"); | ||||||
|  | 					fprintf(fd, "        </LocalAccounts>\n"); | ||||||
|  | 					fprintf(fd, "      </UserAccounts>\n"); | ||||||
|  | 					// Since we set a blank password, we'll ask the user to change it at next logon.
 | ||||||
|  | 					fprintf(fd, "      <FirstLogonCommands>\n"); | ||||||
|  | 					fprintf(fd, "        <SynchronousCommand wcm:action=\"add\">\n"); | ||||||
|  | 					fprintf(fd, "          <Order>%d</Order>\n", order++); | ||||||
|  | 					fprintf(fd, "          <CommandLine>net user "%s" /logonpasswordchg:yes</CommandLine>\n", username); | ||||||
|  | 					fprintf(fd, "        </SynchronousCommand>\n"); | ||||||
|  | 					fprintf(fd, "      </FirstLogonCommands>\n"); | ||||||
|  | 				} else { | ||||||
|  | 					uprintf("Warning: Could not retreive current user name. Local Account was not created"); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			fprintf(fd, "    </component>\n"); | ||||||
|  | 		} | ||||||
|  | 		if (flags & UNATTEND_OOBE_INTERNATIONAL_MASK) { | ||||||
|  | 			fprintf(fd, "    <component name=\"Microsoft-Windows-International-Core\" processorArchitecture=\"%s\" language=\"neutral\" " | ||||||
|  | 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||||||
|  | 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); | ||||||
|  | 			// What a frigging mess retreiving and trying to match the various locales
 | ||||||
|  | 			// Microsoft has made. And, *NO*, the new User Language Settings have not
 | ||||||
|  | 			// improved things in the slightest. They made it much worse for developers!
 | ||||||
|  | 			fprintf(fd, "      <InputLocale>%s</InputLocale>\n", | ||||||
|  | 				ReadRegistryKeyStr(REGKEY_HKCU, "Keyboard Layout\\Preload\\1")); | ||||||
|  | 			fprintf(fd, "      <SystemLocale>%s</SystemLocale>\n", ToLocaleName(GetSystemDefaultLCID())); | ||||||
|  | 			fprintf(fd, "      <UserLocale>%s</UserLocale>\n", ToLocaleName(GetUserDefaultLCID())); | ||||||
|  | 			fprintf(fd, "      <UILanguage>%s</UILanguage>\n", ToLocaleName(GetUserDefaultUILanguage())); | ||||||
|  | 			fprintf(fd, "      <UILanguageFallback>%s</UILanguageFallback>\n", | ||||||
|  | 				// NB: Officially, this is a REG_MULTI_SZ string
 | ||||||
|  | 				ReadRegistryKeyStr(REGKEY_HKLM, "SYSTEM\\CurrentControlSet\\Control\\Nls\\Language\\InstallLanguageFallback")); | ||||||
|  | 			fprintf(fd, "    </component>\n"); | ||||||
|  | 		} | ||||||
|  | 		fprintf(fd, "  </settings>\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (flags & UNATTEND_OFFLINE_SERVICING_MASK) { | ||||||
|  | 		fprintf(fd, "  <settings pass=\"offlineServicing\">\n"); | ||||||
|  | 		if (flags & UNATTEND_OFFLINE_INTERNAL_DRIVES) { | ||||||
|  | 			fprintf(fd, "    <component name=\"Microsoft-Windows-PartitionManager\" processorArchitecture=\"%s\" language=\"neutral\" " | ||||||
|  | 				"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||||||
|  | 				"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); | ||||||
|  | 			fprintf(fd, "      <SanPolicy>4</SanPolicy>\n"); | ||||||
|  | 			fprintf(fd, "    </component>\n"); | ||||||
|  | 		} | ||||||
|  | 		fprintf(fd, "  </settings>\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fprintf(fd, "</unattend>\n"); | ||||||
|  | 	fclose(fd); | ||||||
|  | 	return path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Setup and patch WinPE for Windows XP bootable USBs.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="drive_letter">The letter identifying the target drive.</param>
 | ||||||
|  | /// <returns>TRUE on success, FALSE on error.</returns>
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Checks which versions of Windows are available in an install image
 | ||||||
|  | /// to set our extraction index. Asks the user to select one if needed.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="">(none)</param>
 | ||||||
|  | /// <returns>-2 on user cancel, -1 on other error, >=0 on success.</returns>
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Setup a Windows To Go drive according to the official Microsoft instructions detailed at:
 | ||||||
|  | /// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/jj721578(v=ws.11).
 | ||||||
|  | /// Note that as opposed to the technet guide above we use bcdedit rather than 'unattend.xml'
 | ||||||
|  | /// to disable the recovery environment.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="DriveIndex">The Rufus drive index for the target media.</param>
 | ||||||
|  | /// <param name="drive_name">The path of the target media.</param>
 | ||||||
|  | /// <param name="use_esp">Whether to create an ESP on the target media.</param>
 | ||||||
|  | /// <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]; | ||||||
|  | 	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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Add unattend.xml to 'sources\boot.wim' (install) or 'Windows\Panther\' (Windows To Go).
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="drive_letter">The letter of the drive where the \sources\boot.wim image resides.</param>
 | ||||||
|  | /// <param name="flags">A bitmap of unattend flags to apply.</param>
 | ||||||
|  | /// <returns>TRUE on success, FALSE on error.</returns>
 | ||||||
|  | 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; | ||||||
|  | 	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; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								src/wue.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/wue.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Rufus: The Reliable USB Formatting Utility | ||||||
|  |  * Windows User Experience | ||||||
|  |  * Copyright © 2022 Pete Batard <pete@akeo.ie> | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <windows.h> | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | extern int unattend_xml_flags, unattend_xml_mask; | ||||||
|  | extern int wintogo_index, wininst_index; | ||||||
|  | extern char* unattend_xml_path; | ||||||
|  | 
 | ||||||
|  | char* CreateUnattendXml(int arch, int flags); | ||||||
|  | BOOL ApplyWindowsCustomization(char drive_letter, int flags); | ||||||
|  | int SetWinToGoIndex(void); | ||||||
|  | BOOL SetupWinPE(char drive_letter); | ||||||
|  | BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp); | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue