feat: Use overlays for all modern Windows media (#2082)

This commit is contained in:
Kroese
2026-08-06 19:17:51 +02:00
committed by GitHub
parent 927f92282c
commit 91326c9664
4 changed files with 39 additions and 276 deletions
+9 -46
View File
@@ -151,7 +151,6 @@ addAnswerFile() {
local language="$2" local language="$2"
local stage="$3" local stage="$3"
local script="" name
local answer="$stage/Autounattend.xml" local answer="$stage/Autounattend.xml"
if enabled "$MANUAL"; then if enabled "$MANUAL"; then
@@ -164,7 +163,9 @@ addAnswerFile() {
return 1 return 1
fi fi
local name
name=$(basename "$asset") || return 1 name=$(basename "$asset") || return 1
info "Adding $name for automatic installation..." info "Adding $name for automatic installation..."
if ! cp -L -- "$asset" "$answer"; then if ! cp -L -- "$asset" "$answer"; then
@@ -185,7 +186,7 @@ addAnswerFile() {
fi fi
if ! updateDiskID "$answer" "${DISK_TYPE:-}" "setup"; then if ! updateDiskID "$answer" "${DISK_TYPE:-}"; then
error "Failed to adjust the Windows installation disk!" error "Failed to adjust the Windows installation disk!"
exit 85 exit 85
fi fi
@@ -199,7 +200,7 @@ addAnswerFile() {
if [ -z "${CUSTOM_XML:-}" ]; then if [ -z "${CUSTOM_XML:-}" ]; then
script=$(prepareSetupScript "$asset" "$stage") || exit 84 prepareSetupScript "$asset" "$stage" || exit 84
fi fi
@@ -980,7 +981,6 @@ updateDiskID() {
local asset="$1" local asset="$1"
local disk_type="${2,,}" local disk_type="${2,,}"
local mode="${3:-setup}"
local target="0" local target="0"
local setup="$XML_COMPONENT_SETUP" local setup="$XML_COMPONENT_SETUP"
@@ -990,19 +990,10 @@ updateDiskID() {
[ -s "$asset" ] || return 1 [ -s "$asset" ] || return 1
# The setup overlay occupies disk 0, so common VirtIO installation disks move # The setup overlay occupies disk 0, so VirtIO disks move to disk 1.
# to disk 1 in setup-image mode. Rebuilt media keeps the original disk layout.
case "$mode" in
"setup" )
case "$disk_type" in
"" | "scsi" | "virtio-scsi" | "blk" | "virtio-blk" ) target="1" ;;
esac ;;
"image" ) ;;
* ) return 1 ;;
case "$disk_type" in
"" | "scsi" | "virtio-scsi" | "blk" | "virtio-blk" ) target="1" ;;
esac esac
count=$(getXMLNodeCount "$asset" "$disk_ids") || { count=$(getXMLNodeCount "$asset" "$disk_ids") || {
@@ -1922,15 +1913,15 @@ prepareSetupScript() {
local asset="$1" local asset="$1"
local stage="$2" local stage="$2"
local staged=""
local staged=""
staged=$(stageSetupScript "$asset" "$stage") || return 1 staged=$(stageSetupScript "$asset" "$stage") || return 1
[ -n "$staged" ] || return 0 [ -n "$staged" ] || return 0
updateSetupScript "$staged" "$asset" || return 1 updateSetupScript "$staged" "$asset" || return 1
finalizeSetupScript "$staged" || return 1 finalizeSetupScript "$staged" || return 1
printf '%s' "$staged"
return 0 return 0
} }
@@ -2058,34 +2049,6 @@ stageSetupScript() {
return 0 return 0
} }
installSetupScript() {
local script="$1"
local root="$2"
local target
[ -n "$script" ] || return 0
if [ ! -s "$script" ]; then
error "Failed to find staged setup script: $script"
return 1
fi
target="$root/\$OEM\$/\$\$/Setup/Scripts/SetupComplete.cmd"
if ! mkdir -p "$(dirname "$target")"; then
error "Failed to create setup script directory!"
return 1
fi
if ! cp -f -- "$script" "$target"; then
error "Failed to add setup script to Windows image!"
return 1
fi
return 0
}
rewriteSetupBlock() { rewriteSetupBlock() {
local file="$1" local file="$1"
-11
View File
@@ -630,17 +630,6 @@ getPlatform() {
return 0 return 0
} }
canUseSetupImage() {
local id="$1"
local iso="$2"
supportsXML "$id" || return 1
[[ "${iso,,}" == *".esd" ]] && return 1
return 0
}
createImageDirectory() { createImageDirectory() {
local image="$1" local image="$1"
+29 -218
View File
@@ -158,8 +158,8 @@ prepareWindowsImage() {
local dir="$2" local dir="$2"
local boot="$3" local boot="$3"
# Prefer the original ISO with a small setup image whenever possible. # Keep all run-specific automation on the setup image for XML-capable media.
if canUseSetupImage "$DETECTED" "$iso"; then if supportsXML "$DETECTED"; then
if ! createOverlay "$XML" "$LANGUAGE" "$TMP/setup"; then if ! createOverlay "$XML" "$LANGUAGE" "$TMP/setup"; then
skipUnattended "$dir" "$iso" "$boot" || return 84 skipUnattended "$dir" "$iso" "$boot" || return 84
@@ -171,12 +171,15 @@ prepareWindowsImage() {
exit 86 exit 86
fi fi
useOriginalImage "$iso" || return 88 # Bootable ISOs can be reused unchanged with the generated setup image.
return 0 if (( ! extracted )); then
useOriginalImage "$iso" || return 88
return 0
fi
fi fi
# Legacy or modifiable media must be extracted, updated, and rebuilt. # Extracted modern sources and SIF-based legacy media require a clean rebuild.
if (( ! extracted )); then if (( ! extracted )); then
if ! extractImage "$iso" "$dir" "$VERSION"; then if ! extractImage "$iso" "$dir" "$VERSION"; then
removeIso "$iso" || : removeIso "$iso" || :
@@ -190,12 +193,6 @@ prepareWindowsImage() {
return 0 return 0
fi fi
if ! updateImage "$dir" "$XML" "$LANGUAGE"; then
skipUnattended "$dir" "$iso" "$boot" || return 94
handled=1
return 0
fi
removeImage "$iso" || return 96 removeImage "$iso" || return 96
buildImage "$dir" || return 98 buildImage "$dir" || return 98
@@ -630,6 +627,8 @@ removeIso() {
local iso="$1" local iso="$1"
[ -n "$CUSTOM" ] && return 0
rm -f -- "$iso" 2>/dev/null || : rm -f -- "$iso" 2>/dev/null || :
return 0 return 0
@@ -872,13 +871,17 @@ prepareImage() {
# Legacy rebuilt media must retain the source ISO's El Torito boot-load size. # Legacy rebuilt media must retain the source ISO's El Torito boot-load size.
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
getBootLoadSize "$iso" "$dir" "$desc" || return 1 getBootLoadSize "$iso" "$dir" "$desc" || return 1
fi fi
supportsXML "$DETECTED" || return 0 supportsXML "$DETECTED" || return 0
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
extractBootImage "$iso" "$dir" "$desc" && return 0 extractBootImage "$iso" "$dir" "$desc" && return 0
error "Failed to extract boot image from ISO image \"${iso}\"!" error "Failed to extract boot image from ISO image \"${iso}\"!"
return 1 return 1
fi fi
@@ -913,9 +916,7 @@ getOemFolder() {
addFolder() { addFolder() {
local src="$1" local src="$1"
local target="${2:-image}" local mode="${2:-copy}"
local log="${3:-Y}"
local mode="${4:-copy}"
local file="" source="" folder local file="" source="" folder
local dest="$src/\$OEM\$/\$1/OEM" local dest="$src/\$OEM\$/\$1/OEM"
@@ -925,10 +926,8 @@ addFolder() {
[ -z "$folder" ] && [ -z "$COMMAND" ] && return 0 [ -z "$folder" ] && [ -z "$COMMAND" ] && return 0
if enabled "$log"; then local msg="Adding OEM files to image..."
local msg="Adding OEM files to $target..." info "$msg" && html "$msg"
info "$msg" && html "$msg"
fi
# Setup-image mode cannot modify the original ISO, so create a temporary # Setup-image mode cannot modify the original ISO, so create a temporary
# writable copy of install.bat for the overlay image. # writable copy of install.bat for the overlay image.
@@ -1078,22 +1077,15 @@ selectDrivers() {
addDrivers() { addDrivers() {
local src="$1" local stage="$1"
local tmp="$2" local version="$2"
local version="$3"
local file="${4:-}"
local index="${5:-}"
local log="${6:-Y}"
local drivers="$tmp/drivers" local drivers="$stage/drivers"
rm -rf "$drivers" || return 1 rm -rf "$drivers" || return 1
mkdir -p "$drivers" || return 1 mkdir -p "$drivers" || return 1
if enabled "$log"; then info "Adding drivers to image..."
local msg="Adding drivers to image..."
info "$msg" && html "$msg"
fi
if [ -z "$version" ]; then if [ -z "$version" ]; then
version="win11x64" version="win11x64"
@@ -1109,21 +1101,9 @@ addDrivers() {
mkdir -p "$dest" || return 1 mkdir -p "$dest" || return 1
if [ -n "$file" ]; then
if [ -z "$index" ]; then
error "No boot image index specified!"
return 1
fi
wimlib-imagex update "$file" "$index" \
--command "delete --force --recursive /$target" >/dev/null || true
fi
selectDrivers "$version" "$drivers" "$target" || return 1 selectDrivers "$version" "$drivers" "$target" || return 1
local dst="$src/\$OEM\$/\$\$/Drivers" local dst="$stage/\$OEM\$/\$\$/Drivers"
mkdir -p "$dst" || return 1 mkdir -p "$dst" || return 1
cp -Lr "$dest/." "$dst" || return 1 cp -Lr "$dest/." "$dst" || return 1
@@ -1133,22 +1113,13 @@ addDrivers() {
rm -rf "$dest/viogpudo" || return 1 rm -rf "$dest/viogpudo" || return 1
fi fi
if [ -n "$file" ]; then local winpe="$stage/$target"
rm -rf "$winpe" || return 1
if ! wimlib-imagex update "$file" "$index" --command "add $dest /$target" >/dev/null; then mkdir -p "$winpe" || return 1
return 1 cp -Lr "$dest/." "$winpe" || return 1
fi
else
local winpe="$src/$target"
rm -rf "$winpe" || return 1
mkdir -p "$winpe" || return 1
cp -Lr "$dest/." "$winpe" || return 1
fi
rm -rf "$drivers" || return 1 rm -rf "$drivers" || return 1
return 0 return 0
} }
@@ -1158,8 +1129,6 @@ createOverlay() {
local language="$2" local language="$2"
local stage="$3" local stage="$3"
supportsXML "${DETECTED,,}" || return 0
local msg="Creating overlay image..." local msg="Creating overlay image..."
info "$msg" && html "$msg" info "$msg" && html "$msg"
@@ -1173,12 +1142,12 @@ createOverlay() {
return 1 return 1
fi fi
if ! addDrivers "$stage" "$stage" "$DETECTED"; then if ! addDrivers "$stage" "$DETECTED"; then
error "Failed to include Windows drivers!" error "Failed to include Windows drivers!"
return 1 return 1
fi fi
if ! addFolder "$stage" "image" "Y" "overlay"; then if ! addFolder "$stage" "overlay"; then
error "Failed to include OEM folder!" error "Failed to include OEM folder!"
return 1 return 1
fi fi
@@ -1188,164 +1157,6 @@ createOverlay() {
return 0 return 0
} }
updateImage() {
local dir="$1"
local asset="$2"
local language="$3"
local script=""
local tmp="/tmp/install"
local xml="autounattend.xml"
local bak="${xml//.xml/.org}"
local dat="${xml//.xml/.dat}"
local desc path src wim name info
supportsXML "${DETECTED,,}" || return 0
if [ ! -s "$asset" ] || [ ! -f "$asset" ]; then
asset=""
if ! enabled "$MANUAL"; then
MANUAL="Y"
warn "no answer file provided, $FB."
fi
fi
rm -rf "$tmp" || return 1
mkdir -p "$tmp" || return 1
src=$(find "$dir" -maxdepth 1 -type d -iname sources -print -quit) || return 1
if [ ! -d "$src" ]; then
error "failed to locate 'sources' folder in ISO image, $FB"
return 1
fi
wim=$(find "$src" -maxdepth 1 -type f \( -iname boot.wim -or -iname boot.esd \) -print -quit) || return 1
if [ ! -f "$wim" ]; then
error "failed to locate 'boot.wim' or 'boot.esd' in ISO image, $FB"
return 1
fi
# Windows Setup normally resides in boot image 2; single-image media uses 1.
local idx="1"
if ! info=$(wimlib-imagex info --xml "$wim" | iconv -f UTF-16LE -t UTF-8); then
warn "failed to read boot image information, $FB"
MANUAL="Y"
info=""
fi
if [[ "${info^^}" == *"<IMAGE INDEX=\"2\">"* ]]; then
idx="2"
fi
if ! addDrivers "$src" "$tmp" "$DETECTED" "$wim" "$idx"; then
error "Failed to add drivers to image!"
return 1
fi
if ! addFolder "$src"; then
error "Failed to add OEM folder to image!"
return 1
fi
# Preserve an original answer file only once. The .dat marker identifies an
# image where our generated answer file has already been installed.
if wimlib-imagex extract "$wim" "$idx" "/$xml" "--dest-dir=$tmp" >/dev/null 2>&1; then
if ! wimlib-imagex extract "$wim" "$idx" "/$dat" "--dest-dir=$tmp" >/dev/null 2>&1; then
if ! wimlib-imagex extract "$wim" "$idx" "/$bak" "--dest-dir=$tmp" >/dev/null 2>&1; then
if ! wimlib-imagex update "$wim" "$idx" --command "rename /$xml /$bak" > /dev/null; then
warn "failed to backup original answer file ($xml)."
fi
fi
fi
fi
if ! enabled "$MANUAL"; then
name=$(basename "$asset") || return 1
local answer="$tmp/$name"
info "Adding $name for automatic installation..."
if ! cp "$asset" "$answer"; then
error "Failed to copy answer file to $answer."
return 1
fi
removeGeneratedXML "$asset" || return 1
if [ -z "${CUSTOM_XML:-}" ]; then
if ! updateXML "$answer" "$language"; then
error "Failed to update answer file: $answer"
return 1
fi
fi
if ! updateDiskID "$answer" "${DISK_TYPE:-}" "image"; then
error "Failed to adjust the Windows installation disk!"
exit 85
fi
validateGeneratedXML "$answer" || return 1
if [ -z "${CUSTOM_XML:-}" ]; then
script=$(prepareSetupScript "$asset" "$tmp/setup") || exit 84
fi
if ! wimlib-imagex update "$wim" "$idx" --command "add $answer /$xml" > /dev/null; then
MANUAL="Y"
warn "failed to add answer file ($name) to ISO image, $FB"
else
installSetupScript "$script" "$src" || exit 84
wimlib-imagex update "$wim" "$idx" --command "add $answer /$dat" > /dev/null || true
fi
fi
# Manual mode removes generated automation and restores the original answer
# file when one was backed up earlier.
if enabled "$MANUAL"; then
removeGeneratedXML "$asset" || return 1
wimlib-imagex update "$wim" "$idx" --command "delete --force /$xml" > /dev/null || true
if wimlib-imagex extract "$wim" "$idx" "/$bak" "--dest-dir=$tmp" >/dev/null 2>&1; then
if ! wimlib-imagex update "$wim" "$idx" --command "add $tmp/$bak /$xml" > /dev/null; then
warn "failed to restore original answer file ($bak)."
fi
fi
fi
# Prevent a root-level answer file from overriding the selected automatic or
# manual behavior when Windows Setup first boots.
name="$xml"
enabled "$MANUAL" && name="$bak"
path=$(find "$dir" -maxdepth 1 -type f -iname "$name" -print -quit) || return 1
if [ -f "$path" ]; then
if ! enabled "$MANUAL"; then
if ! mv -f "$path" "${path%.*}.org"; then
error "Failed to rename answer file: $path"
return 1
fi
else
if ! mv -f "$path" "${path%.*}.xml"; then
error "Failed to rename answer file: $path"
return 1
fi
fi
fi
rm -rf "$tmp" || return 1
return 0
}
removeImage() { removeImage() {
local iso="$1" local iso="$1"
+1 -1
View File
@@ -405,7 +405,7 @@ markWindowsBooted() {
fi fi
if ! disabled "$REMOVE"; then if ! disabled "$REMOVE"; then
rm -f "$BOOT" 2>/dev/null || true rm -f "$BOOT" 2>/dev/null || :
fi fi
rm -f "$STORAGE/setup.img" 2>/dev/null || : rm -f "$STORAGE/setup.img" 2>/dev/null || :