fix: Do not fail if marker cannot be created (#2117)

This commit is contained in:
Kroese
2026-08-08 16:33:27 +02:00
committed by GitHub
parent 9e87e1aefa
commit 43be9024e9
5 changed files with 207 additions and 156 deletions
+81 -71
View File
@@ -133,16 +133,10 @@ hasAnswerFile() {
fi
# Editions without a dedicated template can use the generic template.
case "${id,,}" in
"win7"* | "win8"* | "win10"* | "win11"* | "winvista"* | "win20"* )
file="/run/assets/${id%%-*}.xml"
[ -s "$file" ] && return 0 ;;
[ -s "$file" ] || return 1
esac
return 1
return 0
}
addAnswerFile() {
@@ -444,22 +438,25 @@ generateFallbackXML() {
updateUserXML() {
local asset="$1"
local setup="$XML_COMPONENT_SETUP"
local specialize="$XML_COMPONENT_SHELL_SPECIALIZE"
local oobe="$XML_COMPONENT_SHELL_OOBE"
local app="$APP for $ENGINE"
local xpath
xpath="$XML_COMPONENT_SETUP/u:UserData/u:Organization"
xpath+=" | $XML_COMPONENT_SHELL_SPECIALIZE/u:OEMInformation/u:Model"
xpath+=" | $XML_COMPONENT_SHELL_SPECIALIZE/u:OEMName"
xpath+=" | $XML_COMPONENT_SHELL_SPECIALIZE/u:RegisteredOwner"
xpath+=" | $XML_COMPONENT_SHELL_OOBE/u:RegisteredOwner"
local -a args=(
-L
-N "$XML_NS_UNATTEND_ARG"
-u "$setup/u:UserData/u:Organization | $specialize/u:OEMInformation/u:Model | $specialize/u:OEMName | $specialize/u:RegisteredOwner | $oobe/u:RegisteredOwner" -v "$app"
-u "$oobe/u:Display/u:VerticalResolution" -v "$HEIGHT"
-u "$oobe/u:Display/u:HorizontalResolution" -v "$WIDTH"
-u "$xpath" -v "$app"
-u "$XML_COMPONENT_SHELL_OOBE/u:Display/u:VerticalResolution" -v "$HEIGHT"
-u "$XML_COMPONENT_SHELL_OOBE/u:Display/u:HorizontalResolution" -v "$WIDTH"
)
if [ -n "${HOST:-}" ]; then
args+=(-u "$specialize/u:ComputerName" -v "$HOST")
args+=(-u "$XML_COMPONENT_SHELL_SPECIALIZE/u:ComputerName" -v "$HOST")
fi
xmlstarlet ed "${args[@]}" "$asset" || return 1
@@ -537,23 +534,27 @@ updateWorkgroup() {
local asset="$1"
local workgroup="$2"
local component="$XML_COMPONENT_UNATTENDED_JOIN"
local identification="$component/u:Identification"
local join="$identification/*[local-name()='JoinWorkgroup']"
local arch tmp
local arch tmp xpath
arch=$(getXMLArchitecture "$asset") || return 1
# Apply all membership changes to a copy and publish it only after the old
# domain, credential, OU, and workgroup nodes have been replaced successfully.
tmp=$(copyXMLAsset "$asset") || return 1
xpath="$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:Credentials"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:JoinDomain"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:JoinWorkgroup"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:MachineObjectOU"
if ! ensureUnattendedJoin "$tmp" "$arch" ||
! xmlstarlet ed -L \
-N "$XML_NS_UNATTEND_ARG" \
-d "$identification/u:Credentials | $identification/u:JoinDomain | $identification/u:JoinWorkgroup | $identification/u:MachineObjectOU" \
-s "$identification" -t elem -n 'JoinWorkgroup' "$tmp" ||
! xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -u "$join" -v "$workgroup" "$tmp" ||
-d "$xpath" \
-s "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification" -t elem -n 'JoinWorkgroup' "$tmp" ||
! xmlstarlet ed -L \
-N "$XML_NS_UNATTEND_ARG" \
-u "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/*[local-name()='JoinWorkgroup']" -v "$workgroup" "$tmp" ||
! replaceXMLAsset "$asset" "$tmp"; then
rm -f "$tmp"
@@ -575,6 +576,7 @@ updateDomain() {
local arch tmp
arch=$(getXMLArchitecture "$asset") || return 1
# Account and join settings are separate XML transformations, so update a
# copy to keep the original answer file intact if either transformation fails.
tmp=$(copyXMLAsset "$asset") || return 1
@@ -602,7 +604,8 @@ configureDomainAccounts() {
local administrator="$accounts/u:AdministratorPassword"
local autologon="$shell/u:AutoLogon"
local domain_accounts="$accounts/u:DomainAccounts"
local counts shell_count accounts_count administrator_count autologon_count child_count
local counts shell_count administrator_count
local accounts_count autologon_count child_count
counts=$(xmlstarlet sel \
-N "$XML_NS_UNATTEND_ARG" \
@@ -694,15 +697,19 @@ configureDomainJoin() {
local arch="$6"
local cred_domain="$domain"
local component="$XML_COMPONENT_UNATTENDED_JOIN"
local identification="$component/u:Identification"
local credentials="$identification/*[local-name()='Credentials']"
local credentials="$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/*[local-name()='Credentials']"
local xpath
xpath="$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:Credentials"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:JoinDomain"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:JoinWorkgroup"
xpath+=" | $XML_COMPONENT_UNATTENDED_JOIN/u:Identification/u:MachineObjectOU"
local -a args=(
-L
-N "$XML_NS_UNATTEND_ARG"
-d "$identification/u:Credentials | $identification/u:JoinDomain | $identification/u:JoinWorkgroup | $identification/u:MachineObjectOU"
-s "$identification" -t elem -n 'Credentials'
-d "$xpath"
-s "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification" -t elem -n 'Credentials'
)
ensureUnattendedJoin "$asset" "$arch" || return 1
@@ -720,11 +727,11 @@ configureDomainJoin() {
args+=(
-s "$credentials" -t elem -n 'Username'
-s "$credentials" -t elem -n 'Password'
-s "$identification" -t elem -n 'JoinDomain'
-s "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification" -t elem -n 'JoinDomain'
)
if [ -n "$ou" ]; then
args+=(-s "$identification" -t elem -n 'MachineObjectOU')
args+=(-s "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification" -t elem -n 'MachineObjectOU')
fi
xmlstarlet ed "${args[@]}" "$asset" || return 1
@@ -734,7 +741,7 @@ configureDomainJoin() {
-N "$XML_NS_UNATTEND_ARG"
-u "$credentials/*[local-name()='Username']" -v "$auth"
-u "$credentials/*[local-name()='Password']" -v "$pass"
-u "$identification/*[local-name()='JoinDomain']" -v "$domain"
-u "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/*[local-name()='JoinDomain']" -v "$domain"
)
if [ -n "$cred_domain" ]; then
@@ -742,7 +749,7 @@ configureDomainJoin() {
fi
if [ -n "$ou" ]; then
values+=(-u "$identification/*[local-name()='MachineObjectOU']" -v "$ou")
values+=(-u "$XML_COMPONENT_UNATTENDED_JOIN/u:Identification/*[local-name()='MachineObjectOU']" -v "$ou")
fi
xmlstarlet ed "${values[@]}" "$asset" || return 1
@@ -761,10 +768,9 @@ findPrimaryLocalAccount() {
local auto_primary=0 auto_matches=0
local admin_primary=0 admin_matches=0
local selected=0 separator=$'\x1f'
local -a groups=()
local counts records auto_user selected_user position name group
local shell_count local_count found_admin found_autologon token
local -a groups=()
counts=$(xmlstarlet sel \
-N "$XML_NS_UNATTEND_ARG" \
@@ -869,6 +875,7 @@ updateLocalAccount() {
result=$(findPrimaryLocalAccount "$asset") || return 1
mapfile -t values <<< "$result"
(( ${#values[@]} == 4 )) || return 1
primary="${values[0]}"
current_user="${values[1]}"
admin_count="${values[2]}"
@@ -991,9 +998,9 @@ updateEditionXML() {
local asset="$1"
local setup="$XML_COMPONENT_SETUP"
local upper='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
local lower='abcdefghijklmnopqrstuvwxyz'
local setup="$XML_COMPONENT_SETUP"
local selector="$setup/u:ImageInstall/u:OSImage/u:InstallFrom/u:MetaData[translate(normalize-space(u:Key), '$lower', '$upper')='/IMAGE/NAME']/u:Value"
local edition count records position value replacement
local separator=$'\x1f'
@@ -1014,17 +1021,18 @@ updateEditionXML() {
-N "$XML_NS_UNATTEND_ARG" -T -t -m "$selector" -v 'position()' -o "$separator" -v 'string(.)' -n "$asset") || return 1
while IFS="$separator" read -r position value; do
[ -n "$position" ] || continue
# Only Windows Server templates use EDITION as a mutable answer-file
# selector. Products such as Hyper-V Server have fixed SERVER* flags that
# must not be rewritten.
# Only Windows Server templates use EDITION as a mutable answer-file selector.
# Products such as Hyper-V Server have fixed SERVER* flags that must not be rewritten.
[[ "${value,,}" == *"windows server"* ]] || continue
[[ "$value" =~ ^(.*[[:space:]])SERVER[A-Za-z0-9_-]+[[:space:]]*$ ]] || continue
replacement="${BASH_REMATCH[1]}SERVER$edition"
xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -u "($selector)[$position]" -v "$replacement" "$asset" || return 1
done <<< "$records"
return 0
@@ -1169,29 +1177,30 @@ setConfigurationXML() {
return 1
}
if [ "$config_count" = "0" ] &&
! ensureXMLDefaultNamespace "$tmp"; then
local msg="Failed to enable the Windows configuration set!"
if [ "$config_count" = "0" ] && ! ensureXMLDefaultNamespace "$tmp"; then
rm -f "$tmp"
error "$msg (1)"
return 1
fi
if [ "$config_count" = "1" ]; then
xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -u "$config" -v "true" "$tmp" || {
rm -f "$tmp"
error "Failed to enable the Windows configuration set!"
error "$msg (2)"
return 1
}
elif [ "$userdata_count" = "1" ]; then
xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -i "$userdata" -t elem -n "UseConfigurationSet" -v "true" "$tmp" || {
rm -f "$tmp"
error "Failed to enable the Windows configuration set!"
error "$msg (3)"
return 1
}
else
xmlstarlet ed -L -N "$XML_NS_UNATTEND_ARG" -s "$setup" -t elem -n "UseConfigurationSet" -v "true" "$tmp" || {
rm -f "$tmp"
error "Failed to enable the Windows configuration set!"
error "$msg (4)"
return 1
}
fi
@@ -1203,7 +1212,7 @@ setConfigurationXML() {
if [ "$result_count" != "1" ]; then
rm -f "$tmp"
error "Failed to enable the Windows configuration set!"
error "$msg (5)"
return 1
fi
@@ -1219,8 +1228,7 @@ removeSharedFolder() {
local script="$1"
if ! disabled "${SHORTCUT:-}" &&
! disabled "${SAMBA:-}"; then
if ! disabled "${SHORTCUT:-}" && ! disabled "${SAMBA:-}"; then
return 0
fi
@@ -1299,13 +1307,14 @@ removeEmbeddedProductKeys() {
validateXMLSettings() {
validateResolution "WIDTH" "$WIDTH" 320 || return 1
validateResolution "HEIGHT" "$HEIGHT" 200 || return 1
validateMembership || return 1
validateComputerName "${HOST:-}" || return 1
validateProductKey "${KEY:-}" || return 1
validatePassword "${PASSWORD:-}" || return 1
validateResolution "WIDTH" "$WIDTH" 320 || return 1
validateResolution "HEIGHT" "$HEIGHT" 200 || return 1
return 0
}
@@ -1828,15 +1837,13 @@ encodeUnattendPassword() {
# Windows unattend password fields use a field-specific suffix before
# UTF-16LE/Base64 encoding; this is obfuscation rather than encryption.
printf '%s' "${password}${suffix}" |
iconv -f utf-8 -t utf-16le |
base64 -w 0
printf '%s' "${password}${suffix}" | iconv -f utf-8 -t utf-16le | base64 -w 0
}
enableLog() {
local script="$1"
local content
enabled "${LOG:-}" || return 0
@@ -1887,7 +1894,6 @@ getXMLNodeCount() {
local xpath="$2"
xmlstarlet sel -N "$XML_NS_UNATTEND_ARG" -T -t -v "count($xpath)" "$asset"
}
validateUniqueXMLNodes() {
@@ -2001,8 +2007,11 @@ updateSetupScript() {
fi
if [ -n "$domain" ]; then
removeSetupBlock "$script" "LOCAL_ACCOUNT" || return 1
elif [ -n "$user" ]; then
validateUsername "$user" "local" || return 1
id=$(basename "$asset") || return 1
@@ -2026,6 +2035,7 @@ updateSetupScript() {
esac
replaceSetupBlock "$script" "LOCAL_ACCOUNT" "$content" || return 1
fi
enableLog "$script" || return 1
@@ -2047,9 +2057,9 @@ findSetupScript() {
dir=$(dirname "$asset") || return 1
name=$(basename "$asset") || return 1
id="${name%.*}"
normal="$id"
candidates+=("$dir/$id.cmd")
if [[ "${normal,,}" == *"-eval" ]]; then
@@ -2057,19 +2067,19 @@ findSetupScript() {
candidates+=("$dir/$normal.cmd")
fi
# Generated edition-specific answer files inherit the script belonging to
# their generic source template.
case "${normal,,}" in
"win7"* | "win8"* | "win10"* | "win11"* | "winvista"* | "win20"* )
# Generated edition-specific answer files inherit the
# script belonging to their generic source template.
if [[ "$normal" == *-* ]]; then
candidates+=("$dir/${normal%%-*}.cmd")
;;
esac
fi
for candidate in "${candidates[@]}"; do
if [ -f "$candidate" ] && [ -s "$candidate" ]; then
printf '%s' "$candidate"
return 0
fi
done
error "Failed to find setup script for answer file: $asset"
@@ -2082,7 +2092,6 @@ stageSetupScript() {
local stage="$2"
local source target
source=$(findSetupScript "$asset") || return 1
[ -n "$source" ] || return 0
@@ -2139,6 +2148,7 @@ rewriteSetupBlock() {
while IFS= read -r line || [ -n "$line" ]; do
if [ "$line" = "$begin" ]; then
if [ "$action" = "replace" ]; then
if ! printf '%s\n' "$line" >> "$tmp" ||
! printf '%s\n' "$content" >> "$tmp"; then
@@ -2152,6 +2162,7 @@ rewriteSetupBlock() {
fi
if [ "$line" = "$end" ]; then
inside=0
if [ "$action" = "replace" ]; then
@@ -2165,10 +2176,12 @@ rewriteSetupBlock() {
fi
if (( ! inside )); then
if ! printf '%s\n' "$line" >> "$tmp"; then
rm -f "$tmp"
return 1
fi
fi
done < "$file"
@@ -2225,7 +2238,6 @@ escapeSIFValue() {
escapeRegistryValue() {
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
}
extractDrivers() {
@@ -2382,15 +2394,14 @@ addLegacyDrivers() {
local arch="$4"
local drivers="$5"
local file
local msg="Adding drivers to image..."
info "$msg" && html "$msg"
extractDrivers "$drivers" || return 1
copyStorageDriver "$dir" "$target" "$driver" "$arch" "$drivers" || return 1
addNetworkDriver "$dir" "$driver" "$arch" "$drivers" || return 1
local file
file=$(find "$target" -maxdepth 1 -type f -iname TXTSETUP.SIF -print -quit) || return 1
if [ -z "$file" ]; then
@@ -2414,7 +2425,6 @@ setLegacyKey() {
local desc="$4"
local setup pid key file
setup=$(find "$target" -maxdepth 1 -type f -iname setupp.ini -print -quit) || return 1
[[ -n "$setup" ]] || return 0
@@ -2440,7 +2450,9 @@ setLegacyKey() {
# Prefer a staging or OEM key already shipped on the media before falling
# back to Microsoft's documented generic installation keys.
if [[ "$driver" == "2k3" ]]; then
key=$(grep -i -A 2 "StagingKey" "$file" | tail -n 2 | head -n 1) || key=""
else
key="${pid: -8:5}"
@@ -2473,8 +2485,7 @@ setLegacyKey() {
else
# Windows XP Professional x64 generic trial key (no activation)
KEY="B2RBK-7KPT9-4JP6X-QQFWM-PJD6G"
fi
;;
fi ;;
"2k3" )
@@ -2484,8 +2495,7 @@ setLegacyKey() {
else
# Windows Server 2003 Standard x64 generic trial key (no activation)
KEY="P4WJG-WK3W7-3HM8W-RWHCK-8JTRY"
fi
;;
fi ;;
esac
+52 -32
View File
@@ -195,6 +195,7 @@ selectVersion() {
IFS=$'\t' read -r id index <<< "$record"
[[ "${id,,}" == "${candidate,,}" ]] || continue
hasAnswerFile "$id" || continue
printf '%s\n%s\n' "$id" "$index"
@@ -278,6 +279,7 @@ detectVersion() {
for record in "${images[@]}"; do
IFS=$'\t' read -r id index <<< "$record"
hasAnswerFile "$id" || continue
rank=$(getEditionRank "$id")
@@ -327,7 +329,6 @@ detectLanguage() {
for path in "${paths[@]}"; do
lang=$(xmlstarlet sel -T -t -v "normalize-space(string(($path)[1]))" - 2>/dev/null <<< "$xml") || lang=""
[ -n "$lang" ] && break
done
@@ -502,8 +503,6 @@ createImageDirectory() {
local image="$1"
local directory="$2"
# Treat an existing directory as success; create it only when mdir cannot
# already resolve it.
if mdir -i "$image" "$directory" >/dev/null 2>&1; then
return 0
fi
@@ -560,20 +559,25 @@ createSetupImage() {
fi
for entry in "${entries[@]}"; do
if ! mcopy -Q -s -i "$tmp" "$entry" ::; then
rm -f -- "$tmp"
error "Failed to copy image file: $entry"
return 1
fi
done
if [ -n "$folder" ] || [ -f "$install" ]; then
if ! createImageDirectory "$tmp" "::/\$OEM\$" || ! createImageDirectory "$tmp" "::/\$OEM\$/\$1" ||
! createImageDirectory "$tmp" "$target"; then
rm -f -- "$tmp"
error "Failed to create OEM directory in setup image!"
return 1
fi
fi
if [ -n "$folder" ]; then
@@ -592,11 +596,13 @@ createSetupImage() {
fi
for entry in "${entries[@]}"; do
if ! mcopy -Q -s -o -i "$tmp" "$entry" "$target"; then
rm -f -- "$tmp"
error "Failed to copy OEM file: $entry"
return 1
fi
done
fi
@@ -604,11 +610,13 @@ createSetupImage() {
# Copy the generated overlay script last so it replaces an install.bat from
# the mounted OEM folder when both are present.
if [ -f "$install" ]; then
if ! mcopy -Q -o -i "$tmp" "$install" "$target/install.bat"; then
rm -f -- "$tmp"
error "Failed to replace install.bat in setup image!"
return 1
fi
fi
# Verify that mtools can read the completed filesystem before publishing it.
@@ -773,7 +781,6 @@ detectLegacy() {
detectReactOS() {
local dir="$1"
local marker
marker=$(find "$dir" -maxdepth 2 -type f \
@@ -885,8 +892,8 @@ readIsoImageInfo() {
local image="$2"
local header="$3"
local raw result root xml_count rc
local header_size version
local raw result xml_count rc
local root header_size version
local part_number total_parts image_count
local xml_offset xml_size xml_original xml_flags
local -a bytes=() values=()
@@ -1172,8 +1179,10 @@ findImage() {
fi
for name in install.wim install.esd; do
result=$(find "$sources" -maxdepth 1 -type f -iname "$name" -print -quit) || return 2
[ -n "$result" ] && break
done
if [ ! -f "$result" ]; then
@@ -1222,13 +1231,14 @@ validateEdition() {
# Discard a stale server-edition override when it conflicts with the image
# that was actually detected.
EDITION=""
return 0
}
unknownImage() {
local msg="Failed to determine Windows version from image"
local rc=0
local msg="Failed to determine Windows version from image"
setXML "" || rc=$?
@@ -1252,9 +1262,12 @@ describeImage() {
result=$(printEdition "$DETECTED" "$DETECTED" "Y") || return 1
if [[ "${LANGUAGE,,}" != "en" && "${LANGUAGE,,}" != "en-"* ]]; then
local language
language=$(getLanguage "$LANGUAGE" "desc") || return 1
result+=" ($language)"
fi
printf '%s' "$result"
@@ -1373,7 +1386,6 @@ detectImageInfo() {
detectIsoImage() {
local iso="$1"
local image header image_info rc
# Return 1 only when no directly inspectable WIM/ESD payload is available so
@@ -1405,19 +1417,39 @@ detectIsoImage() {
return 0
}
baseDir() {
local path="${1%/}"
[[ -z "$path" || "$path" == "/" ]] && {
echo "/"
return 0
}
path="${path#/}"
path="${path%%/*}"
echo "/$path"
return 0
}
checkFreeSpace() {
local dir="$1"
local size="$2"
local space size_gb space_gb
local base space size_gb space_gb
base=$(baseDir "$dir")
space=$(df --output=avail -B 1 "$dir" | tail -n 1) || return 1
[[ "$space" =~ ^[[:space:]]*[0-9]+[[:space:]]*$ ]] || {
error "Failed to determine available disk space for $dir!"
if ! space=$(df --output=avail -B 1 "$dir" | tail -n 1); then
error "Failed to check free space in $dir."
return 1
}
fi
if [[ ! "$space" =~ ^[[:space:]]*[0-9]+[[:space:]]*$ ]]; then
error "Failed to determine available disk space for $dir."
return 1
fi
space="${space//[[:space:]]/}"
@@ -1426,8 +1458,9 @@ checkFreeSpace() {
size_gb=$(formatBytes "$size")
space_gb=$(formatBytes "$space")
error "Not enough free space in $STORAGE, have $space_gb available but need at least $size_gb."
error "Not enough free space in $base, have $space_gb available but need at least $size_gb."
return 1
fi
return 0
@@ -1462,7 +1495,7 @@ extractESD() {
fi
if (( size < minSize )); then
error "The downloaded ISO file is too small!"
error "The downloaded ESD file is too small!"
return 1
fi
@@ -1514,8 +1547,7 @@ extractESD() {
bootTotal="${fields[1]:-}"
bootLinks="${fields[2]:-}"
if [[ ! "$bootTotal" =~ ^[0-9]+$ ]] ||
[[ ! "$bootLinks" =~ ^[0-9]+$ ]]; then
if [[ ! "$bootTotal" =~ ^[0-9]+$ ]] || [[ ! "$bootLinks" =~ ^[0-9]+$ ]]; then
error "Cannot read bootdisk size from ESD file!"
return 1
fi
@@ -1537,6 +1569,7 @@ extractESD() {
# installation media. Peak additional usage consists of the extracted setup
# files and boot.wim, plus the final ISO containing those files and the ESD.
local freeSpace=$(( size + 2 * (bootSize + wimSize) + spacePad ))
checkFreeSpace "$dir" "$freeSpace" || return
/run/progress.sh "$dir" "$bootSize" "$msg ([P])..." &
@@ -1585,6 +1618,7 @@ extractESD() {
}
fKill "progress.sh"
html "$msg..."
if [[ "${PLATFORM,,}" == "x64" ]]; then
LABEL="CCCOMA_X64FRE_EN-US_DV9"
@@ -1592,9 +1626,6 @@ extractESD() {
LABEL="CPBA_A64FRE_EN-US_DV9"
fi
msg="Extracting image from ESD file"
info "$msg..." && html "$msg..."
index=""
if [[ "${version,,}" == "http"* ]]; then
@@ -1649,10 +1680,8 @@ extractESD() {
fi
installSize=$(( size + installPad ))
/run/progress.sh "$installWim" "$installSize" "$msg ([P])..." &
if ! rm -f -- "$dir/sources/install.wim" "$installWim"; then
fKill "progress.sh"
error "Failed to remove previous Windows installation image!"
return 1
fi
@@ -1660,7 +1689,6 @@ extractESD() {
# Reuse the downloaded solid ESD instead of exporting the selected image.
# Both paths are below $TMP, so this is a same-filesystem rename.
if ! mv -f -- "$iso" "$installWim"; then
fKill "progress.sh"
error "Failed to move downloaded ESD file into the installation media!"
return 1
fi
@@ -1673,7 +1701,6 @@ extractESD() {
wimlib-imagex delete "$installWim" "$image" --soft --quiet || {
ret=$?
fKill "progress.sh"
error "Failed to remove image $image from install.esd!"
return "$ret"
}
@@ -1683,7 +1710,6 @@ extractESD() {
result=$(wimlib-imagex info "$installWim" --xml 2>/dev/null |
iconv -f UTF-16LE -t UTF-8 2>/dev/null) || {
ret=$?
fKill "progress.sh"
error "Cannot verify the prepared install.esd file!"
return "$ret"
}
@@ -1693,7 +1719,6 @@ extractESD() {
-v 'count(/WIM/IMAGE[@INDEX="1"])' -n \
-v 'normalize-space(/WIM/IMAGE[@INDEX="1"]/DESCRIPTION)' -n \
<<< "$result" 2>/dev/null); then
fKill "progress.sh"
error "Cannot verify the prepared install.esd file!"
return 1
fi
@@ -1705,26 +1730,22 @@ extractESD() {
resultEdition="${fields[2]:-}"
if [[ "$resultCount" != "1" ]] || [[ "$resultIndex" != "1" ]]; then
fKill "progress.sh"
error "Prepared install.esd does not contain exactly one image at index 1!"
return 1
fi
if [[ "${version,,}" != "http"* ]] &&
[[ "${resultEdition,,}" != "${edition,,}" ]]; then
fKill "progress.sh"
error "Prepared install.esd does not contain only '$edition' at index 1!"
return 1
fi
fKill "progress.sh"
return 0
}
normalizeBatch() {
local file="$1"
local bom tmp encoding
[ ! -s "$file" ] && return 0
@@ -1768,7 +1789,6 @@ reportBatchMatches() {
local suggestion="$5"
local matches line
matches=$(grep -Pin "$pattern" "$file" || true)
[ -n "$matches" ] || return 0
+49 -32
View File
@@ -43,16 +43,12 @@ selectWindowsImage() {
local dir="$2"
local boot="$3"
local rc
XML=""
FB="falling back to manual installation!"
# Known catalog versions already provide the required image metadata.
if [ -z "$DETECTED" ] && [ -z "$CUSTOM" ] && [[ "${VERSION,,}" != "http"* ]]; then
DETECTED="$VERSION"
fi
DETECTED="${DETECTED/-enterprise-iot/-iot}"
DETECTED="${DETECTED/-enterprise-ltsc/-ltsc}"
normalizeDetected || return 70
if [ -n "$DETECTED" ]; then
@@ -74,12 +70,16 @@ selectWindowsImage() {
fi
# Inspect unknown media directly before falling back to extraction.
# Inspect unknown bootable media directly before falling back to extraction.
if [[ "${iso,,}" != *.esd ]]; then
detectIsoImage "$iso" && return 0
local rc=$?
rc=$?
(( rc == 1 )) || return 76
fi
if ! extractImage "$iso" "$dir" "$VERSION"; then
removeImage "$iso" || :
return 74
@@ -105,10 +105,9 @@ configureMachine() {
local boot="$3"
local desc
desc=$(printVariant "$DETECTED" "$DETECTED") || return 78
if ! checkMemory "$DETECTED" "$desc"; then
if ! checkMemory "$DETECTED"; then
if [ -z "$CUSTOM" ]; then
useOriginalImage "$iso" || return 79
fi
@@ -146,7 +145,7 @@ prepareWindowsImage() {
fi
if ! createSetupImage "$TMP/setup" "$STORAGE/setup.img"; then
exit 86
return 86
fi
# Bootable ISOs can be reused unchanged with the generated setup image.
@@ -385,7 +384,7 @@ skipInstall() {
local iso="$1"
local previousBase="$2"
local boot="$STORAGE/windows.boot"
local marker="$STORAGE/windows.boot"
if [ -n "$previousBase" ]; then
@@ -409,7 +408,7 @@ skipInstall() {
# Older releases may have left a rebuilt custom ISO at its synthetic source
# identity. A completed installation no longer needs that installation media.
if [[ "${previousBase,,}" == "windows."* ]] && [ -f "$boot" ] && hasData; then
if [[ "${previousBase,,}" == "windows."* ]] && hasData && [ -f "$marker" ]; then
if ! rm -f -- "$STORAGE/$previousBase"; then
error "Failed to remove obsolete ISO file \"$STORAGE/$previousBase\" !"
exit 50
@@ -441,7 +440,7 @@ skipInstall() {
else
method="your custom .iso file was removed"
if [ -f "$boot" ] && hasData; then
if hasData && [ -f "$marker" ]; then
info "Detected that $method, will be ignored."
return 0
fi
@@ -449,7 +448,7 @@ skipInstall() {
fi
fi
if enabled "$SHUTDOWN" && [ ! -f "$boot" ]; then
if enabled "$SHUTDOWN" && [ ! -f "$marker" ]; then
discardPrevious "$STORAGE/$previousBase" || exit 50
return 1
fi
@@ -465,7 +464,7 @@ skipInstall() {
fi
fi
[ -f "$boot" ] && hasData && return 0
hasData && [ -f "$marker" ] && return 0
return 1
}
@@ -476,7 +475,7 @@ finishInstall() {
local aborted="$2"
local boot="$3"
local base
local base secure=0
if [ ! -s "$iso" ] || [ ! -f "$iso" ]; then
error "Failed to find ISO file: $iso" && return 1
@@ -509,11 +508,9 @@ finishInstall() {
else
local secure=0
# Aborted Win11 installs boot without any answer file present,
# so enable Secure Boot and TPM to satisfy its hardware checks.
if [[ "$aborted" == [Yy1]* ]] || enabled "$MANUAL"; then
if enabled "$aborted" || enabled "$MANUAL"; then
[[ "${DETECTED,,}" == "win11"* ]] && secure=1
fi
@@ -578,6 +575,7 @@ findFile() {
ISO="$file"
CUSTOM="$file"
# Encode the custom ISO size in a synthetic source identity so replacing a
# bind-mounted ISO is detected as a different installation source.
BOOT="$STORAGE/windows.$size.iso"
@@ -585,6 +583,19 @@ findFile() {
return 0
}
normalizeDetected() {
# Known catalog versions already provide the required image metadata.
if [ -z "$DETECTED" ] && [ -z "$CUSTOM" ] && [[ "${VERSION,,}" != "http"* ]]; then
DETECTED="$VERSION"
fi
DETECTED="${DETECTED/-enterprise-iot/-iot}"
DETECTED="${DETECTED/-enterprise-ltsc/-ltsc}"
return 0
}
detectCustom() {
CUSTOM=""
@@ -629,10 +640,10 @@ removeImage() {
setImage() {
supportsXML "${DETECTED,,}" || return 0
local rc=0
supportsXML "${DETECTED,,}" || return 0
setXML "" || rc=$?
if (( rc == 0 )); then
@@ -688,8 +699,7 @@ getArchiveSize() {
local result_name="$2"
local -n result="$result_name"
local listing line value
local found=0 rc
local found=0 listing line value rc
result=0
@@ -728,8 +738,7 @@ extractImage() {
local target="$dir"
local desc="local ISO"
local archive="${dir}.archive"
local file size required archiveSize
local rc
local file size required archiveSize rc
if [ -z "$CUSTOM" ]; then
desc="downloaded ISO"
@@ -850,25 +859,30 @@ extractImage() {
detectImage() {
local dir="$1"
local desc rc
info "Detecting version from ISO image..."
# Marker-based legacy and ReactOS detection must run before looking for a WIM.
# Marker-based legacy detection must run before looking for a WIM.
if detectLegacy "$dir"; then
desc=$(printEdition "$DETECTED" "$DETECTED" "Y") || return 2
info "Detected: $desc"
return 0
else
rc=$?
(( rc == 1 )) || return "$rc"
fi
if detectReactOS "$dir"; then
desc=$(printEdition "$DETECTED" "$DETECTED" "Y") || return 2
info "Detected: $desc"
return 0
else
rc=$?
(( rc == 1 )) || return "$rc"
@@ -962,9 +976,11 @@ addFolder() {
rm -f -- "$install" || return 1
if [ -n "$folder" ]; then
source=$(find -L "$folder" -maxdepth 1 -type f -iname install.bat -print -quit) || return 1
if [ -n "$source" ]; then
if ! cp -L -- "$source" "$install"; then
error "Failed to create a writable copy of $source!"
return 1
@@ -972,6 +988,7 @@ addFolder() {
file="$install"
fi
fi
else
@@ -1338,10 +1355,10 @@ restoreBootMode() {
restoreMachine() {
# Restore the saved machine only when q35 is still the default; an explicit
# user-selected machine must remain untouched.
[[ "${PLATFORM,,}" != "x64" ]] && return 0
# Restore the saved machine only when q35 is still the default;
# an explicit user-selected machine must remain untouched.
[[ "${MACHINE,,}" != "q35" ]] && return 0
[[ "${PLATFORM,,}" != "x64" ]] && return 0
MACHINE=""
restoreState "MACHINE" "old" || return 1
+20 -16
View File
@@ -7,8 +7,6 @@ handleCurlError() {
local server="$2"
local reason="${3:-}"
local signal
if [ -n "$reason" ] && (( code <= 125 )); then
error "Request to $server servers failed: ${reason%.}."
return 1
@@ -23,6 +21,7 @@ handleCurlError() {
return 1
fi
local signal
signal=$(kill -l "$((code - 128))" 2>/dev/null || true)
case "$signal" in
@@ -72,10 +71,7 @@ curlRequest() {
rm -f "$log"
handleCurlError "$rc" "$server" "$reason" || :
if (( rc >= 129 )); then
return "$rc"
fi
(( rc >= 129 )) && return "$rc"
return 1
fi
@@ -579,11 +575,13 @@ getWindows() {
"win81${PLATFORM,,}"* | \
"win10${PLATFORM,,}-enterprise-ltsc-eval" | \
"win11${PLATFORM,,}-enterprise-iot-eval" )
if [[ "${lang,,}" != "en" && "${lang,,}" != "en-"* ]]; then
error "No download in the $language language available for $edition!"
MIDO_URL=""
return 1
fi ;;
esac
# ARM64 downloads exist only for the explicitly supported Windows 11
@@ -593,24 +591,28 @@ getWindows() {
"win11${PLATFORM,,}" ) ;;
"win11${PLATFORM,,}-enterprise"* ) ;;
* )
if [[ "${PLATFORM,,}" != "x64" ]]; then
error "No download for the ${PLATFORM^^} platform available for $edition!"
MIDO_URL=""
return 1
fi ;;
esac
# Prefer live Microsoft download routes. Unsupported or failed live routes
# fall through to the configured static catalog below.
case "${version,,}" in
"win10x64" | "win11${PLATFORM,,}" )
if downloadWindows "$version" "$lang" "$edition"; then
return 0
else
rc=$?
(( rc == 1 )) || return "$rc"
fi ;;
fi
(( rc == 1 )) || return "$rc" ;;
"win11${PLATFORM,,}-enterprise"* )
@@ -618,8 +620,9 @@ getWindows() {
return 0
else
rc=$?
(( rc == 1 )) || return "$rc"
fi ;;
fi
(( rc == 1 )) || return "$rc" ;;
"win2025-eval" | "win2022-eval" | "win2019-eval" | \
"win2019-hv" | "win2016-eval" | "win2012r2-eval" )
@@ -628,14 +631,16 @@ getWindows() {
return 0
else
rc=$?
(( rc == 1 )) || return "$rc"
fi ;;
fi
(( rc == 1 )) || return "$rc" ;;
"win2008r2"*| "win81${PLATFORM,,}"* | "win10${PLATFORM,,}-enterprise"* ) ;;
* )
error "Invalid VERSION specified, value \"$version\" is not recognized!"
return 1 ;;
esac
# Static catalog URLs are the last resort after live Microsoft methods are
@@ -995,10 +1000,7 @@ getESD() {
rm -f "$log"
if (( rc >= 129 )); then
return "$rc"
fi
(( rc >= 129 )) && return "$rc"
return 1
fi
@@ -1209,6 +1211,7 @@ tryDownload() {
if ! rm -f -- "$iso" "$iso.aria2"; then
warn "failed to remove invalid download \"$iso\"!"
fi
return 2
}
@@ -1278,6 +1281,7 @@ downloadImage() {
web_desc="$desc"
tryDownload "$iso" "$version" "" "" "$desc" "$seconds" "$web_desc" || return
return 0
fi
+1 -1
View File
@@ -429,7 +429,7 @@ finish() {
failed=1
fi
touch "$QEMU_END"
touch "$QEMU_END" || :
forceKillQemu "$reason"