feat: Improve error handling (#2120)

This commit is contained in:
Kroese
2026-08-09 02:46:04 +02:00
committed by GitHub
parent 33ae12677e
commit 19264a42da
5 changed files with 424 additions and 145 deletions
+103 -35
View File
@@ -30,10 +30,21 @@ updateXML() {
[ -z "${HEIGHT:-}" ] && HEIGHT="720"
validateXMLSettings || return 1
ensureXMLDefaultNamespace "$asset" || return 1
updateUserXML "$asset" || return 1
updateLocaleXML "$asset" "$language" || return 1
if ! ensureXMLDefaultNamespace "$asset"; then
error "Failed to prepare the answer file XML namespace!"
return 1
fi
if ! updateUserXML "$asset"; then
error "Failed to update user and display settings in answer file!"
return 1
fi
if ! updateLocaleXML "$asset" "$language"; then
error "Failed to update regional settings in answer file!"
return 1
fi
if [ -n "$domain" ]; then
@@ -46,13 +57,24 @@ updateXML() {
else
updateLocalAccount "$asset" || return 1
if ! updateLocalAccount "$asset"; then
error "Failed to update local account settings in answer file!"
return 1
fi
fi
updateMembership "$asset" "$domain" "$workgroup" "$account" "$auth" || return 1
updateAutologinXML "$asset" || return 1
updateEditionXML "$asset" || return 1
if ! updateAutologinXML "$asset"; then
error "Failed to update automatic logon settings in answer file!"
return 1
fi
if ! updateEditionXML "$asset"; then
error "Failed to update edition settings in answer file!"
return 1
fi
validateGeneratedXML "$asset" || return 1
@@ -73,7 +95,7 @@ setXML() {
if [ -d "${custom_files[0]}" ]; then
error "The bind ${custom_files[0]} maps to a file that does not exist!"
exit 67
return 2
fi
# A custom answer file always takes precedence over bundled or generated
@@ -182,7 +204,7 @@ addAnswerFile() {
if ! updateDiskID "$answer" "${DISK_TYPE:-}"; then
error "Failed to adjust the Windows installation disk!"
exit 85
return 1
fi
if ! setConfigurationXML "$answer"; then
@@ -194,7 +216,10 @@ addAnswerFile() {
if [ -z "${CUSTOM_XML:-}" ]; then
prepareSetupScript "$asset" "$stage" || exit 84
if ! prepareSetupScript "$asset" "$stage"; then
error "Failed to prepare the Windows setup script!"
return 1
fi
fi
@@ -1080,7 +1105,13 @@ updateDiskID() {
done <<< "$values"
mapfile -t ids < <(printf '%s\n' "${ids[@]}" | sort -u)
local unique
unique=$(printf '%s\n' "${ids[@]}" | sort -u) || {
error "Failed to normalize DiskID values from answer file: $asset"
return 1
}
mapfile -t ids <<< "$unique"
# Leave explicit multi-disk configurations untouched.
(( ${#ids[@]} == 1 )) || return 0
@@ -1946,10 +1977,16 @@ markGeneratedXML() {
local file="$1"
local marker='<!-- generated-answer-file: do not reuse as a template -->'
local first
[ -s "$file" ] || return 1
if head -n 1 "$file" | grep -q '^<?xml'; then
if ! first=$(head -n 1 "$file"); then
error "Failed to inspect generated answer file: $file"
return 1
fi
if [[ "$first" == "<?xml"* ]]; then
sed -i "1a$marker" "$file" || return 1
else
sed -i "1i$marker" "$file" || return 1
@@ -1961,12 +1998,17 @@ markGeneratedXML() {
removeGeneratedXML() {
local file="$1"
local header
[ -n "$file" ] || return 0
[ -f "$file" ] || return 0
head -n 5 "$file" |
grep -Fqi 'generated-answer-file' || return 0
if ! header=$(head -n 5 "$file"); then
error "Failed to inspect answer file: $file"
return 1
fi
grep -Fqi 'generated-answer-file' <<< "$header" || return 0
if ! rm -f "$file"; then
error "Failed to remove generated answer file: $file"
@@ -2323,10 +2365,18 @@ addSIFEntry() {
return 1
fi
if grep -Fqx "$entry" "$file" || grep -Fqx "$entry"$'\r' "$file"; then
local rc=0
grep -Fqx -e "$entry" -e "$entry"$'\r' "$file" || rc=$?
if (( rc == 0 )); then
return 0
fi
if (( rc != 1 )); then
error "Failed to inspect section \"$header\" in \"$file\" !"
return 1
fi
if [[ "$ending" == "crlf" ]]; then
printf '%s\r\n' "$entry"
else
@@ -2412,7 +2462,9 @@ addLegacyDrivers() {
patchStorageDriver "$file" "$arch" || return 1
addSataDriver "$dir" "$target" "$arch" "$drivers" "$file" || return 1
rm -rf "$drivers" || return 1
if ! rm -rf "$drivers"; then
warn "failed to clean temporary driver files!"
fi
return 0
}
@@ -2424,7 +2476,7 @@ setLegacyKey() {
local arch="$3"
local desc="$4"
local setup pid key file
local setup pid file block
setup=$(find "$target" -maxdepth 1 -type f -iname setupp.ini -print -quit) || return 1
[[ -n "$setup" ]] || return 0
@@ -2434,7 +2486,8 @@ setLegacyKey() {
pid="${pid%$'\r'}"
if [[ "$driver" == "2k" ]]; then
echo "${pid::-3}270" > "$setup" || return 1
[ "${#pid}" -ge 3 ] || return 0
echo "${pid::-3}270" > "$setup" || :
return 0
fi
@@ -2447,28 +2500,44 @@ setLegacyKey() {
if [[ -n "$file" ]]; then
local key=""
# 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=""
block=$(grep -i -A 2 "StagingKey" "$file") || block=""
if [ -n "$block" ]; then
key=$(printf '%s\n' "$block" | tail -n 2 | head -n 1) || key=""
fi
else
key="${pid: -8:5}"
if [[ "${pid^^}" == *"OEM" ]]; then
key=$(grep -i -A 2 "$key" "$file" | tail -n 2 | head -n 1) || key=""
block=$(grep -i -A 2 "$key" "$file") || block=""
else
key=$(grep -i -m 1 -A 2 "$key" "$file" | tail -n 2 | head -n 1) || key=""
block=$(grep -i -m 1 -A 2 "$key" "$file") || block=""
fi
if [ -n "$block" ]; then
key=$(printf '%s\n' "$block" | tail -n 2 | head -n 1) || key=""
fi
key="${key#*= }"
fi
if [ -n "$key" ]; then
key="${key%$'\r'}"
[[ "${#key}" == "29" ]] && KEY="$key"
fi
fi
@@ -2499,7 +2568,9 @@ setLegacyKey() {
esac
echo "${pid::-3}000" > "$setup" || return 1
if [ "${#pid}" -ge 3 ]; then
echo "${pid::-3}000" > "$setup" || :
fi
return 0
}
@@ -2768,12 +2839,10 @@ disableAutoReboot() {
local target="$1"
local file
local file rc=0
local pattern='^[[:space:]]*HKLM[[:space:]]*,[[:space:]]*"SYSTEM\\CurrentControlSet\\Control\\CrashControl"[[:space:]]*,[[:space:]]*"AutoReboot"[[:space:]]*,[[:space:]]*[^,]*,'
file=$(find \
"$target" -maxdepth 1 -type f -iname HIVESYS.INF -print -quit
) || return 1
file=$(find "$target" -maxdepth 1 -type f -iname HIVESYS.INF -print -quit) || return 1
if [ -z "$file" ]; then
error "The file HIVESYS.INF could not be found!"
@@ -2782,19 +2851,18 @@ disableAutoReboot() {
# Keep setup crashes visible instead of immediately rebooting into an
# opaque installation loop.
if grep -Eqi "${pattern}[[:space:]]*[^,;[:space:]]+" "$file"; then
sed -i -E \
"s|(${pattern})[[:space:]]*[^,;[:space:]]+|\\1 0|I" \
"$file" || return 1
else
grep -Eqi "${pattern}[[:space:]]*[^,;[:space:]]+" "$file" || rc=$?
case "$rc" in
0 )
sed -i -E "s|(${pattern})[[:space:]]*[^,;[:space:]]+|\\1 0|I" "$file" || :
;;
1 )
printf '%s\n' \
'HKLM,"SYSTEM\CurrentControlSet\Control\CrashControl","AutoReboot",0x00010001,0' |
unix2dos >> "$file" || return 1
fi
unix2dos >> "$file" || :
;;
esac
return 0
}
+41 -23
View File
@@ -647,23 +647,28 @@ getVersion() {
local evaluation=""
id=$(fromName "$name" "$arch")
[ -z "$id" ] && return 0
[[ "${name,,}" == *"evaluation"* ]] && evaluation="-eval"
case "${id,,}" in
"winvista"* | "win7"* | "win8"* | "win10"* | "win11"* )
if edition=$(getEditionID "$name" "$id"); then
[ -n "$edition" ] && id+="-$edition"
[ -n "$evaluation" ] && id+="$evaluation"
fi
;;
"win20"* )
if [[ "${name,,}" == *"hyper-v server"* ]]; then
id+="-hv"
elif edition=$(getServerEditionID "$name" "$id"); then
[ -n "$edition" ] && id+="-$edition"
[ -n "$evaluation" ] && id+="$evaluation"
fi
;;
fi ;;
* )
if edition=$(getEditionID "$name" "$id"); then
[ -n "$edition" ] && id+="-$edition"
[ -n "$evaluation" ] && id+="$evaluation"
fi ;;
esac
echo "$id"
@@ -695,6 +700,7 @@ getEditionRank() {
case "$base" in
"win20"* )
case "$edition" in
"" ) echo 0 ;;
"datacenter-azure-core" | "datacenter-azure-core-"* | \
@@ -709,9 +715,10 @@ getEditionRank() {
"essentials" | "essentials-"* ) echo 5 ;;
"hv" | "hv-"* ) echo 10 ;;
* ) echo 99 ;;
esac
;;
esac ;;
* )
case "$edition" in
"enterprise-iot" | "enterprise-iot-"* | "iot" | "iot-"* ) echo 3 ;;
"enterprise-ltsc" | "enterprise-ltsc-"* | "ltsc" | "ltsc-"* ) echo 4 ;;
@@ -723,8 +730,8 @@ getEditionRank() {
"home" | "home-"* ) echo 6 ;;
"starter" | "starter-"* ) echo 7 ;;
* ) echo 99 ;;
esac
;;
esac ;;
esac
return 0
@@ -735,6 +742,7 @@ getEditionPolicy() {
local base="${1,,}"
case "$base" in
"win20"* )
printf '%s\n' \
"normalizeServerEditionID" \
@@ -750,9 +758,10 @@ getEditionPolicy() {
"-datacenter-azure-core" \
"-enterprise-core" \
"-web-core" \
"-hv"
;;
"-hv" ;;
* )
printf '%s\n' \
"normalizeEditionID" \
"-enterprise" \
@@ -764,8 +773,8 @@ getEditionPolicy() {
"-home" \
"-home-premium" \
"-home-basic" \
"-starter"
;;
"-starter" ;;
esac
return 0
@@ -774,18 +783,20 @@ getEditionPolicy() {
normalizeEdition() {
local source="${1,,}"
local edition
local edition transliterated
source="${source//evaluation/}"
source=$(printf '%s' "$source" |
uconv -x 'Any-Latin; Latin-ASCII' 2>/dev/null) || return 1
if transliterated=$(printf '%s' "$source" |
uconv -x 'Any-Latin; Latin-ASCII' 2>/dev/null); then
source="$transliterated"
fi
edition=$(sed -E \
-e 's/[^a-z0-9]+/-/g' \
-e 's/^-+//' \
-e 's/-+$//' \
<<< "$source")
<<< "$source") || edition=""
echo "$edition"
return 0
@@ -799,11 +810,15 @@ normalizeEditionID() {
edition=$(normalizeEdition "$1") || return 1
case "$edition" in
"pro" | "professional" | "business" )
edition="" ;;
"pro-n" | "pron" | "professional-n" | "professionaln" | "business-n" | "businessn" )
edition="n" ;;
* )
if ! isClientEdition "$edition"; then
case "$edition" in
*"-n" ) base="${edition%-n}" ;;
@@ -816,10 +831,13 @@ normalizeEditionID() {
fi
fi ;;
esac
case "${id,,}" in
"win10"* | "win11"* )
case "$edition" in
"iot-enterprise-ltsc" | \
"iot-enterprise-ltsc-"[0-9][0-9][0-9][0-9] )
@@ -827,8 +845,8 @@ normalizeEditionID() {
"enterprise-ltsc" | \
"enterprise-ltsc-"[0-9][0-9][0-9][0-9] )
edition="ltsc" ;;
esac
;;
esac ;;
esac
echo "$edition"
+86 -25
View File
@@ -273,13 +273,17 @@ detectLanguage() {
for path in "${paths[@]}"; do
lang=$(xmlstarlet sel -T -t -v "normalize-space(string(($path)[1]))" - 2>/dev/null <<< "$xml") || lang=""
if ! lang=$(xmlstarlet sel -T -t -v "normalize-space(string(($path)[1]))" - 2>/dev/null <<< "$xml"); then
warn "failed to read language metadata from Windows image!"
return 0
fi
[ -n "$lang" ] && break
done
if [ -z "$lang" ]; then
warn "Language could not be detected from ISO!"
warn "language could not be detected from ISO!"
return 0
fi
@@ -290,7 +294,7 @@ detectLanguage() {
return 0
fi
warn "Invalid language detected: \"$lang\""
warn "invalid language detected: \"$lang\""
return 0
}
@@ -617,7 +621,7 @@ detectLegacy() {
if [ -n "$marker" ]; then
error "Windows IA-64 (Itanium) images are not supported by this container!"
exit 67
return 2
fi
marker=$(find "$dir" -maxdepth 1 -type d -iname WIN95 -print -quit) || return 2
@@ -786,7 +790,6 @@ readWimHeader() {
if ! rm -f -- "$header"; then
enabled "$DEBUG" && echo "Failed to remove the previous temporary WIM header: $header" >&2
error "Failed to prepare Windows image header!"
return 1
fi
@@ -794,35 +797,30 @@ readWimHeader() {
# extracting install.wim or install.esd from the ISO.
if ! udfread range --ignore-case -o "$header" "$iso" "$image" 0 208 >/dev/null 2>&1; then
enabled "$DEBUG" && echo "udfread failed to read the first 208 bytes of $image from $iso." >&2
error "Failed to read Windows image header!"
rm -f -- "$header"
return 1
fi
if ! size=$(stat -c%s -- "$header"); then
enabled "$DEBUG" && echo "Failed to determine the size of the temporary WIM header: $header" >&2
error "Failed to read Windows image header!"
rm -f -- "$header"
return 1
fi
if (( size != 208 )); then
enabled "$DEBUG" && echo "The WIM header is $size bytes instead of the expected 208 bytes." >&2
error "Failed to read Windows image header!"
rm -f -- "$header"
return 1
fi
if ! signature=$(od -An -N8 -tx1 "$header" | tr -d ' \n'); then
enabled "$DEBUG" && echo "Failed to read the WIM header signature from $header." >&2
error "Failed to read Windows image header!"
rm -f -- "$header"
return 1
fi
if [[ "$signature" != "4d5357494d000000" ]]; then
enabled "$DEBUG" && echo "The WIM header has an invalid signature: ${signature:-empty}." >&2
error "Failed to read Windows image header!"
rm -f -- "$header"
return 1
fi
@@ -1241,7 +1239,7 @@ configureImage() {
if [[ "$DETECTED" == "win81x86"* || "$DETECTED" == "win10x86"* ]]; then
error "The 32-bit version of $desc is not supported!"
exit 67
return 2
fi
local msg="the answer file for $desc was not found ($DETECTED.xml)"
@@ -1269,18 +1267,14 @@ configureImage() {
detectImageInfo() {
local image_info="$1"
local desc index rc
checkPlatform "$image_info" || {
enabled "$DEBUG" && echo "Platform validation failed for the Windows image metadata." >&2
exit 67
}
checkPlatform "$image_info" || return 2
local output
output=$(detectVersion "$image_info") || {
enabled "$DEBUG" && echo "Version detection failed while parsing the Windows image metadata." >&2
error "Failed to detect Windows version from image metadata!"
error "Failed to detect the Windows version from image metadata!"
return 1
}
@@ -1292,16 +1286,18 @@ detectImageInfo() {
validateEdition || {
enabled "$DEBUG" && echo "Edition validation failed for detected image: ${DETECTED:-empty}, index: ${index:-empty}." >&2
error "Failed to validate Windows edition from image metadata!"
error "Failed to validate the Windows edition from image metadata!"
return 1
}
if [ -z "$DETECTED" ]; then
unknownImage || {
rc=$?
enabled "$DEBUG" && echo "Unknown-image handling failed after no Windows version could be detected (status $rc)." >&2
return "$rc"
}
return 0
fi
@@ -1337,25 +1333,84 @@ detectIsoImage() {
# the caller may extract the media. Metadata parsing/configuration errors use 2.
image=$(findIsoImage "$iso") || {
rc=$?
enabled "$DEBUG" && echo "Direct ISO image lookup stopped with status $rc." >&2
enabled "$DEBUG" && echo "ISO image lookup failed (status $rc)." >&2
return "$rc"
}
header=$(readWimHeader "$iso" "$image") || {
enabled "$DEBUG" && echo "Reading the WIM header failed for $image." >&2
error "Failed to read the Windows image header!"
return 2
}
image_info=$(readIsoImageInfo "$iso" "$image" "$header") || {
enabled "$DEBUG" && echo "Reading the WIM XML metadata failed for $image." >&2
error "Failed to read Windows image metadata!"
error "Failed to read the Windows image metadata!"
return 2
}
info "Detecting version from ISO image..."
detectImageInfo "$image_info" || {
enabled "$DEBUG" && echo "Processing the Windows image metadata failed." >&2
error "Failed to process the Windows image metadata!"
return 2
}
return 0
}
detectESDImage() {
local iso="$1"
local image_info install_info output index rc
local -a detected=()
image_info=$(wimlib-imagex info "$iso" --xml 2>/dev/null |
iconv -f UTF-16LE -t UTF-8 2>/dev/null) || {
rc=$?
error "Cannot read ESD file information (status $rc)."
return 2
}
# Microsoft download ESDs use images 1-3 for setup media, WinPE, and Windows
# Setup; images 4 and higher contain installable editions.
if ! install_info=$(xmlstarlet ed \
-d '/WIM/IMAGE[number(@INDEX) < 4]' \
<<< "$image_info" 2>/dev/null); then
error "Cannot read installable images from ESD file!"
return 2
fi
checkPlatform "$install_info" || return 2
local output
output=$(detectVersion "$install_info") || {
error "Failed to detect Windows version from the ESD metadata!"
return 2
}
mapfile -t detected <<< "$output"
index="${detected[1]:-}"
if [ -z "$index" ]; then
error "Failed to select an installation image based on the ESD metadata!"
return 2
fi
# extractESD removes every other image, leaving the selected edition at
# index 1. Detect against that final layout so the generated answer file
# already references the index that will exist after extraction.
if ! image_info=$(xmlstarlet ed \
-d "/WIM/IMAGE[number(@INDEX) != $index]" \
-u "/WIM/IMAGE[@INDEX='$index']/@INDEX" -v '1' \
<<< "$install_info" 2>/dev/null); then
error "Cannot prepare ESD image information!"
return 2
fi
info "Detecting version from ESD image..."
detectImageInfo "$image_info" || {
error "Failed to process the ESD image metadata!"
return 2
}
@@ -1364,6 +1419,8 @@ detectIsoImage() {
baseDir() {
# TODO: Can be removed with base image 7.45+
local path="${1%/}"
[[ -z "$path" || "$path" == "/" ]] && {
@@ -1584,7 +1641,7 @@ extractESD() {
return 1
fi
checkPlatform "$xml" || return
checkPlatform "$installXml" || return
output=$(detectVersion "$installXml") || return
mapfile -t detected <<< "$output"
@@ -1990,7 +2047,11 @@ buildImage() {
base=$(basename "$BOOT")
local out="$TMP/${base%.*}.tmp"
rm -f "$out"
if ! rm -f "$out"; then
error "Failed to remove temporary ISO image: $out"
return 1
fi
desc=$(printVariant "$DETECTED" "ISO")
+171 -48
View File
@@ -3,20 +3,43 @@ set -Eeuo pipefail
startWindows() {
parseVersion || exit 58
parseLanguage || exit 62
detectCustom || exit 64
parseVersion || {
error "Failed to parse the Windows version!"
exit 58
}
parseLanguage || {
error "Failed to parse the Windows language!"
exit 62
}
detectCustom || {
error "Failed to scan for custom installation media!"
exit 64
}
local rc=0
startInstall || rc=$?
(( rc > 1 )) && exit "$rc"
if (( rc )); then
bootWindows || {
error "Failed to boot Windows!"
exit 66
}
if ! startInstall; then
bootWindows || exit 66
return 0
fi
if ! hasImage "$ISO"; then
if ! downloadImage "$ISO" "$VERSION" "$LANGUAGE"; then
removeImage "$ISO" || :
exit 68
fi
fi
local boot="$BOOT"
@@ -48,11 +71,12 @@ selectWindowsImage() {
XML=""
FB="falling back to manual installation!"
normalizeDetected || return 70
normalizeDetected || :
if [ -n "$DETECTED" ]; then
if ! setImage; then
error "Failed to configure the detected Windows image!"
return 70
fi
@@ -61,6 +85,7 @@ selectWindowsImage() {
fi
if ! extractImage "$iso" "$dir" "$VERSION"; then
error "Failed to extract the Windows installation image!"
removeImage "$iso" || :
return 72
fi
@@ -76,11 +101,21 @@ selectWindowsImage() {
detectIsoImage "$iso" && return 0
rc=$?
(( rc == 1 )) || return 76
if (( rc != 1 )); then
error "Failed to inspect the Windows installation ISO!"
return 76
fi
elif [[ "${iso,,}" == *.esd ]]; then
detectESDImage "$iso" && return 0
error "Failed to inspect the Windows installation ESD!"
return 76
fi
if ! extractImage "$iso" "$dir" "$VERSION"; then
error "Failed to extract the Windows installation image!"
removeImage "$iso" || :
return 74
fi
@@ -90,9 +125,15 @@ selectWindowsImage() {
detectImage "$dir" && return 0
rc=$?
(( rc == 1 )) || return 76
if (( rc != 1 )); then
error "Failed to detect the extracted Windows installation image!"
return 76
fi
skipUnattended "$dir" "$iso" "$boot" || return 76
skipUnattended "$dir" "$iso" "$boot" || {
error "Failed to fall back to manual installation!"
return 76
}
handled=1
return 0
@@ -108,24 +149,37 @@ configureMachine() {
desc=$(printVariant "$DETECTED" "$DETECTED") || return 78
if ! checkMemory "$DETECTED"; then
if [ -z "$CUSTOM" ]; then
useOriginalImage "$iso" || return 79
useOriginalImage "$iso" || {
error "Failed to preserve the original installation image!"
return 79
}
fi
return 79
fi
if ! setMachine "$DETECTED" "$iso" "$dir" "$desc"; then
error "Failed to configure the virtual machine for $desc!"
return 80
fi
if ! restoreMachineState; then
error "Failed to restore the saved machine state!"
return 82
fi
if ! supportsUnattended "$DETECTED"; then
skipUnattended "$dir" "$iso" "$boot" "N" || return 83
skipUnattended "$dir" "$iso" "$boot" "N" || {
error "Failed to fall back to manual installation!"
return 83
}
handled=1
return 0
fi
return 0
@@ -141,16 +195,23 @@ prepareWindowsImage() {
if supportsXML "$DETECTED"; then
if ! createOverlay "$XML" "$LANGUAGE" "$TMP/setup"; then
error "Failed to create the Windows setup overlay!"
return 84
fi
if ! createSetupImage "$TMP/setup" "$STORAGE/setup.img"; then
error "Failed to create the Windows setup image!"
return 86
fi
# Bootable ISOs can be reused unchanged with the generated setup image.
if (( ! extracted )); then
useOriginalImage "$iso" || return 88
if (( ! extracted )) && isDirectImage "$iso"; then
useOriginalImage "$iso" || {
error "Failed to preserve the original installation image!"
return 88
}
return 0
fi
@@ -158,28 +219,54 @@ prepareWindowsImage() {
# Extracted modern sources and SIF-based legacy media require a clean rebuild.
if (( ! extracted )); then
if ! extractImage "$iso" "$dir" "$VERSION"; then
error "Failed to extract the Windows installation image!"
removeImage "$iso" || :
return 90
fi
fi
if ! prepareImage "$iso" "$dir"; then
error "Failed to prepare the Windows installation image!"
return 92
fi
removeImage "$iso" || return 96
buildImage "$dir" || return 98
removeImage "$iso" || {
error "Failed to remove the source installation image!"
return 96
}
buildImage "$dir" || {
error "Failed to build the Windows installation image!"
return 98
}
return 0
}
bootWindows() {
restoreMachineState || return
restoreBootMode || return
restoreMachine || return
reserveSambaPorts || return
if ! restoreMachineState; then
error "Failed to restore the saved machine state!"
return 1
fi
if ! restoreBootMode; then
error "Failed to restore the saved boot mode!"
return 1
fi
if ! restoreMachine; then
error "Failed to restore the saved machine type!"
return 1
fi
if ! reserveSambaPorts; then
error "Failed to reserve Samba ports!"
return 1
fi
return 0
}
@@ -211,13 +298,17 @@ startInstall() {
case "${boot,,}" in
"windows."* )
error "The download filename \"$file\" uses the reserved \"windows.*\" namespace!"
exit 58 ;;
return 58 ;;
esac
else
local language
language=$(getLanguage "$LANGUAGE" "culture")
if ! language=$(getLanguage "$LANGUAGE" "culture"); then
error "Failed to determine the Windows language!"
return 62
fi
language="${language%%-*}"
if [ -n "$language" ] && [[ "${language,,}" != "en" ]]; then
@@ -235,28 +326,32 @@ startInstall() {
if ! rm -rf -- "$TMP"; then
error "Failed to remove directory \"$TMP\" !"
exit 50
return 50
fi
local setup="$STORAGE/setup.img"
if ! rm -f -- "$setup" "${setup}.tmp"; then
error "Failed to remove setup image \"$setup\" !"
exit 50
return 50
fi
local previousBase
if ! previousBase=$(readState "base"); then
error "Failed to read the previous installation state!"
exit 50
return 50
fi
skipInstall "$BOOT" "$previousBase" && return 1
local rc=0
skipInstall "$BOOT" "$previousBase" || rc=$?
(( rc > 1 )) && return "$rc"
(( rc )) || return 1
if [ -z "$previousBase" ] && hasData; then
if enabled "$SHUTDOWN" && [ ! -f "$STORAGE/windows.boot" ]; then
discardPrevious "" || exit 50
discardPrevious "" || return 50
else
if ! backupPrevious ""; then
warn "the backup was incomplete, continuing with installation..."
@@ -267,7 +362,7 @@ startInstall() {
if ! makeDir "$TMP"; then
error "Failed to create directory \"$TMP\" !"
exit 50
return 50
fi
if [ -z "$CUSTOM" ]; then
@@ -285,27 +380,27 @@ startInstall() {
if [ -n "$CUSTOM" ] || [ ! -s "$BOOT" ]; then
if ! rm -f -- "$BOOT"; then
error "Failed to remove obsolete ISO file \"$BOOT\" !"
exit 50
return 50
fi
fi
if ! find "$STORAGE" -maxdepth 1 -type f -iname 'data.*' -not -iname '*.iso' -delete; then
error "Failed to remove obsolete disk files from \"$STORAGE\" !"
exit 50
return 50
fi
if ! find "$STORAGE" -maxdepth 1 -type f -iname 'windows.*' -not -iname '*.iso' -delete; then
error "Failed to remove obsolete Windows files from \"$STORAGE\" !"
exit 50
return 50
fi
if ! find "$STORAGE" -maxdepth 1 -type f \( -iname '*.rom' -or -iname '*.vars' \) -delete; then
error "Failed to remove obsolete firmware files from \"$STORAGE\" !"
exit 50
return 50
fi
if [ -z "$CUSTOM" ] && [[ "${VERSION,,}" != "http"* ]]; then
checkMemory "$VERSION" || exit 67
checkMemory "$VERSION" || return 67
fi
# Work from the temporary directory so the persistent source path can
@@ -313,7 +408,7 @@ startInstall() {
if [ -z "$CUSTOM" ] && [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
if ! mv -f -- "$BOOT" "$ISO"; then
error "Failed to move ISO file from \"$BOOT\" to \"$ISO\" !"
exit 50
return 50
fi
fi
@@ -333,7 +428,7 @@ skipUnattended() {
# so they cannot use the manual-install fallback.
if ! isDirectImage "$iso"; then
error "Failed to boot \"$iso\" because it is not a directly bootable ISO image!"
exit 60
return 1
fi
# When automatic preparation fails, inspect extracted media to determine
@@ -382,7 +477,7 @@ skipInstall() {
if ! writeState "base" "$previousBase"; then
error "Failed to migrate the previous installation state!"
exit 50
return 50
fi
fi
@@ -390,10 +485,12 @@ 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."* ]] && hasData && [ -f "$marker" ]; then
if ! rm -f -- "$STORAGE/$previousBase"; then
error "Failed to remove obsolete ISO file \"$STORAGE/$previousBase\" !"
exit 50
return 50
fi
fi
# A changed source invalidates an unfinished installation. Back up an
@@ -404,7 +501,7 @@ skipInstall() {
if ! rm -f -- "$STORAGE/$previousBase"; then
error "Failed to remove ISO file \"$STORAGE/$previousBase\" !"
exit 50
return 50
fi
return 1
@@ -430,7 +527,7 @@ skipInstall() {
fi
if enabled "$SHUTDOWN" && [ ! -f "$marker" ]; then
discardPrevious "$STORAGE/$previousBase" || exit 50
discardPrevious "$STORAGE/$previousBase" || return 50
return 1
fi
@@ -469,7 +566,10 @@ finishInstall() {
fi
local file="$STORAGE/windows.ver"
cp -f /etc/version "$file" || return 1
cp -f /etc/version "$file" || {
error "Failed to save the Windows installation version!"
return 1
}
if ! setOwner "$file"; then
warn "Failed to set the owner for \"$file\" !"
@@ -477,15 +577,23 @@ finishInstall() {
if [[ "$boot" == "$STORAGE/"* ]]; then
if [[ "$aborted" != [Yy1]* ]] || [ -z "$CUSTOM" ]; then
base=$(basename "$boot")
writeState "base" "$base" || return 1
writeState "base" "$base" || {
error "Failed to save the Windows installation source!"
return 1
}
fi
fi
if [[ "${PLATFORM,,}" == "x64" ]]; then
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
writeState "mode" "$BOOT_MODE" || return 1
writeState "mode" "$BOOT_MODE" || {
error "Failed to save the Windows boot mode!"
return 1
}
else
@@ -496,14 +604,22 @@ finishInstall() {
fi
if (( secure )); then
BOOT_MODE="windows_secure"
writeState "mode" "$BOOT_MODE" || return 1
writeState "mode" "$BOOT_MODE" || {
error "Failed to save the Windows boot mode!"
return 1
}
fi
fi
fi
reserveSambaPorts || return 1
reserveSambaPorts || {
error "Failed to reserve Samba ports!"
return 1
}
if ! rm -rf -- "$TMP"; then
error "Failed to remove directory \"$TMP\" !"
@@ -516,7 +632,6 @@ finishInstall() {
findFile() {
local fname="$1"
local dir file base
local boot="$STORAGE/windows.boot"
@@ -896,7 +1011,12 @@ detectImage() {
local image_info
image_info=$(readImageInfo "$wim") || return $?
detectImageInfo "$image_info" || return 2
detectImageInfo "$image_info" || {
error "Failed to process the Windows image metadata!"
return 2
}
return 0
}
prepareImage() {
@@ -1212,7 +1332,10 @@ createOverlay() {
return 1
fi
addAnswerFile "$asset" "$language" "$stage" || return 1
addAnswerFile "$asset" "$language" "$stage" || {
error "Failed to include the Windows answer file!"
return 1
}
return 0
}
@@ -1309,8 +1432,8 @@ backupPrevious () {
failed="Y"
fi
[ -z "$(ls -A "$dir")" ] && rm -rf "$dir"
[ -z "$(ls -A "$root")" ] && rm -rf "$root"
rmdir "$dir" 2>/dev/null || :
rmdir "$root" 2>/dev/null || :
[ -n "$failed" ] && return 1
+17 -8
View File
@@ -175,13 +175,16 @@ downloadWindowsLink() {
--max-filesize 100K \
-- "$skuUrl") || return
# Let jq parsing failures propagate so malformed API data is not mistaken
# for a normal missing-result response. The same applies to the link data.
skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null || return
skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null || skuId=""
if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]]; then
if [[ "${lang,,}" != "en" && "${lang,,}" != "en-"* ]]; then
language=$(getLanguage "$lang" "desc")
error "No download in the $language language available for $desc!"
else
error "Microsoft server provided us no SKU ID in response to our request!"
info "Response: $skuJson"
fi
return 1
fi
@@ -214,10 +217,10 @@ downloadWindowsLink() {
return 1
fi
link=$(printf '%s\n' "$linkJson" | jq --argjson TYPE "$type" -r 'first(.ProductDownloadOptions[]? | select(.DownloadType == $TYPE) | .Uri) // empty') 2>/dev/null || return
link=$(printf '%s\n' "$linkJson" | jq --argjson TYPE "$type" -r 'first(.ProductDownloadOptions[]? | select(.DownloadType == $TYPE) | .Uri) // empty') 2>/dev/null || link=""
if [ -z "$link" ] || [[ "${link,,}" == "null" ]]; then
error "Microsoft server gave us no download link to our request for an automated download!"
error "Microsoft server provided us no download link to our request for an automated download!"
info "Response: $linkJson"
return 1
fi
@@ -287,7 +290,7 @@ downloadWindows() {
grep -Eio "<option[^>]*value=[\"'][0-9]+[\"'][^>]*>[[:space:]]*Windows[^<]*" |
sed -nE "s/.*value=[\"']([0-9]+)[\"'].*/\1/p" |
sed -n '1p' |
cut -c 1-16) || return
cut -c 1-16) || productId=""
enabled "$DEBUG" && echo "$productId"
if [ -z "$productId" ]; then
@@ -1119,7 +1122,7 @@ verifyFile() {
info "Successfully verified $type!" && return 0
fi
error "The downloaded file has an unknown $algo checksum: $hash , as the expected value was: $check. Please report this at $SUPPORT/issues"
warn "the downloaded file has an unknown $algo checksum: $hash , as the expected value was: $check. Please report this at $SUPPORT/issues"
return 1
}
@@ -1280,7 +1283,11 @@ downloadImage() {
desc=$(fromFile "$base")
web_desc="$desc"
tryDownload "$iso" "$version" "" "" "$desc" "$seconds" "$web_desc" || return
tryDownload "$iso" "$version" "" "" "$desc" "$seconds" "$web_desc" || {
rc=$?
error "Failed to download the Windows image from the specified URL!"
return "$rc"
}
return 0
fi
@@ -1450,6 +1457,8 @@ downloadImage() {
if [[ "$tried" == "n" ]]; then
error "No download method is available for $desc!"
else
error "All download methods failed for $desc!"
fi
return 1