mirror of
https://github.com/dockur/windows.git
synced 2026-08-24 07:39:00 +01:00
feat: Simplify WIM platform and version detection (#2108)
This commit is contained in:
+16
-71
@@ -762,27 +762,23 @@ normalizeServerEdition() {
|
|||||||
edition="${edition#r2-}"
|
edition="${edition#r2-}"
|
||||||
|
|
||||||
case "$edition" in
|
case "$edition" in
|
||||||
"core" | "core-installation" | "server-core-installation" )
|
"core" | "server-core" | "servercore" | \
|
||||||
|
"core-installation" | "server-core-installation" )
|
||||||
edition="standard-core" ;;
|
edition="standard-core" ;;
|
||||||
"desktop-experience" | "server-with-a-gui" | "full-installation" )
|
"desktop-experience" | "server-with-a-gui" | "full-installation" )
|
||||||
edition="standard" ;;
|
edition="standard" ;;
|
||||||
*"-server-core-installation" )
|
*"-server-core-installation" | *"-core-installation" )
|
||||||
edition="${edition%-server-core-installation}-core" ;;
|
edition="${edition%-server-core-installation}"
|
||||||
*"-core-installation" )
|
|
||||||
edition="${edition%-core-installation}-core" ;;
|
edition="${edition%-core-installation}-core" ;;
|
||||||
*"-desktop-experience" )
|
*"-desktop-experience" | *"-server-with-a-gui" | *"-full-installation" )
|
||||||
edition="${edition%-desktop-experience}" ;;
|
edition="${edition%-desktop-experience}"
|
||||||
*"-server-with-a-gui" )
|
edition="${edition%-server-with-a-gui}"
|
||||||
edition="${edition%-server-with-a-gui}" ;;
|
|
||||||
*"-full-installation" )
|
|
||||||
edition="${edition%-full-installation}" ;;
|
edition="${edition%-full-installation}" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
edition="${edition#server-}"
|
edition="${edition#server-}"
|
||||||
edition="${edition#server}"
|
edition="${edition#server}"
|
||||||
|
|
||||||
[ "$edition" == "core" ] && edition="standard-core"
|
|
||||||
|
|
||||||
echo "$edition"
|
echo "$edition"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -794,24 +790,13 @@ normalizeServerEditionID() {
|
|||||||
edition=$(normalizeServerEdition "$1") || return 1
|
edition=$(normalizeServerEdition "$1") || return 1
|
||||||
|
|
||||||
case "$edition" in
|
case "$edition" in
|
||||||
"" | "standard" | "serverstandard" ) edition="" ;;
|
"standard" ) edition="" ;;
|
||||||
"core" | "standard-core" | "standardcore" | "serverstandardcore" ) edition="standard-core" ;;
|
"standardcore" | "datacentercore" | "enterprisecore" | "webcore" )
|
||||||
"datacenter" | "serverdatacenter" ) edition="datacenter" ;;
|
edition="${edition%core}-core" ;;
|
||||||
"datacenter-core" | "datacentercore" | "serverdatacentercore" ) edition="datacenter-core" ;;
|
"datacenter-azure-edition" | "datacenterazureedition" )
|
||||||
"datacenter-azure" | "datacenter-azure-edition" | "datacenterazureedition" | \
|
edition="datacenter-azure" ;;
|
||||||
"serverdatacenterazureedition" | "serverturbine" ) edition="datacenter-azure" ;;
|
"datacenter-azure-edition-core" | "datacenterazureeditioncore" )
|
||||||
"datacenter-azure-core" | "datacenter-azure-edition-core" | \
|
edition="datacenter-azure-core" ;;
|
||||||
"datacenterazureeditioncore" | "serverdatacenterazureeditioncore" | \
|
|
||||||
"serverturbinecore" ) edition="datacenter-azure-core" ;;
|
|
||||||
"enterprise" | "serverenterprise" ) edition="enterprise" ;;
|
|
||||||
"enterprise-core" | "enterprisecore" | "serverenterprisecore" ) edition="enterprise-core" ;;
|
|
||||||
"web" | "serverweb" ) edition="web" ;;
|
|
||||||
"web-core" | "webcore" | "serverwebcore" ) edition="web-core" ;;
|
|
||||||
"foundation" | "serverfoundation" ) edition="foundation" ;;
|
|
||||||
"essentials" | "serveressentials" ) edition="essentials" ;;
|
|
||||||
# Keep unrecognized internal edition IDs deterministic and unique.
|
|
||||||
# Known aliases above only provide stable, friendlier public names.
|
|
||||||
* ) : ;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "$edition"
|
echo "$edition"
|
||||||
@@ -842,45 +827,6 @@ getServerEditionID() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getEditionOrder() {
|
|
||||||
|
|
||||||
local id="${1,,}"
|
|
||||||
|
|
||||||
case "$id" in
|
|
||||||
"win20"* )
|
|
||||||
printf '%s\n' \
|
|
||||||
"|default|@default" \
|
|
||||||
"-datacenter|datacenter|datacenter datacenter-*" \
|
|
||||||
"-datacenter-azure|datacenter|datacenter-azure" \
|
|
||||||
"-enterprise|enterprise|enterprise enterprise-*" \
|
|
||||||
"-web|web|web web-*" \
|
|
||||||
"-foundation|foundation|foundation foundation-*" \
|
|
||||||
"-essentials|essentials|essentials essentials-*" \
|
|
||||||
"-standard-core|standard-core|standard-core standard-core-*" \
|
|
||||||
"-datacenter-core|datacenter-core|datacenter-core datacenter-core-*" \
|
|
||||||
"-datacenter-azure-core|datacenter-core|datacenter-azure-core" \
|
|
||||||
"-enterprise-core|enterprise-core|enterprise-core enterprise-core-*" \
|
|
||||||
"-web-core|web-core|web-core web-core-*" \
|
|
||||||
"-hv|hv|hv hv-*"
|
|
||||||
;;
|
|
||||||
* )
|
|
||||||
printf '%s\n' \
|
|
||||||
"-enterprise|enterprise|enterprise enterprise-*" \
|
|
||||||
"-ultimate|ultimate|ultimate ultimate-*" \
|
|
||||||
"|default|@default n pro pro-* professional professional-* business business-*" \
|
|
||||||
"-iot|iot|iot iot-* enterprise-iot enterprise-iot-*" \
|
|
||||||
"-ltsc|ltsc|ltsc ltsc-* enterprise-ltsc enterprise-ltsc-*" \
|
|
||||||
"-education|education|education education-* pro-education pro-education-*" \
|
|
||||||
"-home|home|home home-*" \
|
|
||||||
"-home-premium|home|home-premium home-premium-*" \
|
|
||||||
"-home-basic|home|home-basic home-basic-*" \
|
|
||||||
"-starter|starter|starter starter-*"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
|
|
||||||
local name="$1"
|
local name="$1"
|
||||||
@@ -899,8 +845,7 @@ getVersion() {
|
|||||||
[ -n "$evaluation" ] && id+="$evaluation"
|
[ -n "$evaluation" ] && id+="$evaluation"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"win2025"* | "win2022"* | "win2019"* | "win2016"* | \
|
"win20"* )
|
||||||
"win2012"* | "win2008"* | "win2003"* )
|
|
||||||
if [[ "${name,,}" == *"hyper-v server"* ]]; then
|
if [[ "${name,,}" == *"hyper-v server"* ]]; then
|
||||||
id+="-hv"
|
id+="-hv"
|
||||||
elif edition=$(getServerEditionID "$name" "$id"); then
|
elif edition=$(getServerEditionID "$name" "$id"); then
|
||||||
@@ -1453,7 +1398,7 @@ getLink4() {
|
|||||||
"win2008r2" )
|
"win2008r2" )
|
||||||
size=3166584832
|
size=3166584832
|
||||||
sum="dfd9890881b7e832a927c38310fb415b7ea62ac5a896671f2ce2a111998f0df8"
|
sum="dfd9890881b7e832a927c38310fb415b7ea62ac5a896671f2ce2a111998f0df8"
|
||||||
url="en_windows_server_2008_r2_with_sp1_x64_dvd_617601_202006/en_windows_server_2008_r2_with_sp1_x64_dvd_617601.iso"
|
url="en_windows_server_2008_r2_with_sp1_x64_dvd_617601_202405/en_windows_server_2008_r2_with_sp1_x64_dvd_617601.iso"
|
||||||
;;
|
;;
|
||||||
"win7x64" | "win7x64-ultimate" )
|
"win7x64" | "win7x64-ultimate" )
|
||||||
size=3320903680
|
size=3320903680
|
||||||
|
|||||||
+177
-325
@@ -4,39 +4,17 @@ set -Eeuo pipefail
|
|||||||
getVersions() {
|
getVersions() {
|
||||||
|
|
||||||
local xml="$1"
|
local xml="$1"
|
||||||
local versions_name="$2"
|
|
||||||
local bases_name="$3"
|
|
||||||
local groups_name="$4"
|
|
||||||
local indexes_name="$5"
|
|
||||||
|
|
||||||
local -n bases_ref="$bases_name"
|
local image_index image edition_id
|
||||||
local -n groups_ref="$groups_name"
|
local install_type candidate_base
|
||||||
local -n indexes_ref="$indexes_name"
|
local candidate candidate_id flags
|
||||||
local -n versions_ref="$versions_name"
|
local evaluation key name structured
|
||||||
|
local platform records display product
|
||||||
local platform image_count records record_count=0
|
|
||||||
local image_index display product image edition_id
|
|
||||||
local install_type flags candidate candidate_id
|
|
||||||
local candidate_base evaluation key name structured
|
|
||||||
local separator=$'\x1f'
|
local separator=$'\x1f'
|
||||||
|
local -A indexes=()
|
||||||
bases_ref=()
|
|
||||||
groups_ref=()
|
|
||||||
indexes_ref=()
|
|
||||||
versions_ref=()
|
|
||||||
|
|
||||||
platform=$(getPlatform "$xml") || return 1
|
platform=$(getPlatform "$xml") || return 1
|
||||||
image_count=$(xmlstarlet sel -T -t -v 'count(/WIM/IMAGE)' - 2>/dev/null <<< "$xml") || return 1
|
|
||||||
|
|
||||||
if [[ ! "$image_count" =~ ^[0-9]+$ ]]; then
|
|
||||||
error "Invalid image count in WIM metadata: '$image_count'"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
(( image_count > 0 )) || return 0
|
|
||||||
|
|
||||||
# Keep one compact record per image. XML 1.0 cannot contain U+001F, so it
|
|
||||||
# can safely separate fields while all edition logic remains in Bash.
|
|
||||||
if ! records=$(xmlstarlet sel \
|
if ! records=$(xmlstarlet sel \
|
||||||
-T -t \
|
-T -t \
|
||||||
-m '/WIM/IMAGE' \
|
-m '/WIM/IMAGE' \
|
||||||
@@ -54,8 +32,6 @@ getVersions() {
|
|||||||
|
|
||||||
while IFS="$separator" read -r image_index display product image edition_id install_type flags; do
|
while IFS="$separator" read -r image_index display product image edition_id install_type flags; do
|
||||||
|
|
||||||
((record_count += 1))
|
|
||||||
|
|
||||||
[ -n "$image_index" ] || continue
|
[ -n "$image_index" ] || continue
|
||||||
|
|
||||||
if [[ ! "$image_index" =~ ^[1-9][0-9]*$ ]]; then
|
if [[ ! "$image_index" =~ ^[1-9][0-9]*$ ]]; then
|
||||||
@@ -66,47 +42,41 @@ getVersions() {
|
|||||||
candidate_id=""
|
candidate_id=""
|
||||||
candidate_base=""
|
candidate_base=""
|
||||||
|
|
||||||
# NAME normally contains the most precise edition identifier (including
|
|
||||||
# Server Core), while DISPLAYNAME is the best fallback for other images.
|
|
||||||
for candidate in "$image" "$display" "$product"; do
|
for candidate in "$image" "$display" "$product"; do
|
||||||
|
|
||||||
[[ "$candidate" == *"Operating System"* ]] && continue
|
[[ "$candidate" == *"Operating System"* ]] && continue
|
||||||
[ -n "$candidate" ] || continue
|
[ -n "$candidate" ] || continue
|
||||||
|
|
||||||
candidate_base=$(fromName "$candidate" "$platform")
|
|
||||||
candidate_id=$(getVersion "$candidate" "$platform")
|
candidate_id=$(getVersion "$candidate" "$platform")
|
||||||
|
candidate_base="${candidate_id%%-*}"
|
||||||
|
|
||||||
[ -n "$candidate_base" ] && [ -n "$candidate_id" ] && break
|
[ -n "$candidate_id" ] && break
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# Preserve NAME precedence, but allow a recognized DISPLAYNAME edition to
|
# NAME sometimes contains only the Windows family while DISPLAYNAME
|
||||||
# refine an otherwise generic family name.
|
# contains the actual edition. Use the more specific DISPLAYNAME then.
|
||||||
if [ -n "$candidate_base" ] && [ -n "$candidate_id" ] && [ -n "$display" ]; then
|
if [ -n "$candidate_base" ] && [ -n "$candidate_id" ] && [ -n "$display" ]; then
|
||||||
|
|
||||||
name=$(normalizeEdition "$(printVersion "$candidate_base" "")") || return 1
|
name=$(normalizeEdition "$(printVersion "$candidate_base" "")") || return 1
|
||||||
candidate=$(normalizeEdition "$candidate") || return 1
|
candidate=$(normalizeEdition "$candidate") || return 1
|
||||||
|
|
||||||
if [ "$candidate" = "$name" ]; then
|
if [ "$candidate" = "$name" ]; then
|
||||||
|
|
||||||
structured=$(getVersion "$display" "$platform")
|
structured=$(getVersion "$display" "$platform")
|
||||||
|
|
||||||
if [ "$(fromName "$display" "$platform")" = "$candidate_base" ] &&
|
if [ -n "$structured" ] && [ "${structured%%-*}" = "$candidate_base" ] &&
|
||||||
[ -n "$structured" ] && [[ "${structured%-eval}" != "$candidate_base" ]] &&
|
[[ "${structured%-eval}" != "$candidate_base" ]] &&
|
||||||
[[ "$(getVersionPriority "$structured" "$candidate_base")" != "other" ]]; then
|
[ "$(getEditionRank "$structured")" -lt 99 ]; then
|
||||||
candidate_id="$structured"
|
candidate_id="$structured"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$candidate_base" ] || [ -z "$candidate_id" ]; then
|
if [ -z "$candidate_base" ] || [ -z "$candidate_id" ]; then
|
||||||
|
|
||||||
name="${display:-${image:-$product}}"
|
name="${display:-${image:-$product}}"
|
||||||
[ -n "$name" ] && warn "Unknown image name: '$name'"
|
[ -n "$name" ] && warn "Unknown image name: '$name'"
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
evaluation=""
|
evaluation=""
|
||||||
@@ -122,10 +92,9 @@ getVersions() {
|
|||||||
|
|
||||||
key="${candidate_id,,}"
|
key="${candidate_id,,}"
|
||||||
|
|
||||||
# Some client media use the same friendly name-derived ID for distinct
|
# Friendly names can collapse two different images to the same ID.
|
||||||
# editions. Preserve the established unsuffixed Pro ID, and use the
|
# Structured metadata is only needed to disambiguate that collision.
|
||||||
# structured edition metadata only to disambiguate a collision.
|
if [[ -v "indexes[$key]" ]]; then
|
||||||
if [[ -v "indexes_ref[$key]" ]]; then
|
|
||||||
|
|
||||||
structured=""
|
structured=""
|
||||||
|
|
||||||
@@ -136,9 +105,6 @@ getVersions() {
|
|||||||
"win20"* )
|
"win20"* )
|
||||||
structured=$(normalizeServerEditionID "${flags:-$edition_id}") || return 1
|
structured=$(normalizeServerEditionID "${flags:-$edition_id}") || return 1
|
||||||
|
|
||||||
# Some media use the same EDITIONID for Core and Desktop images.
|
|
||||||
# INSTALLATIONTYPE provides the structural distinction without
|
|
||||||
# requiring a hardcoded marketing name.
|
|
||||||
if [[ "${install_type,,}" == *"core"* && "$structured" != *"-core" ]]; then
|
if [[ "${install_type,,}" == *"core"* && "$structured" != *"-core" ]]; then
|
||||||
structured+="-core"
|
structured+="-core"
|
||||||
fi
|
fi
|
||||||
@@ -152,262 +118,187 @@ getVersions() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -v "indexes_ref[$key]" ]]; then
|
if [[ -v "indexes[$key]" ]]; then
|
||||||
warn "Duplicate image identity '$candidate_id' at indexes ${indexes_ref[$key]} and $image_index"
|
warn "Duplicate image identity '$candidate_id' at indexes ${indexes[$key]} and $image_index"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
indexes_ref["$key"]="$image_index"
|
indexes["$key"]="$image_index"
|
||||||
versions_ref+=( "$candidate_id" )
|
printf '%s\t%s\n' "$candidate_id" "$image_index"
|
||||||
bases_ref+=( "$candidate_base" )
|
|
||||||
groups_ref+=( "$(getVersionPriority "$candidate_id" "$candidate_base")" )
|
|
||||||
|
|
||||||
done <<< "$records"
|
done <<< "$records"
|
||||||
|
|
||||||
if (( record_count != image_count )); then
|
|
||||||
error "Expected $image_count image records in WIM metadata, found $record_count!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersionPriority() {
|
getEditionRank() {
|
||||||
|
|
||||||
local id="${1,,}"
|
local id="${1,,}"
|
||||||
local base="${2,,}"
|
local base="${id%%-*}"
|
||||||
|
local edition
|
||||||
local entry priority patterns pattern
|
|
||||||
local result="other" score best_score=-1
|
|
||||||
local -a order=() pattern_list=()
|
|
||||||
|
|
||||||
id="${id%-eval}"
|
id="${id%-eval}"
|
||||||
|
edition="${id#"$base"}"
|
||||||
mapfile -t order < <(getEditionOrder "$base")
|
|
||||||
|
|
||||||
local edition="${id#"$base"}"
|
|
||||||
edition="${edition#-}"
|
edition="${edition#-}"
|
||||||
|
|
||||||
# Use the most specific matching pattern. This prevents broad patterns
|
case "$base" in
|
||||||
# such as enterprise-* from taking precedence over enterprise-iot-*.
|
"win20"* )
|
||||||
for entry in "${order[@]}"; do
|
case "$edition" in
|
||||||
|
"" ) echo 0 ;;
|
||||||
IFS='|' read -r _ priority patterns <<< "$entry"
|
"datacenter-azure-core" | "datacenter-azure-core-"* | \
|
||||||
read -r -a pattern_list <<< "$patterns"
|
"datacenter-core" | "datacenter-core-"* ) echo 7 ;;
|
||||||
|
"enterprise-core" | "enterprise-core-"* ) echo 8 ;;
|
||||||
for pattern in "${pattern_list[@]}"; do
|
"web-core" | "web-core-"* ) echo 9 ;;
|
||||||
|
"standard-core" | "standard-core-"* ) echo 6 ;;
|
||||||
if [ "$pattern" = "@default" ]; then
|
"datacenter" | "datacenter-"* ) echo 1 ;;
|
||||||
[ -z "$edition" ] || continue
|
"enterprise" | "enterprise-"* ) echo 2 ;;
|
||||||
score=1
|
"web" | "web-"* ) echo 3 ;;
|
||||||
elif [[ "$pattern" == *"*" ]]; then
|
"foundation" | "foundation-"* ) echo 4 ;;
|
||||||
local prefix="${pattern%\*}"
|
"essentials" | "essentials-"* ) echo 5 ;;
|
||||||
[[ "$edition" == "$prefix"* ]] || continue
|
"hv" | "hv-"* ) echo 10 ;;
|
||||||
score="${#pattern}"
|
* ) echo 99 ;;
|
||||||
elif [ "$edition" = "$pattern" ]; then
|
esac
|
||||||
score="${#pattern}"
|
;;
|
||||||
else
|
* )
|
||||||
continue
|
case "$edition" in
|
||||||
fi
|
"enterprise-iot" | "enterprise-iot-"* | "iot" | "iot-"* ) echo 3 ;;
|
||||||
|
"enterprise-ltsc" | "enterprise-ltsc-"* | "ltsc" | "ltsc-"* ) echo 4 ;;
|
||||||
if (( score > best_score )); then
|
"pro-education" | "pro-education-"* | "education" | "education-"* ) echo 5 ;;
|
||||||
result="$priority"
|
"enterprise" | "enterprise-"* ) echo 0 ;;
|
||||||
best_score="$score"
|
"ultimate" | "ultimate-"* ) echo 1 ;;
|
||||||
fi
|
"" | "n" | "pro" | "pro-"* | "professional" | "professional-"* | \
|
||||||
|
"business" | "business-"* ) echo 2 ;;
|
||||||
done
|
"home" | "home-"* ) echo 6 ;;
|
||||||
|
"starter" | "starter-"* ) echo 7 ;;
|
||||||
done
|
* ) echo 99 ;;
|
||||||
|
esac
|
||||||
echo "$result"
|
;;
|
||||||
return 0
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
selectVersion() {
|
selectVersion() {
|
||||||
|
|
||||||
local versions_name="$1"
|
local wanted="$1"
|
||||||
local indexes_name="$2"
|
shift
|
||||||
local preferred_name="$3"
|
|
||||||
local result_name="$4"
|
|
||||||
local index_name="$5"
|
|
||||||
|
|
||||||
local -a candidates=()
|
local candidate record id index
|
||||||
local -n index_map="$indexes_name"
|
local -a candidates=("$wanted")
|
||||||
local -n version_list="$versions_name"
|
|
||||||
local -n selected_version="$result_name"
|
|
||||||
local -n preference_list="$preferred_name"
|
|
||||||
local -n selected_image_index="$index_name"
|
|
||||||
local wanted candidate match
|
|
||||||
|
|
||||||
# A detected edition is only selectable when a matching answer file can
|
# Normal and Evaluation variants of the same edition are compatible.
|
||||||
# actually be staged for it.
|
if [[ "${wanted,,}" == *"-eval" ]]; then
|
||||||
for wanted in "${preference_list[@]}"; do
|
candidates+=("${wanted%-eval}")
|
||||||
|
else
|
||||||
|
candidates+=("$wanted-eval")
|
||||||
|
fi
|
||||||
|
|
||||||
[ -n "$wanted" ] || continue
|
for candidate in "${candidates[@]}"; do
|
||||||
mapfile -t candidates < <(getCompatibleVersions "$wanted")
|
for record in "$@"; do
|
||||||
|
|
||||||
for candidate in "${candidates[@]}"; do
|
IFS=$'\t' read -r id index <<< "$record"
|
||||||
|
[[ "${id,,}" == "${candidate,,}" ]] || continue
|
||||||
match=$(hasVersion "$candidate" "${version_list[@]}") || continue
|
hasAnswerFile "$id" || continue
|
||||||
|
|
||||||
hasAnswerFile "$match" || continue
|
|
||||||
|
|
||||||
local key="${match,,}"
|
|
||||||
selected_version="$match"
|
|
||||||
selected_image_index="${index_map[$key]}"
|
|
||||||
|
|
||||||
|
printf '%s\n%s\n' "$id" "$index"
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompatibleVersions() {
|
|
||||||
|
|
||||||
local wanted="$1"
|
|
||||||
|
|
||||||
printf '%s\n' "$wanted"
|
|
||||||
|
|
||||||
# Treat normal and Evaluation variants of the same edition as compatible.
|
|
||||||
# The exact requested variant is always checked first.
|
|
||||||
if [[ "${wanted,,}" == *"-eval" ]]; then
|
|
||||||
printf '%s\n' "${wanted%-eval}"
|
|
||||||
else
|
|
||||||
printf '%s\n' "$wanted-eval"
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
selectEdition() {
|
|
||||||
|
|
||||||
local versions_name="$1"
|
|
||||||
local bases_name="$2"
|
|
||||||
local groups_name="$3"
|
|
||||||
local indexes_name="$4"
|
|
||||||
local suggested="$5"
|
|
||||||
local result_name="$6"
|
|
||||||
local index_name="$7"
|
|
||||||
local normalize_name="$8"
|
|
||||||
local order_name="$9"
|
|
||||||
|
|
||||||
local -A seen=()
|
|
||||||
local -a preferred=()
|
|
||||||
local -n edition_bases="$bases_name"
|
|
||||||
local -n edition_order="$order_name"
|
|
||||||
local -n edition_groups="$groups_name"
|
|
||||||
local -n edition_versions="$versions_name"
|
|
||||||
local base edition entry suffix priority i
|
|
||||||
|
|
||||||
# Selection precedence is explicit EDITION, source suggestion, canonical
|
|
||||||
# edition order, then noncanonical editions from the same priority groups.
|
|
||||||
if [ -n "$EDITION" ]; then
|
|
||||||
|
|
||||||
for base in "${edition_bases[@]}"; do
|
|
||||||
edition=$("$normalize_name" "$EDITION" "$base") || return 1
|
|
||||||
preferred+=("$base${edition:+-$edition}")
|
|
||||||
done
|
|
||||||
|
|
||||||
if selectVersion "$versions_name" "$indexes_name" preferred "$result_name" "$index_name"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
warn "edition '$EDITION' is not supported by this image, using automatic selection instead."
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$suggested" ]; then
|
|
||||||
|
|
||||||
preferred=("$suggested")
|
|
||||||
|
|
||||||
if selectVersion "$versions_name" "$indexes_name" preferred "$result_name" "$index_name"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# First try each canonical edition in its configured order.
|
|
||||||
preferred=()
|
|
||||||
|
|
||||||
for entry in "${edition_order[@]}"; do
|
|
||||||
|
|
||||||
IFS='|' read -r suffix _ _ <<< "$entry"
|
|
||||||
|
|
||||||
for base in "${edition_bases[@]}"; do
|
|
||||||
preferred+=("$base$suffix")
|
|
||||||
done
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
if selectVersion "$versions_name" "$indexes_name" preferred "$result_name" "$index_name"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Then try noncanonical editions from the same preference groups.
|
|
||||||
seen=()
|
|
||||||
preferred=()
|
|
||||||
|
|
||||||
for entry in "${edition_order[@]}"; do
|
|
||||||
|
|
||||||
IFS='|' read -r _ priority _ <<< "$entry"
|
|
||||||
|
|
||||||
[[ -v "seen[$priority]" ]] && continue
|
|
||||||
seen["$priority"]="Y"
|
|
||||||
|
|
||||||
for ((i=0;i<${#edition_versions[@]};i++)); do
|
|
||||||
|
|
||||||
[[ "${edition_groups[$i]}" == "$priority" ]] || continue
|
|
||||||
preferred+=("${edition_versions[$i]}")
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
selectVersion "$versions_name" "$indexes_name" preferred "$result_name" "$index_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
detectVersion() {
|
detectVersion() {
|
||||||
|
|
||||||
local xml="$1"
|
local xml="$1"
|
||||||
local suggested="${2:-}"
|
|
||||||
|
|
||||||
local -a bases=()
|
local normalize="normalizeEditionID"
|
||||||
local -a groups=()
|
local output record id index base edition suffix
|
||||||
local -a versions=()
|
local -a images=() bases=() order=()
|
||||||
local -A image_indexes=()
|
|
||||||
local -a selection_order=()
|
|
||||||
local result="" index=""
|
|
||||||
|
|
||||||
getVersions "$xml" versions bases groups image_indexes || return
|
output=$(getVersions "$xml") || return
|
||||||
|
[ -n "$output" ] && mapfile -t images <<< "$output"
|
||||||
|
|
||||||
if [ "${#versions[@]}" -eq 0 ]; then
|
if [ "${#images[@]}" -eq 0 ]; then
|
||||||
printf '%s\n%s\n' "$result" "$index"
|
printf '\n\n'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local normalize="normalizeEditionID"
|
for record in "${images[@]}"; do
|
||||||
|
IFS=$'\t' read -r id _ <<< "$record"
|
||||||
|
bases+=("${id%%-*}")
|
||||||
|
done
|
||||||
|
|
||||||
case "${bases[0],,}" in
|
case "${bases[0],,}" in
|
||||||
"win20"* )
|
"win20"* )
|
||||||
normalize="normalizeServerEditionID" ;;
|
normalize="normalizeServerEditionID"
|
||||||
|
order=(
|
||||||
|
"" "-datacenter" "-datacenter-azure" "-enterprise" "-web"
|
||||||
|
"-foundation" "-essentials" "-standard-core" "-datacenter-core"
|
||||||
|
"-datacenter-azure-core" "-enterprise-core" "-web-core" "-hv"
|
||||||
|
) ;;
|
||||||
|
* )
|
||||||
|
order=(
|
||||||
|
"-enterprise" "-ultimate" "" "-iot" "-ltsc" "-education"
|
||||||
|
"-home" "-home-premium" "-home-basic" "-starter"
|
||||||
|
) ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
mapfile -t selection_order < <(getEditionOrder "${bases[0]}")
|
# An explicit EDITION always gets first choice.
|
||||||
|
if [ -n "$EDITION" ]; then
|
||||||
|
|
||||||
if selectEdition versions bases groups image_indexes "$suggested" result index "$normalize" selection_order; then
|
for base in "${bases[@]}"; do
|
||||||
printf '%s\n%s\n' "$result" "$index"
|
|
||||||
|
edition=$("$normalize" "$EDITION" "$base") || return 1
|
||||||
|
|
||||||
|
if output=$(selectVersion "$base${edition:+-$edition}" "${images[@]}"); then
|
||||||
|
printf '%s\n' "$output"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
warn "edition '$EDITION' is not supported by this image, using automatic selection instead."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prefer the normal editions in their established order.
|
||||||
|
for suffix in "${order[@]}"; do
|
||||||
|
for base in "${bases[@]}"; do
|
||||||
|
|
||||||
|
if output=$(selectVersion "$base$suffix" "${images[@]}"); then
|
||||||
|
printf '%s\n' "$output"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Otherwise keep the established family preference for noncanonical
|
||||||
|
# editions, while preserving WIM order within the same family.
|
||||||
|
local rank best_rank=99 result="" result_index=""
|
||||||
|
|
||||||
|
for record in "${images[@]}"; do
|
||||||
|
|
||||||
|
IFS=$'\t' read -r id index <<< "$record"
|
||||||
|
hasAnswerFile "$id" || continue
|
||||||
|
|
||||||
|
rank=$(getEditionRank "$id")
|
||||||
|
(( rank < best_rank )) || continue
|
||||||
|
|
||||||
|
best_rank="$rank"
|
||||||
|
result="$id"
|
||||||
|
result_index="$index"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$result" ]; then
|
||||||
|
printf '%s\n%s\n' "$result" "$result_index"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Keep the first detected image identity when no edition with a usable answer
|
# Keep the first detected identity when only manual/generic fallback remains.
|
||||||
# file was found, so manual and generic fallback handling can still continue.
|
IFS=$'\t' read -r id index <<< "${images[0]}"
|
||||||
result="${versions[0]}"
|
|
||||||
local key="${result,,}"
|
|
||||||
index="${image_indexes[$key]}"
|
|
||||||
|
|
||||||
printf '%s\n%s\n' "$result" "$index"
|
printf '%s\n%s\n' "$id" "$index"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,22 +404,6 @@ getImageSize() {
|
|||||||
printf '%s\n' "$size"
|
printf '%s\n' "$size"
|
||||||
}
|
}
|
||||||
|
|
||||||
hasVersion() {
|
|
||||||
|
|
||||||
local wanted="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
local actual
|
|
||||||
|
|
||||||
for actual in "$@"; do
|
|
||||||
[[ "${actual,,}" == "${wanted,,}" ]] || continue
|
|
||||||
echo "$actual"
|
|
||||||
return 0
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPlatform() {
|
checkPlatform() {
|
||||||
|
|
||||||
local xml="$1"
|
local xml="$1"
|
||||||
@@ -571,76 +446,53 @@ getPlatform() {
|
|||||||
|
|
||||||
local xml="$1"
|
local xml="$1"
|
||||||
|
|
||||||
local image_count invalid_count
|
local arch current platform=""
|
||||||
local x86 x64 arm64 unknown value
|
local image_count output records
|
||||||
local platform="" count=0 output
|
local separator=$'\x1f'
|
||||||
local -a counts=()
|
|
||||||
|
|
||||||
if ! output=$(xmlstarlet sel \
|
if ! output=$(xmlstarlet sel \
|
||||||
-T -t \
|
-T -t \
|
||||||
-v 'count(/WIM/IMAGE)' -n \
|
-v 'count(/WIM/IMAGE)' -n \
|
||||||
-v 'count(/WIM/IMAGE[count(WINDOWS/ARCH) != 1])' -n \
|
-m '/WIM/IMAGE' \
|
||||||
-v 'count(/WIM/IMAGE/WINDOWS/ARCH[normalize-space(.)="0"])' -n \
|
-v 'count(WINDOWS/ARCH)' -o "$separator" \
|
||||||
-v 'count(/WIM/IMAGE/WINDOWS/ARCH[normalize-space(.)="9"])' -n \
|
-v 'normalize-space(WINDOWS/ARCH)' -n \
|
||||||
-v 'count(/WIM/IMAGE/WINDOWS/ARCH[normalize-space(.)="12"])' -n \
|
|
||||||
-v 'count(/WIM/IMAGE/WINDOWS/ARCH[normalize-space(.)!="0" and normalize-space(.)!="9" and normalize-space(.)!="12"])' -n \
|
|
||||||
- 2>/dev/null <<< "$xml"); then
|
- 2>/dev/null <<< "$xml"); then
|
||||||
error "Failed to read architecture metadata from WIM image!"
|
error "Failed to read architecture metadata from WIM image!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mapfile -t counts <<< "$output"
|
image_count="${output%%$'\n'*}"
|
||||||
|
|
||||||
if (( ${#counts[@]} != 6 )); then
|
if [ "$image_count" = "0" ]; then
|
||||||
error "Failed to read architecture counts from WIM metadata!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
for value in "${counts[@]}"; do
|
|
||||||
if [[ ! "$value" =~ ^[0-9]+$ ]]; then
|
|
||||||
error "Invalid architecture count in WIM metadata: '$value'"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
image_count="${counts[0]}"
|
|
||||||
invalid_count="${counts[1]}"
|
|
||||||
x86="${counts[2]}"
|
|
||||||
x64="${counts[3]}"
|
|
||||||
arm64="${counts[4]}"
|
|
||||||
unknown="${counts[5]}"
|
|
||||||
|
|
||||||
if (( image_count == 0 )); then
|
|
||||||
error "No images were found in WIM metadata!"
|
error "No images were found in WIM metadata!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if (( invalid_count > 0 )); then
|
records="${output#*$'\n'}"
|
||||||
error "Missing or duplicate architecture metadata in WIM image!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (( unknown > 0 )); then
|
while IFS="$separator" read -r count arch; do
|
||||||
error "Unsupported architecture value in WIM metadata!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
(( x86 > 0 )) && ((count += 1))
|
if [ "$count" != "1" ]; then
|
||||||
(( x64 > 0 )) && ((count += 1))
|
error "Missing or duplicate architecture metadata in WIM image!"
|
||||||
(( arm64 > 0 )) && ((count += 1))
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if (( count > 1 )); then
|
case "$arch" in
|
||||||
platform="mixed"
|
"0" ) current="x86" ;;
|
||||||
elif (( x86 > 0 )); then
|
"9" ) current="x64" ;;
|
||||||
platform="x86"
|
"12" ) current="arm64" ;;
|
||||||
elif (( x64 > 0 )); then
|
* )
|
||||||
platform="x64"
|
error "Unsupported architecture value in WIM metadata!"
|
||||||
elif (( arm64 > 0 )); then
|
return 1 ;;
|
||||||
platform="arm64"
|
esac
|
||||||
else
|
|
||||||
error "Failed to determine architecture from WIM metadata!"
|
if [ -z "$platform" ]; then
|
||||||
return 1
|
platform="$current"
|
||||||
fi
|
elif [ "$platform" != "$current" ]; then
|
||||||
|
platform="mixed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done <<< "$records"
|
||||||
|
|
||||||
echo "$platform"
|
echo "$platform"
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user