mirror of
https://github.com/dockur/windows.git
synced 2026-08-03 20:47:12 +01:00
feat: Improve Windows image detection and unattended setup (#2037)
This commit is contained in:
+902
-486
File diff suppressed because it is too large
Load Diff
+144
-196
@@ -47,116 +47,79 @@ parseVersion() {
|
|||||||
|
|
||||||
case "${VERSION,,}" in
|
case "${VERSION,,}" in
|
||||||
"11" | "11p" | "win11" | "pro11" | "win11p" | "windows11" | "windows 11" )
|
"11" | "11p" | "win11" | "pro11" | "win11p" | "windows11" | "windows 11" )
|
||||||
VERSION="win11x64"
|
VERSION="win11x64" ;;
|
||||||
;;
|
|
||||||
"11e" | "win11e" | "windows11e" | "windows 11e" )
|
"11e" | "win11e" | "windows11e" | "windows 11e" )
|
||||||
VERSION="win11x64-enterprise-eval"
|
VERSION="win11x64-enterprise-eval" ;;
|
||||||
;;
|
|
||||||
"11l" | "11ltsc" | "ltsc11" | "win11l" | "win11-ltsc" | "win11x64-ltsc" )
|
"11l" | "11ltsc" | "ltsc11" | "win11l" | "win11-ltsc" | "win11x64-ltsc" )
|
||||||
VERSION="win11x64-enterprise-ltsc-eval"
|
VERSION="win11x64-enterprise-ltsc-eval" ;;
|
||||||
;;
|
|
||||||
"11i" | "11iot" | "iot11" | "win11i" | "win11-iot" | "win11x64-iot" )
|
"11i" | "11iot" | "iot11" | "win11i" | "win11-iot" | "win11x64-iot" )
|
||||||
VERSION="win11x64-enterprise-iot-eval"
|
VERSION="win11x64-enterprise-iot-eval" ;;
|
||||||
;;
|
|
||||||
"10" | "10p" | "win10" | "pro10" | "win10p" | "windows10" | "windows 10" )
|
"10" | "10p" | "win10" | "pro10" | "win10p" | "windows10" | "windows 10" )
|
||||||
VERSION="win10x64"
|
VERSION="win10x64" ;;
|
||||||
;;
|
|
||||||
"10e" | "win10e" | "windows10e" | "windows 10e" )
|
"10e" | "win10e" | "windows10e" | "windows 10e" )
|
||||||
VERSION="win10x64-enterprise-eval"
|
VERSION="win10x64-enterprise-eval" ;;
|
||||||
;;
|
|
||||||
"10l" | "10ltsc" | "ltsc10" | "win10l" | "win10-ltsc" | "win10x64-ltsc" )
|
"10l" | "10ltsc" | "ltsc10" | "win10l" | "win10-ltsc" | "win10x64-ltsc" )
|
||||||
VERSION="win10x64-enterprise-ltsc-eval"
|
VERSION="win10x64-enterprise-ltsc-eval" ;;
|
||||||
;;
|
|
||||||
"10i" | "10iot" | "iot10" | "win10i" | "win10-iot" | "win10x64-iot" )
|
"10i" | "10iot" | "iot10" | "win10i" | "win10-iot" | "win10x64-iot" )
|
||||||
VERSION="win10x64-enterprise-iot-eval"
|
VERSION="win10x64-enterprise-iot-eval" ;;
|
||||||
;;
|
|
||||||
"8" | "8p" | "81" | "81p" | "pro8" | "8.1" | "win8" | "win8p" | "win81" | "win81p" | "windows 8" )
|
"8" | "8p" | "81" | "81p" | "pro8" | "8.1" | "win8" | "win8p" | "win81" | "win81p" | "windows 8" )
|
||||||
VERSION="win81x64"
|
VERSION="win81x64" ;;
|
||||||
;;
|
|
||||||
"8e" | "81e" | "8.1e" | "win8e" | "win81e" | "windows 8e" )
|
"8e" | "81e" | "8.1e" | "win8e" | "win81e" | "windows 8e" )
|
||||||
VERSION="win81x64-enterprise-eval"
|
VERSION="win81x64-enterprise-eval" ;;
|
||||||
;;
|
|
||||||
"7" | "win7" | "windows7" | "windows 7" )
|
"7" | "win7" | "windows7" | "windows 7" )
|
||||||
VERSION="win7x64"
|
VERSION="win7x64" ;;
|
||||||
;;
|
|
||||||
"7u" | "win7u" | "windows7u" | "windows 7u" )
|
"7u" | "win7u" | "windows7u" | "windows 7u" )
|
||||||
VERSION="win7x64-ultimate"
|
VERSION="win7x64-ultimate" ;;
|
||||||
;;
|
|
||||||
"7e" | "win7e" | "windows7e" | "windows 7e" )
|
"7e" | "win7e" | "windows7e" | "windows 7e" )
|
||||||
VERSION="win7x64-enterprise"
|
VERSION="win7x64-enterprise" ;;
|
||||||
;;
|
|
||||||
"7x86" | "win7x86" | "win732" | "windows7x86" )
|
"7x86" | "win7x86" | "win732" | "windows7x86" )
|
||||||
VERSION="win7x86"
|
VERSION="win7x86" ;;
|
||||||
;;
|
|
||||||
"7ux86" | "7u32" | "win7x86-ultimate" )
|
"7ux86" | "7u32" | "win7x86-ultimate" )
|
||||||
VERSION="win7x86-ultimate"
|
VERSION="win7x86-ultimate" ;;
|
||||||
;;
|
|
||||||
"7ex86" | "7e32" | "win7x86-enterprise" )
|
"7ex86" | "7e32" | "win7x86-enterprise" )
|
||||||
VERSION="win7x86-enterprise"
|
VERSION="win7x86-enterprise" ;;
|
||||||
;;
|
|
||||||
"vista" | "vs" | "6" | "winvista" | "windowsvista" | "windows vista" )
|
"vista" | "vs" | "6" | "winvista" | "windowsvista" | "windows vista" )
|
||||||
VERSION="winvistax64"
|
VERSION="winvistax64" ;;
|
||||||
;;
|
|
||||||
"vistu" | "vu" | "6u" | "winvistu" )
|
"vistu" | "vu" | "6u" | "winvistu" )
|
||||||
VERSION="winvistax64-ultimate"
|
VERSION="winvistax64-ultimate" ;;
|
||||||
;;
|
|
||||||
"viste" | "ve" | "6e" | "winviste" )
|
"viste" | "ve" | "6e" | "winviste" )
|
||||||
VERSION="winvistax64-enterprise"
|
VERSION="winvistax64-enterprise" ;;
|
||||||
;;
|
|
||||||
"vistax86" | "vista32" | "6x86" | "winvistax86" | "windowsvistax86" )
|
"vistax86" | "vista32" | "6x86" | "winvistax86" | "windowsvistax86" )
|
||||||
VERSION="winvistax86"
|
VERSION="winvistax86" ;;
|
||||||
;;
|
|
||||||
"vux86" | "vu32" | "winvistax86-ultimate" )
|
"vux86" | "vu32" | "winvistax86-ultimate" )
|
||||||
VERSION="winvistax86-ultimate"
|
VERSION="winvistax86-ultimate" ;;
|
||||||
;;
|
|
||||||
"vex86" | "ve32" | "winvistax86-enterprise" )
|
"vex86" | "ve32" | "winvistax86-enterprise" )
|
||||||
VERSION="winvistax86-enterprise"
|
VERSION="winvistax86-enterprise" ;;
|
||||||
;;
|
|
||||||
"xp" | "xp32" | "xpx86" | "5" | "5x86" | "winxp" | "winxp86" | "windowsxp" | "windows xp" )
|
"xp" | "xp32" | "xpx86" | "5" | "5x86" | "winxp" | "winxp86" | "windowsxp" | "windows xp" )
|
||||||
VERSION="winxpx86"
|
VERSION="winxpx86" ;;
|
||||||
;;
|
|
||||||
"xp64" | "xpx64" | "5x64" | "winxp64" | "winxpx64" | "windowsxp64" | "windowsxpx64" )
|
"xp64" | "xpx64" | "5x64" | "winxp64" | "winxpx64" | "windowsxp64" | "windowsxpx64" )
|
||||||
VERSION="winxpx64"
|
VERSION="winxpx64" ;;
|
||||||
;;
|
|
||||||
"2k" | "2000" | "win2k" | "win2000" | "windows2k" | "windows2000" )
|
"2k" | "2000" | "win2k" | "win2000" | "windows2k" | "windows2000" )
|
||||||
VERSION="win2kx86"
|
VERSION="win2kx86" ;;
|
||||||
;;
|
|
||||||
"25" | "2025" | "win25" | "win2025" | "windows2025" | "windows 2025" )
|
"25" | "2025" | "win25" | "win2025" | "windows2025" | "windows 2025" )
|
||||||
VERSION="win2025-eval"
|
VERSION="win2025-eval" ;;
|
||||||
;;
|
|
||||||
"22" | "2022" | "win22" | "win2022" | "windows2022" | "windows 2022" )
|
"22" | "2022" | "win22" | "win2022" | "windows2022" | "windows 2022" )
|
||||||
VERSION="win2022-eval"
|
VERSION="win2022-eval" ;;
|
||||||
;;
|
|
||||||
"19" | "2019" | "win19" | "win2019" | "windows2019" | "windows 2019" )
|
"19" | "2019" | "win19" | "win2019" | "windows2019" | "windows 2019" )
|
||||||
VERSION="win2019-eval"
|
VERSION="win2019-eval" ;;
|
||||||
;;
|
|
||||||
"16" | "2016" | "win16" | "win2016" | "windows2016" | "windows 2016" )
|
"16" | "2016" | "win16" | "win2016" | "windows2016" | "windows 2016" )
|
||||||
VERSION="win2016-eval"
|
VERSION="win2016-eval" ;;
|
||||||
;;
|
|
||||||
"hv" | "hyperv" | "hyper v" | "hyper-v" | "19hv" | "2019hv" | "win2019hv" )
|
"hv" | "hyperv" | "hyper v" | "hyper-v" | "19hv" | "2019hv" | "win2019hv" )
|
||||||
VERSION="win2019-hv"
|
VERSION="win2019-hv" ;;
|
||||||
;;
|
|
||||||
"2012" | "2012r2" | "win2012" | "win2012r2" | "windows2012" | "windows 2012" )
|
"2012" | "2012r2" | "win2012" | "win2012r2" | "windows2012" | "windows 2012" )
|
||||||
VERSION="win2012r2-eval"
|
VERSION="win2012r2-eval" ;;
|
||||||
;;
|
|
||||||
"2008" | "2008r2" | "win2008" | "win2008r2" | "windows2008" | "windows 2008" )
|
"2008" | "2008r2" | "win2008" | "win2008r2" | "windows2008" | "windows 2008" )
|
||||||
VERSION="win2008r2"
|
VERSION="win2008r2" ;;
|
||||||
;;
|
|
||||||
"2003" | "2003r2" | "win2003" | "win2003r2" | "windows2003" | "windows 2003" )
|
"2003" | "2003r2" | "win2003" | "win2003r2" | "windows2003" | "windows 2003" )
|
||||||
VERSION="win2003r2"
|
VERSION="win2003r2" ;;
|
||||||
;;
|
|
||||||
"core11" | "core 11" )
|
"core11" | "core 11" )
|
||||||
VERSION="core11"
|
VERSION="core11" ;;
|
||||||
;;
|
|
||||||
"tiny11" | "tiny 11" )
|
"tiny11" | "tiny 11" )
|
||||||
VERSION="tiny11"
|
VERSION="tiny11" ;;
|
||||||
;;
|
|
||||||
"tiny10" | "tiny 10" )
|
"tiny10" | "tiny 10" )
|
||||||
VERSION="tiny10"
|
VERSION="tiny10" ;;
|
||||||
;;
|
|
||||||
"reactos" | "react os" )
|
"reactos" | "react os" )
|
||||||
VERSION="reactos"
|
VERSION="reactos" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
SUGGEST=$(getSuggestedVersion "$VERSION")
|
SUGGEST=$(getSuggestedVersion "$VERSION")
|
||||||
@@ -172,29 +135,21 @@ getSuggestedVersion() {
|
|||||||
|
|
||||||
case "$id" in
|
case "$id" in
|
||||||
"win10x64" | "win11x64" )
|
"win10x64" | "win11x64" )
|
||||||
echo "$id"
|
echo "$id" ;;
|
||||||
;;
|
|
||||||
"win7x64" | "win7x86" | "winvistax64" | "winvistax86" )
|
"win7x64" | "win7x86" | "winvistax64" | "winvistax86" )
|
||||||
echo "$id-ultimate"
|
echo "$id-ultimate" ;;
|
||||||
;;
|
|
||||||
"tiny10" )
|
"tiny10" )
|
||||||
echo "win10x64-ltsc"
|
echo "win10x64-ltsc" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-ltsc-eval" )
|
*"-enterprise-ltsc-eval" )
|
||||||
echo "${id%-enterprise-ltsc-eval}-ltsc"
|
echo "${id%-enterprise-ltsc-eval}-ltsc" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-iot-eval" )
|
*"-enterprise-iot-eval" )
|
||||||
echo "${id%-enterprise-iot-eval}-iot"
|
echo "${id%-enterprise-iot-eval}-iot" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-ltsc" )
|
*"-enterprise-ltsc" )
|
||||||
echo "${id%-enterprise-ltsc}-ltsc"
|
echo "${id%-enterprise-ltsc}-ltsc" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-iot" )
|
*"-enterprise-iot" )
|
||||||
echo "${id%-enterprise-iot}-iot"
|
echo "${id%-enterprise-iot}-iot" ;;
|
||||||
;;
|
|
||||||
*"-eval" )
|
*"-eval" )
|
||||||
echo "${id%-eval}"
|
echo "${id%-eval}" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -205,11 +160,8 @@ getLanguage() {
|
|||||||
local source="$1"
|
local source="$1"
|
||||||
local input="${1,,}"
|
local input="${1,,}"
|
||||||
local ret="$2"
|
local ret="$2"
|
||||||
local id="$source"
|
|
||||||
local lang=""
|
local id="$source" lang="" desc="" short="" culture=""
|
||||||
local desc=""
|
|
||||||
local short=""
|
|
||||||
local culture=""
|
|
||||||
|
|
||||||
case "$input" in
|
case "$input" in
|
||||||
"ar" | "ar-"* | "arabic" | "arab" )
|
"ar" | "ar-"* | "arabic" | "arab" )
|
||||||
@@ -496,14 +448,11 @@ printVariant() {
|
|||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
*"-iot" | *"-iot-eval" )
|
*"-iot" | *"-iot-eval" )
|
||||||
desc+=" IoT"
|
desc+=" IoT" ;;
|
||||||
;;
|
|
||||||
*"-ltsc" | *"-ltsc-eval" )
|
*"-ltsc" | *"-ltsc-eval" )
|
||||||
desc+=" LTSC"
|
desc+=" LTSC" ;;
|
||||||
;;
|
|
||||||
*"-enterprise" | *"-enterprise-eval" )
|
*"-enterprise" | *"-enterprise-eval" )
|
||||||
desc+=" Enterprise"
|
desc+=" Enterprise" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if enabled "$show_eval" && [[ "${id,,}" == *"-eval" ]]; then
|
if enabled "$show_eval" && [[ "${id,,}" == *"-eval" ]]; then
|
||||||
@@ -540,6 +489,7 @@ printEdition() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local desc="$2"
|
local desc="$2"
|
||||||
local show_eval="${3:-N}"
|
local show_eval="${3:-N}"
|
||||||
|
|
||||||
local normalized="${id,,}"
|
local normalized="${id,,}"
|
||||||
local result edition="" suffix=""
|
local result edition="" suffix=""
|
||||||
|
|
||||||
@@ -561,20 +511,15 @@ printEdition() {
|
|||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
"home" )
|
"home" )
|
||||||
edition="Home"
|
edition="Home" ;;
|
||||||
;;
|
|
||||||
"starter" )
|
"starter" )
|
||||||
edition="Starter"
|
edition="Starter" ;;
|
||||||
;;
|
|
||||||
"ultimate" )
|
"ultimate" )
|
||||||
edition="Ultimate"
|
edition="Ultimate" ;;
|
||||||
;;
|
|
||||||
"enterprise" )
|
"enterprise" )
|
||||||
edition="Enterprise"
|
edition="Enterprise" ;;
|
||||||
;;
|
|
||||||
"education" )
|
"education" )
|
||||||
edition="Education"
|
edition="Education" ;;
|
||||||
;;
|
|
||||||
"n" )
|
"n" )
|
||||||
case "$normalized" in
|
case "$normalized" in
|
||||||
"win7"* ) edition="Professional N" ;;
|
"win7"* ) edition="Professional N" ;;
|
||||||
@@ -582,22 +527,18 @@ printEdition() {
|
|||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
"iot" | "enterprise-iot" )
|
"iot" | "enterprise-iot" )
|
||||||
edition="IoT Enterprise LTSC"
|
edition="IoT Enterprise LTSC" ;;
|
||||||
;;
|
|
||||||
"ltsc" | "enterprise-ltsc" )
|
"ltsc" | "enterprise-ltsc" )
|
||||||
edition="Enterprise LTSC"
|
edition="Enterprise LTSC" ;;
|
||||||
;;
|
|
||||||
* )
|
* )
|
||||||
edition=$(formatEdition "$suffix")
|
edition=$(formatEdition "$suffix")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
"winxp"* )
|
"winxp"* )
|
||||||
edition="Professional"
|
edition="Professional" ;;
|
||||||
;;
|
|
||||||
"win2019-hv"* )
|
"win2019-hv"* )
|
||||||
edition="2019"
|
edition="2019" ;;
|
||||||
;;
|
|
||||||
"win20"* )
|
"win20"* )
|
||||||
[[ "$normalized" == *"-"* ]] && suffix="${normalized#*-}"
|
[[ "$normalized" == *"-"* ]] && suffix="${normalized#*-}"
|
||||||
|
|
||||||
@@ -625,10 +566,10 @@ printEdition() {
|
|||||||
|
|
||||||
fromFile() {
|
fromFile() {
|
||||||
|
|
||||||
local id=""
|
|
||||||
local desc="$1"
|
local desc="$1"
|
||||||
local file="${1,,}"
|
local file="${1,,}"
|
||||||
local arch="${PLATFORM,,}"
|
|
||||||
|
local id="" arch="${PLATFORM,,}"
|
||||||
|
|
||||||
file="${file//-/_}"
|
file="${file//-/_}"
|
||||||
file="${file// /_}"
|
file="${file// /_}"
|
||||||
@@ -694,10 +635,11 @@ fromFile() {
|
|||||||
|
|
||||||
fromName() {
|
fromName() {
|
||||||
|
|
||||||
local id=""
|
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local arch="$2"
|
local arch="$2"
|
||||||
|
|
||||||
|
local id=""
|
||||||
|
|
||||||
local add=""
|
local add=""
|
||||||
[[ "$arch" != "x64" ]] && add="$arch"
|
[[ "$arch" != "x64" ]] && add="$arch"
|
||||||
|
|
||||||
@@ -723,9 +665,23 @@ fromName() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isClientEdition() {
|
||||||
|
|
||||||
|
case "${1,,}" in
|
||||||
|
"pro" | "professional" | "business" | \
|
||||||
|
"enterprise" | "ultimate" | "education" | \
|
||||||
|
"home" | "homepremium" | "home-premium" | \
|
||||||
|
"homebasic" | "home-basic" | "starter" | "core" )
|
||||||
|
return 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
normalizeEdition() {
|
normalizeEdition() {
|
||||||
|
|
||||||
local source="${1,,}"
|
local source="${1,,}"
|
||||||
|
|
||||||
local edition
|
local edition
|
||||||
|
|
||||||
source="${source//evaluation/}"
|
source="${source//evaluation/}"
|
||||||
@@ -745,7 +701,7 @@ normalizeEdition() {
|
|||||||
|
|
||||||
normalizeEditionID() {
|
normalizeEditionID() {
|
||||||
|
|
||||||
local edition
|
local edition base
|
||||||
local id="$2"
|
local id="$2"
|
||||||
|
|
||||||
edition=$(normalizeEdition "$1")
|
edition=$(normalizeEdition "$1")
|
||||||
@@ -753,8 +709,21 @@ normalizeEditionID() {
|
|||||||
case "$edition" in
|
case "$edition" in
|
||||||
"pro" | "professional" | "business" )
|
"pro" | "professional" | "business" )
|
||||||
edition="" ;;
|
edition="" ;;
|
||||||
"pro-n" | "pron" | "professional-n" | "professionaln" )
|
"pro-n" | "pron" | "professional-n" | "professionaln" | "business-n" | "businessn" )
|
||||||
edition="n" ;;
|
edition="n" ;;
|
||||||
|
* )
|
||||||
|
if ! isClientEdition "$edition"; then
|
||||||
|
case "$edition" in
|
||||||
|
*"-n" ) base="${edition%-n}" ;;
|
||||||
|
*"n" ) base="${edition%n}" ;;
|
||||||
|
* ) base="" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "$base" ] && isClientEdition "$base"; then
|
||||||
|
edition="$base-n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
@@ -778,6 +747,7 @@ getEditionID() {
|
|||||||
|
|
||||||
local name="${1,,}"
|
local name="${1,,}"
|
||||||
local id="${2,,}"
|
local id="${2,,}"
|
||||||
|
|
||||||
local edition
|
local edition
|
||||||
|
|
||||||
case "$id" in
|
case "$id" in
|
||||||
@@ -813,26 +783,19 @@ normalizeServerEdition() {
|
|||||||
|
|
||||||
case "$edition" in
|
case "$edition" in
|
||||||
"core" | "core-installation" | "server-core-installation" )
|
"core" | "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" )
|
||||||
edition="${edition%-server-core-installation}-core"
|
edition="${edition%-server-core-installation}-core" ;;
|
||||||
;;
|
|
||||||
*"-core-installation" )
|
*"-core-installation" )
|
||||||
edition="${edition%-core-installation}-core"
|
edition="${edition%-core-installation}-core" ;;
|
||||||
;;
|
|
||||||
*"-desktop-experience" )
|
*"-desktop-experience" )
|
||||||
edition="${edition%-desktop-experience}"
|
edition="${edition%-desktop-experience}" ;;
|
||||||
;;
|
|
||||||
*"-server-with-a-gui" )
|
*"-server-with-a-gui" )
|
||||||
edition="${edition%-server-with-a-gui}"
|
edition="${edition%-server-with-a-gui}" ;;
|
||||||
;;
|
|
||||||
*"-full-installation" )
|
*"-full-installation" )
|
||||||
edition="${edition%-full-installation}"
|
edition="${edition%-full-installation}" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
edition="${edition#server-}"
|
edition="${edition#server-}"
|
||||||
@@ -879,6 +842,7 @@ getServerEditionID() {
|
|||||||
|
|
||||||
local name="${1,,}"
|
local name="${1,,}"
|
||||||
local id="${2,,}"
|
local id="${2,,}"
|
||||||
|
|
||||||
local edition
|
local edition
|
||||||
|
|
||||||
case "$id" in
|
case "$id" in
|
||||||
@@ -901,42 +865,36 @@ getServerEditionID() {
|
|||||||
getEditionOrder() {
|
getEditionOrder() {
|
||||||
|
|
||||||
local id="${1,,}"
|
local id="${1,,}"
|
||||||
local result_name="$2"
|
|
||||||
local -n result="$result_name"
|
|
||||||
|
|
||||||
result=()
|
|
||||||
|
|
||||||
case "$id" in
|
case "$id" in
|
||||||
"win20"* )
|
"win20"* )
|
||||||
result=(
|
printf '%s\n' \
|
||||||
"|default|@default"
|
"|default|@default" \
|
||||||
"-datacenter|datacenter|datacenter datacenter-*"
|
"-datacenter|datacenter|datacenter datacenter-*" \
|
||||||
"-datacenter-azure|datacenter|datacenter-azure"
|
"-datacenter-azure|datacenter|datacenter-azure" \
|
||||||
"-enterprise|enterprise|enterprise enterprise-*"
|
"-enterprise|enterprise|enterprise enterprise-*" \
|
||||||
"-web|web|web web-*"
|
"-web|web|web web-*" \
|
||||||
"-foundation|foundation|foundation foundation-*"
|
"-foundation|foundation|foundation foundation-*" \
|
||||||
"-essentials|essentials|essentials essentials-*"
|
"-essentials|essentials|essentials essentials-*" \
|
||||||
"-standard-core|standard-core|standard-core standard-core-*"
|
"-standard-core|standard-core|standard-core standard-core-*" \
|
||||||
"-datacenter-core|datacenter-core|datacenter-core datacenter-core-*"
|
"-datacenter-core|datacenter-core|datacenter-core datacenter-core-*" \
|
||||||
"-datacenter-azure-core|datacenter-core|datacenter-azure-core"
|
"-datacenter-azure-core|datacenter-core|datacenter-azure-core" \
|
||||||
"-enterprise-core|enterprise-core|enterprise-core enterprise-core-*"
|
"-enterprise-core|enterprise-core|enterprise-core enterprise-core-*" \
|
||||||
"-web-core|web-core|web-core web-core-*"
|
"-web-core|web-core|web-core web-core-*" \
|
||||||
"-hv|hv|hv hv-*"
|
"-hv|hv|hv hv-*"
|
||||||
)
|
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
result=(
|
printf '%s\n' \
|
||||||
"-enterprise|enterprise|enterprise enterprise-*"
|
"-enterprise|enterprise|enterprise enterprise-*" \
|
||||||
"-ultimate|ultimate|ultimate ultimate-*"
|
"-ultimate|ultimate|ultimate ultimate-*" \
|
||||||
"|default|@default n pro pro-* professional professional-* business business-*"
|
"|default|@default n pro pro-* professional professional-* business business-*" \
|
||||||
"-iot|iot|iot iot-* enterprise-iot enterprise-iot-*"
|
"-iot|iot|iot iot-* enterprise-iot enterprise-iot-*" \
|
||||||
"-ltsc|ltsc|ltsc ltsc-* enterprise-ltsc enterprise-ltsc-*"
|
"-ltsc|ltsc|ltsc ltsc-* enterprise-ltsc enterprise-ltsc-*" \
|
||||||
"-education|education|education education-* pro-education pro-education-*"
|
"-education|education|education education-* pro-education pro-education-*" \
|
||||||
"-home|home|home home-*"
|
"-home|home|home home-*" \
|
||||||
"-home-premium|home|home-premium home-premium-*"
|
"-home-premium|home|home-premium home-premium-*" \
|
||||||
"-home-basic|home|home-basic home-basic-*"
|
"-home-basic|home|home-basic home-basic-*" \
|
||||||
"-starter|starter|starter starter-*"
|
"-starter|starter|starter starter-*"
|
||||||
)
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -945,9 +903,10 @@ getEditionOrder() {
|
|||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
|
|
||||||
local id edition
|
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local arch="$2"
|
local arch="$2"
|
||||||
|
|
||||||
|
local id edition
|
||||||
local evaluation=""
|
local evaluation=""
|
||||||
|
|
||||||
id=$(fromName "$name" "$arch")
|
id=$(fromName "$name" "$arch")
|
||||||
@@ -1002,16 +961,11 @@ isLegacy() {
|
|||||||
|
|
||||||
switchEdition() {
|
switchEdition() {
|
||||||
|
|
||||||
local -n id="$1"
|
local version="$1"
|
||||||
|
|
||||||
[[ "${id,,}" == *"-eval" ]] || return 1
|
[[ "${version,,}" == *"-eval" ]] || return 1
|
||||||
|
|
||||||
id="${id::-5}"
|
|
||||||
|
|
||||||
if ! enabled "${DETECTED_ORG:-}"; then
|
|
||||||
DETECTED="${SUGGEST:-$id}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
echo "${version::-5}"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,9 +974,8 @@ getMido() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local ret="$3"
|
local ret="$3"
|
||||||
local url=""
|
|
||||||
local sum=""
|
local url="" sum="" size=""
|
||||||
local size=""
|
|
||||||
|
|
||||||
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
||||||
|
|
||||||
@@ -1119,9 +1072,8 @@ getLink1() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local ret="$3"
|
local ret="$3"
|
||||||
local url=""
|
|
||||||
local sum=""
|
local url="" sum="" size=""
|
||||||
local size=""
|
|
||||||
local host="https://dl.bobpony.com/windows"
|
local host="https://dl.bobpony.com/windows"
|
||||||
|
|
||||||
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
||||||
@@ -1258,9 +1210,8 @@ getLink2() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local ret="$3"
|
local ret="$3"
|
||||||
local url=""
|
|
||||||
local sum=""
|
local url="" sum="" size=""
|
||||||
local size=""
|
|
||||||
local host="https://files.dog/MSDN"
|
local host="https://files.dog/MSDN"
|
||||||
|
|
||||||
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
||||||
@@ -1352,9 +1303,8 @@ getLink3() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local ret="$3"
|
local ret="$3"
|
||||||
local url=""
|
|
||||||
local sum=""
|
local url="" sum="" size=""
|
||||||
local size=""
|
|
||||||
local host="https://iso.reactos.org"
|
local host="https://iso.reactos.org"
|
||||||
|
|
||||||
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
||||||
@@ -1381,9 +1331,8 @@ getLink4() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local ret="$3"
|
local ret="$3"
|
||||||
local url=""
|
|
||||||
local sum=""
|
local url="" sum="" size=""
|
||||||
local size=""
|
|
||||||
local host="https://archive.org/download"
|
local host="https://archive.org/download"
|
||||||
|
|
||||||
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
[[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0
|
||||||
@@ -1552,12 +1501,13 @@ getLink4() {
|
|||||||
|
|
||||||
getValue() {
|
getValue() {
|
||||||
|
|
||||||
local val=""
|
|
||||||
local id="$2"
|
local id="$2"
|
||||||
local lang="$3"
|
local lang="$3"
|
||||||
local type="$4"
|
local type="$4"
|
||||||
local func="getLink$1"
|
local func="getLink$1"
|
||||||
|
|
||||||
|
local val=""
|
||||||
|
|
||||||
if [ "$1" -gt 0 ] && [ "$1" -le "$MIRRORS" ]; then
|
if [ "$1" -gt 0 ] && [ "$1" -le "$MIRRORS" ]; then
|
||||||
val=$($func "$id" "$lang" "$type")
|
val=$($func "$id" "$lang" "$type")
|
||||||
fi
|
fi
|
||||||
@@ -1585,6 +1535,7 @@ isMido() {
|
|||||||
|
|
||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
|
|
||||||
local sum
|
local sum
|
||||||
|
|
||||||
disabled "${MIDO:-}" && return 1
|
disabled "${MIDO:-}" && return 1
|
||||||
@@ -1607,8 +1558,7 @@ isESD() {
|
|||||||
"win10${PLATFORM,,}" | \
|
"win10${PLATFORM,,}" | \
|
||||||
"win11${PLATFORM,,}-enterprise" | \
|
"win11${PLATFORM,,}-enterprise" | \
|
||||||
"win10${PLATFORM,,}-enterprise" )
|
"win10${PLATFORM,,}-enterprise" )
|
||||||
return 0
|
return 0 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
@@ -1627,10 +1577,8 @@ validVersion() {
|
|||||||
isESD "$id" "$lang" && return 0
|
isESD "$id" "$lang" && return 0
|
||||||
|
|
||||||
for ((i=1;i<=MIRRORS;i++)); do
|
for ((i=1;i<=MIRRORS;i++)); do
|
||||||
|
|
||||||
url=$(getLink "$i" "$id" "$lang")
|
url=$(getLink "$i" "$id" "$lang")
|
||||||
[ -n "$url" ] && return 0
|
[ -n "$url" ] && return 0
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
+365
-408
File diff suppressed because it is too large
Load Diff
+236
-122
@@ -14,83 +14,138 @@ startWindows() {
|
|||||||
|
|
||||||
if ! hasImage "$ISO"; then
|
if ! hasImage "$ISO"; then
|
||||||
if ! downloadImage "$ISO" "$VERSION" "$LANGUAGE"; then
|
if ! downloadImage "$ISO" "$VERSION" "$LANGUAGE"; then
|
||||||
removeIso "$ISO" && return 68
|
removeIso "$ISO" || :
|
||||||
|
return 68
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local extracted=0
|
|
||||||
local boot="$BOOT"
|
local boot="$BOOT"
|
||||||
local dir="$TMP/unpack"
|
local dir="$TMP/unpack"
|
||||||
|
local handled=0 extracted=0
|
||||||
|
|
||||||
|
selectWindowsImage "$ISO" "$dir" "$boot" || return $?
|
||||||
|
(( handled )) && return 0
|
||||||
|
|
||||||
|
configureMachine "$ISO" "$dir" "$boot" || return $?
|
||||||
|
(( handled )) && return 0
|
||||||
|
|
||||||
|
prepareWindowsImage "$ISO" "$dir" "$boot" || return $?
|
||||||
|
(( handled )) && return 0
|
||||||
|
|
||||||
|
finishInstall "$BOOT" "N" "$boot" || return 100
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
selectWindowsImage() {
|
||||||
|
|
||||||
|
local iso="$1"
|
||||||
|
local dir="$2"
|
||||||
|
local boot="$3"
|
||||||
|
|
||||||
|
local detect_rc=0
|
||||||
|
|
||||||
|
# Known versions already provide the required image metadata.
|
||||||
if resolveImage "$VERSION"; then
|
if resolveImage "$VERSION"; then
|
||||||
|
|
||||||
if ! setImage; then
|
if ! setImage; then
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 70
|
abortInstall "$dir" "$iso" "$boot" || return 70
|
||||||
|
handled=1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if needsExtraction "$DETECTED" "$ISO"; then
|
if ! needsExtraction "$DETECTED" "$iso"; then
|
||||||
if ! extractImage "$ISO" "$dir" "$VERSION"; then
|
return 0
|
||||||
removeIso "$ISO" && return 72
|
|
||||||
fi
|
|
||||||
|
|
||||||
extracted=1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
if ! extractImage "$iso" "$dir" "$VERSION"; then
|
||||||
|
removeIso "$iso" || :
|
||||||
|
return 72
|
||||||
|
fi
|
||||||
|
|
||||||
local detect_rc=0
|
extracted=1
|
||||||
detectIsoImage "$ISO" || detect_rc=$?
|
return 0
|
||||||
|
|
||||||
case "$detect_rc" in
|
|
||||||
|
|
||||||
0 ) ;;
|
|
||||||
1 )
|
|
||||||
|
|
||||||
if ! extractImage "$ISO" "$dir" "$VERSION"; then
|
|
||||||
removeIso "$ISO" && return 74
|
|
||||||
fi
|
|
||||||
|
|
||||||
extracted=1
|
|
||||||
|
|
||||||
if ! detectImage "$dir"; then
|
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 76
|
|
||||||
return 0
|
|
||||||
fi ;;
|
|
||||||
|
|
||||||
* )
|
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 76
|
|
||||||
return 0 ;;
|
|
||||||
|
|
||||||
esac
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local desc
|
# Inspect unknown media directly before falling back to extraction.
|
||||||
if ! desc=$(printVariant "$DETECTED" "$DETECTED"); then
|
detectIsoImage "$iso" || detect_rc=$?
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 78
|
|
||||||
|
if (( detect_rc == 0 )); then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! setMachine "$DETECTED" "$ISO" "$dir" "$desc"; then
|
# Only code 1 indicates that extraction may recover detection.
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 80
|
if (( detect_rc != 1 )); then
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 76
|
||||||
|
handled=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! extractImage "$iso" "$dir" "$VERSION"; then
|
||||||
|
removeIso "$iso" || :
|
||||||
|
return 74
|
||||||
|
fi
|
||||||
|
|
||||||
|
extracted=1
|
||||||
|
|
||||||
|
if detectImage "$dir"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 76
|
||||||
|
handled=1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureMachine() {
|
||||||
|
|
||||||
|
local iso="$1"
|
||||||
|
local dir="$2"
|
||||||
|
local boot="$3"
|
||||||
|
|
||||||
|
local desc
|
||||||
|
|
||||||
|
if ! desc=$(printVariant "$DETECTED" "$DETECTED"); then
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 78
|
||||||
|
handled=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! setMachine "$DETECTED" "$iso" "$dir" "$desc"; then
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 80
|
||||||
|
handled=1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! restoreMachineState; then
|
if ! restoreMachineState; then
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 82
|
abortInstall "$dir" "$iso" "$boot" || return 82
|
||||||
|
handled=1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Direct-boot media skips all unattended installation preparation.
|
||||||
if bootDirect "$DETECTED"; then
|
if bootDirect "$DETECTED"; then
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 83
|
abortInstall "$dir" "$iso" "$boot" || return 83
|
||||||
|
handled=1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if canUseSetupImage "$DETECTED" "$ISO"; then
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareWindowsImage() {
|
||||||
|
|
||||||
|
local iso="$1"
|
||||||
|
local dir="$2"
|
||||||
|
local boot="$3"
|
||||||
|
|
||||||
|
# Prefer the original ISO with a small setup image whenever possible.
|
||||||
|
if canUseSetupImage "$DETECTED" "$iso"; then
|
||||||
|
|
||||||
if ! stageSetup "$XML" "$LANGUAGE" "$TMP/setup"; then
|
if ! stageSetup "$XML" "$LANGUAGE" "$TMP/setup"; then
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 84
|
abortInstall "$dir" "$iso" "$boot" || return 84
|
||||||
|
handled=1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -98,32 +153,33 @@ startWindows() {
|
|||||||
exit 86
|
exit 86
|
||||||
fi
|
fi
|
||||||
|
|
||||||
useOriginalImage "$ISO" || return 88
|
useOriginalImage "$iso" || return 88
|
||||||
|
return 0
|
||||||
else
|
|
||||||
|
|
||||||
if (( ! extracted )); then
|
|
||||||
if ! extractImage "$ISO" "$dir" "$VERSION"; then
|
|
||||||
removeIso "$ISO" && return 90
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! prepareImage "$ISO" "$dir"; then
|
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 92
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! updateImage "$dir" "$XML" "$LANGUAGE"; then
|
|
||||||
abortInstall "$dir" "$ISO" "$boot" || return 94
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
removeImage "$ISO" || return 96
|
|
||||||
buildImage "$dir" || return 98
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
finishInstall "$BOOT" "N" "$boot" || return 100
|
# Legacy or modifiable media must be extracted, updated, and rebuilt.
|
||||||
|
if (( ! extracted )); then
|
||||||
|
if ! extractImage "$iso" "$dir" "$VERSION"; then
|
||||||
|
removeIso "$iso" || :
|
||||||
|
return 90
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! prepareImage "$iso" "$dir"; then
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 92
|
||||||
|
handled=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! updateImage "$dir" "$XML" "$LANGUAGE"; then
|
||||||
|
abortInstall "$dir" "$iso" "$boot" || return 94
|
||||||
|
handled=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
removeImage "$iso" || return 96
|
||||||
|
buildImage "$dir" || return 98
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -218,6 +274,8 @@ startInstall() {
|
|||||||
ISO=$(basename "$BOOT")
|
ISO=$(basename "$BOOT")
|
||||||
ISO="$TMP/$ISO"
|
ISO="$TMP/$ISO"
|
||||||
|
|
||||||
|
# Work from the temporary directory so the persistent source path can
|
||||||
|
# later contain either the preserved ISO or the rebuilt installation image.
|
||||||
if [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
|
if [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
|
||||||
if ! mv -f -- "$BOOT" "$ISO"; then
|
if ! mv -f -- "$BOOT" "$ISO"; then
|
||||||
error "Failed to move ISO file from \"$BOOT\" to \"$ISO\" !"
|
error "Failed to move ISO file from \"$BOOT\" to \"$ISO\" !"
|
||||||
@@ -255,11 +313,16 @@ abortInstall() {
|
|||||||
local dir="$1"
|
local dir="$1"
|
||||||
local iso="$2"
|
local iso="$2"
|
||||||
local boot="$3"
|
local boot="$3"
|
||||||
|
|
||||||
local efi efi32 efi64
|
local efi efi32 efi64
|
||||||
|
|
||||||
|
# Standalone ESD files and nested archives are not directly bootable media,
|
||||||
|
# so they cannot use the manual-install fallback.
|
||||||
[[ "${iso,,}" == *".esd" ]] && exit 60
|
[[ "${iso,,}" == *".esd" ]] && exit 60
|
||||||
enabled "${UNPACK:-}" && exit 60
|
enabled "${UNPACK:-}" && exit 60
|
||||||
|
|
||||||
|
# When automatic preparation fails, inspect extracted media to determine
|
||||||
|
# whether it can still be booted manually using legacy firmware.
|
||||||
if [[ "${PLATFORM,,}" == "x64" ]] && [ -d "$dir" ]; then
|
if [[ "${PLATFORM,,}" == "x64" ]] && [ -d "$dir" ]; then
|
||||||
|
|
||||||
efi=$(find "$dir" -maxdepth 1 -type d -iname efi -print -quit)
|
efi=$(find "$dir" -maxdepth 1 -type d -iname efi -print -quit)
|
||||||
@@ -278,6 +341,8 @@ abortInstall() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Preserve custom media in place. Downloaded or reused media must be moved
|
||||||
|
# back to persistent storage before the manual fallback is started.
|
||||||
if [ -n "$CUSTOM" ]; then
|
if [ -n "$CUSTOM" ]; then
|
||||||
BOOT="$iso"
|
BOOT="$iso"
|
||||||
REMOVE="N"
|
REMOVE="N"
|
||||||
@@ -317,9 +382,12 @@ skipInstall() {
|
|||||||
|
|
||||||
local iso="$1"
|
local iso="$1"
|
||||||
local previousBase="$2"
|
local previousBase="$2"
|
||||||
|
|
||||||
local boot="$STORAGE/windows.boot"
|
local boot="$STORAGE/windows.boot"
|
||||||
|
|
||||||
if [ -n "$previousBase" ]; then
|
if [ -n "$previousBase" ]; then
|
||||||
|
# A changed source invalidates an unfinished installation. Back up an
|
||||||
|
# existing installation, but discard stale media when no disk exists yet.
|
||||||
if [[ "${STORAGE,,}/${previousBase,,}" != "${iso,,}" ]]; then
|
if [[ "${STORAGE,,}/${previousBase,,}" != "${iso,,}" ]]; then
|
||||||
|
|
||||||
if ! hasDisk; then
|
if ! hasDisk; then
|
||||||
@@ -372,6 +440,7 @@ finishInstall() {
|
|||||||
local iso="$1"
|
local iso="$1"
|
||||||
local aborted="$2"
|
local aborted="$2"
|
||||||
local boot="$3"
|
local boot="$3"
|
||||||
|
|
||||||
local base
|
local base
|
||||||
|
|
||||||
if [ ! -s "$iso" ] || [ ! -f "$iso" ]; then
|
if [ ! -s "$iso" ] || [ ! -f "$iso" ]; then
|
||||||
@@ -428,8 +497,9 @@ finishInstall() {
|
|||||||
|
|
||||||
findFile() {
|
findFile() {
|
||||||
|
|
||||||
local dir file base
|
|
||||||
local fname="$1"
|
local fname="$1"
|
||||||
|
|
||||||
|
local dir file base
|
||||||
local boot="$STORAGE/windows.boot"
|
local boot="$STORAGE/windows.boot"
|
||||||
|
|
||||||
dir=$(find / -maxdepth 1 -type d -iname "$fname" -print -quit)
|
dir=$(find / -maxdepth 1 -type d -iname "$fname" -print -quit)
|
||||||
@@ -462,6 +532,8 @@ findFile() {
|
|||||||
|
|
||||||
ISO="$file"
|
ISO="$file"
|
||||||
CUSTOM="$file"
|
CUSTOM="$file"
|
||||||
|
# Include the custom ISO size in its persistent name so replacing a
|
||||||
|
# bind-mounted ISO is detected as a different installation source.
|
||||||
BOOT="$STORAGE/windows.$size.iso"
|
BOOT="$STORAGE/windows.$size.iso"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -501,6 +573,8 @@ needsExtraction() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local iso="$2"
|
local iso="$2"
|
||||||
|
|
||||||
|
# Direct-boot media does not need rebuilding. Legacy/skipped versions,
|
||||||
|
# standalone ESD downloads, and nested archives require full extraction.
|
||||||
bootDirect "$id" && return 1
|
bootDirect "$id" && return 1
|
||||||
|
|
||||||
skipVersion "$id" ||
|
skipVersion "$id" ||
|
||||||
@@ -512,6 +586,7 @@ checkFreeSpace() {
|
|||||||
|
|
||||||
local dir="$1"
|
local dir="$1"
|
||||||
local size="$2"
|
local size="$2"
|
||||||
|
|
||||||
local size_gb space space_gb
|
local size_gb space space_gb
|
||||||
|
|
||||||
size_gb=$(formatBytes "$size")
|
size_gb=$(formatBytes "$size")
|
||||||
@@ -526,16 +601,6 @@ checkFreeSpace() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getEsdField() {
|
|
||||||
|
|
||||||
local list="$1"
|
|
||||||
local index="$2"
|
|
||||||
|
|
||||||
sed -n "${index}p" <<< "$list" | tr -cd '0-9'
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
extractESD() {
|
extractESD() {
|
||||||
|
|
||||||
local iso="$1"
|
local iso="$1"
|
||||||
@@ -543,11 +608,11 @@ extractESD() {
|
|||||||
local version="$3"
|
local version="$3"
|
||||||
local desc="$4"
|
local desc="$4"
|
||||||
|
|
||||||
local info count totals links
|
local bootTotal bootLinks wimTotal wimLinks
|
||||||
local bootTotal bootLinks
|
local installSize size edition imgEdition
|
||||||
local wimTotal wimLinks
|
local bootWim installWim bootSize wimSize
|
||||||
local installSize size
|
local index line ret xml metadata count
|
||||||
local edition imgEdition
|
local -a fields
|
||||||
|
|
||||||
local minSize=100000000
|
local minSize=100000000
|
||||||
local freeSpace=9606127360
|
local freeSpace=9606127360
|
||||||
@@ -579,12 +644,30 @@ extractESD() {
|
|||||||
|
|
||||||
checkFreeSpace "$dir" "$freeSpace" || return 1
|
checkFreeSpace "$dir" "$freeSpace" || return 1
|
||||||
|
|
||||||
info=$(wimlib-imagex info "$iso") || {
|
if ! xml=$(wimlib-imagex info "$iso" --xml 2>/dev/null |
|
||||||
|
iconv -f UTF-16LE -t UTF-8 2>/dev/null); then
|
||||||
error "Cannot read ESD file information!"
|
error "Cannot read ESD file information!"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
|
|
||||||
count=$(awk '/Image Count:/ {print $3}' <<< "$info")
|
# Microsoft download ESDs use images 1-3 for setup media, WinPE, and Windows
|
||||||
|
# Setup; images 4 and higher contain installable editions. Read all metadata
|
||||||
|
# once because repeatedly inspecting a solid-compressed ESD is expensive.
|
||||||
|
if ! metadata=$(xmlstarlet sel -t \
|
||||||
|
-v 'count(/WIM/IMAGE)' -n \
|
||||||
|
-v 'normalize-space(/WIM/IMAGE[@INDEX="1"]/TOTALBYTES)' -n \
|
||||||
|
-v 'normalize-space(/WIM/IMAGE[@INDEX="1"]/HARDLINKBYTES)' -n \
|
||||||
|
-v 'normalize-space(/WIM/IMAGE[@INDEX="3"]/TOTALBYTES)' -n \
|
||||||
|
-v 'normalize-space(/WIM/IMAGE[@INDEX="3"]/HARDLINKBYTES)' -n \
|
||||||
|
-m '/WIM/IMAGE[number(@INDEX) >= 4]' -v '@INDEX' -o $'\t' -v 'DESCRIPTION' -n \
|
||||||
|
<<< "$xml" 2>/dev/null); then
|
||||||
|
error "Cannot read ESD file information!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t fields <<< "$metadata"
|
||||||
|
|
||||||
|
count="${fields[0]:-}"
|
||||||
if [[ ! "$count" =~ ^[0-9]+$ ]]; then
|
if [[ ! "$count" =~ ^[0-9]+$ ]]; then
|
||||||
error "Cannot read the image count in ESD file!"
|
error "Cannot read the image count in ESD file!"
|
||||||
return 1
|
return 1
|
||||||
@@ -595,34 +678,33 @@ extractESD() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
totals=$(grep "Total Bytes:" <<< "$info" || true)
|
bootTotal="${fields[1]:-}"
|
||||||
links=$(grep "Hard Link Bytes:" <<< "$info" || true)
|
bootLinks="${fields[2]:-}"
|
||||||
|
|
||||||
bootTotal=$(getEsdField "$totals" 1)
|
if [[ ! "$bootTotal" =~ ^[0-9]+$ ]] ||
|
||||||
bootLinks=$(getEsdField "$links" 1)
|
[[ ! "$bootLinks" =~ ^[0-9]+$ ]]; then
|
||||||
|
|
||||||
if [[ ! "$bootTotal" =~ ^[0-9]+$ ]] || [[ ! "$bootLinks" =~ ^[0-9]+$ ]]; then
|
|
||||||
error "Cannot read bootdisk size from ESD file!"
|
error "Cannot read bootdisk size from ESD file!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local bootSize=$(( bootTotal - bootLinks ))
|
bootSize=$(( bootTotal - bootLinks ))
|
||||||
|
|
||||||
wimTotal=$(getEsdField "$totals" 3)
|
wimTotal="${fields[3]:-}"
|
||||||
wimLinks=$(getEsdField "$links" 3)
|
wimLinks="${fields[4]:-}"
|
||||||
|
|
||||||
if [[ ! "$wimTotal" =~ ^[0-9]+$ ]] || [[ ! "$wimLinks" =~ ^[0-9]+$ ]]; then
|
if [[ ! "$wimTotal" =~ ^[0-9]+$ ]] ||
|
||||||
|
[[ ! "$wimLinks" =~ ^[0-9]+$ ]]; then
|
||||||
error "Cannot read boot.wim size from ESD file!"
|
error "Cannot read boot.wim size from ESD file!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local wimSize=$(( wimTotal - wimLinks + bootPad ))
|
wimSize=$(( wimTotal - wimLinks + bootPad ))
|
||||||
|
|
||||||
/run/progress.sh "$dir" "$bootSize" "$msg ([P])..." &
|
/run/progress.sh "$dir" "$bootSize" "$msg ([P])..." &
|
||||||
|
|
||||||
local index="1"
|
index="1"
|
||||||
wimlib-imagex apply "$iso" "$index" "$dir" --quiet 2>/dev/null || {
|
wimlib-imagex apply "$iso" "$index" "$dir" --quiet 2>/dev/null || {
|
||||||
local ret=$?
|
ret=$?
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
error "Extracting $desc bootdisk failed ($ret)"
|
error "Extracting $desc bootdisk failed ($ret)"
|
||||||
return 1
|
return 1
|
||||||
@@ -630,8 +712,8 @@ extractESD() {
|
|||||||
|
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
|
|
||||||
local bootWim="$dir/sources/boot.wim"
|
bootWim="$dir/sources/boot.wim"
|
||||||
local installWim="$dir/sources/install.wim"
|
installWim="$dir/sources/install.wim"
|
||||||
|
|
||||||
msg="Extracting $desc environment"
|
msg="Extracting $desc environment"
|
||||||
info "$msg..." && html "$msg..."
|
info "$msg..." && html "$msg..."
|
||||||
@@ -639,8 +721,9 @@ extractESD() {
|
|||||||
index="2"
|
index="2"
|
||||||
/run/progress.sh "$bootWim" "$wimSize" "$msg ([P])..." &
|
/run/progress.sh "$bootWim" "$wimSize" "$msg ([P])..." &
|
||||||
|
|
||||||
wimlib-imagex export "$iso" "$index" "$bootWim" --compress=none --quiet || {
|
wimlib-imagex export "$iso" "$index" "$bootWim" \
|
||||||
local ret=$?
|
--compress=none --quiet || {
|
||||||
|
ret=$?
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
error "Adding WinPE failed ($ret)"
|
error "Adding WinPE failed ($ret)"
|
||||||
return 1
|
return 1
|
||||||
@@ -654,8 +737,9 @@ extractESD() {
|
|||||||
index="3"
|
index="3"
|
||||||
/run/progress.sh "$bootWim" "$wimSize" "$msg ([P])..." &
|
/run/progress.sh "$bootWim" "$wimSize" "$msg ([P])..." &
|
||||||
|
|
||||||
wimlib-imagex export "$iso" "$index" "$bootWim" --compress=none --boot --quiet || {
|
wimlib-imagex export "$iso" "$index" "$bootWim" \
|
||||||
local ret=$?
|
--compress=none --boot --quiet || {
|
||||||
|
ret=$?
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
error "Adding Windows Setup failed ($ret)"
|
error "Adding Windows Setup failed ($ret)"
|
||||||
return 1
|
return 1
|
||||||
@@ -678,18 +762,19 @@ extractESD() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for (( index=4; index<=count; index++ )); do
|
for line in "${fields[@]:5}"; do
|
||||||
|
|
||||||
imgEdition=$(wimlib-imagex info "$iso" "$index" | grep '^Description:' | sed 's/Description:[ \t]*//')
|
IFS=$'\t' read -r index imgEdition <<< "$line"
|
||||||
|
|
||||||
|
[[ ! "$index" =~ ^[0-9]+$ ]] && continue
|
||||||
[[ "${imgEdition,,}" != "${edition,,}" ]] && continue
|
[[ "${imgEdition,,}" != "${edition,,}" ]] && continue
|
||||||
|
|
||||||
installSize=$(stat -c%s "$iso")
|
installSize=$(( size + installPad ))
|
||||||
installSize=$(( installSize + installPad ))
|
|
||||||
|
|
||||||
/run/progress.sh "$installWim" "$installSize" "$msg ([P])..." &
|
/run/progress.sh "$installWim" "$installSize" "$msg ([P])..." &
|
||||||
|
|
||||||
wimlib-imagex export "$iso" "$index" "$installWim" --quiet || {
|
wimlib-imagex export "$iso" "$index" "$installWim" --quiet || {
|
||||||
local ret=$?
|
ret=$?
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
error "Addition of $index to the $desc image failed ($ret)"
|
error "Addition of $index to the $desc image failed ($ret)"
|
||||||
return 1
|
return 1
|
||||||
@@ -710,8 +795,9 @@ extractImage() {
|
|||||||
local iso="$1"
|
local iso="$1"
|
||||||
local dir="$2"
|
local dir="$2"
|
||||||
local version="$3"
|
local version="$3"
|
||||||
local desc="local ISO"
|
|
||||||
local file size
|
local file size
|
||||||
|
local desc="local ISO"
|
||||||
|
|
||||||
if [ -z "$CUSTOM" ]; then
|
if [ -z "$CUSTOM" ]; then
|
||||||
desc="downloaded ISO"
|
desc="downloaded ISO"
|
||||||
@@ -766,6 +852,8 @@ extractImage() {
|
|||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
# UNPACK archives contain another ISO. Extract the nested ISO, then
|
||||||
|
# preserve it as the actual source media for subsequent processing.
|
||||||
file=$(find "$dir" -maxdepth 1 -type f -iname "*.iso" -print -quit)
|
file=$(find "$dir" -maxdepth 1 -type f -iname "*.iso" -print -quit)
|
||||||
|
|
||||||
if [ -z "$file" ]; then
|
if [ -z "$file" ]; then
|
||||||
@@ -799,7 +887,11 @@ setMachine() {
|
|||||||
local dir="$3"
|
local dir="$3"
|
||||||
local desc="$4"
|
local desc="$4"
|
||||||
|
|
||||||
ETFS="boot/etfsboot.com"
|
if [[ "${id,,}" != "win9"* ]]; then
|
||||||
|
ETFS="boot/etfsboot.com"
|
||||||
|
else
|
||||||
|
ETFS="[BOOT]/Boot-1.44M.img"
|
||||||
|
fi
|
||||||
|
|
||||||
local version=""
|
local version=""
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
@@ -821,7 +913,7 @@ setMachine() {
|
|||||||
|
|
||||||
writeState "mode" "windows_legacy" || return 1
|
writeState "mode" "windows_legacy" || return 1
|
||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
"win9"* | "win2k"* | "reactos" )
|
"win9"* | "win2k"* | "reactos" )
|
||||||
writeState "vga" "cirrus" || return 1 ;;
|
writeState "vga" "cirrus" || return 1 ;;
|
||||||
* )
|
* )
|
||||||
@@ -903,12 +995,13 @@ prepareImage() {
|
|||||||
|
|
||||||
local iso="$1"
|
local iso="$1"
|
||||||
local dir="$2"
|
local dir="$2"
|
||||||
|
|
||||||
local desc missing
|
local desc missing
|
||||||
|
|
||||||
desc=$(printVariant "$DETECTED" "$DETECTED")
|
desc=$(printVariant "$DETECTED" "$DETECTED")
|
||||||
|
|
||||||
if [[ "${BOOT_MODE,,}" == "windows_legacy" &&
|
# Legacy rebuilt media must retain the source ISO's El Torito boot-load size.
|
||||||
"${DETECTED,,}" != "win9"* ]]; then
|
if [[ "${BOOT_MODE,,}" == "windows_legacy" && "${DETECTED,,}" != "win9"* ]]; then
|
||||||
getBootLoadSize "$iso" "$dir" "$desc" || return 1
|
getBootLoadSize "$iso" "$dir" "$desc" || return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -917,13 +1010,14 @@ prepareImage() {
|
|||||||
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
|
||||||
|
|
||||||
EFISYS="efi/microsoft/boot/efisys_noprompt.bin"
|
EFISYS="efi/microsoft/boot/efisys_noprompt.bin"
|
||||||
|
|
||||||
|
# A modern rebuilt ISO requires both its BIOS and no-prompt UEFI boot images.
|
||||||
[ -f "$dir/$ETFS" ] && [ -s "$dir/$ETFS" ] &&
|
[ -f "$dir/$ETFS" ] && [ -s "$dir/$ETFS" ] &&
|
||||||
[ -f "$dir/$EFISYS" ] && [ -s "$dir/$EFISYS" ] && return 0
|
[ -f "$dir/$EFISYS" ] && [ -s "$dir/$EFISYS" ] && return 0
|
||||||
|
|
||||||
@@ -954,7 +1048,8 @@ addFolder() {
|
|||||||
local target="${2:-image}"
|
local target="${2:-image}"
|
||||||
local log="${3:-Y}"
|
local log="${3:-Y}"
|
||||||
local mode="${4:-copy}"
|
local mode="${4:-copy}"
|
||||||
local folder file="" source=""
|
|
||||||
|
local file="" source="" folder
|
||||||
local dest="$src/\$OEM\$/\$1/OEM"
|
local dest="$src/\$OEM\$/\$1/OEM"
|
||||||
local install="$src/.overlay-install.bat"
|
local install="$src/.overlay-install.bat"
|
||||||
|
|
||||||
@@ -967,6 +1062,8 @@ addFolder() {
|
|||||||
info "$msg" && html "$msg"
|
info "$msg" && html "$msg"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Setup-image mode cannot modify the original ISO, so create a temporary
|
||||||
|
# writable copy of install.bat for the overlay image.
|
||||||
if [ "$mode" = "overlay" ]; then
|
if [ "$mode" = "overlay" ]; then
|
||||||
|
|
||||||
rm -f -- "$install" || return 1
|
rm -f -- "$install" || return 1
|
||||||
@@ -1037,6 +1134,7 @@ addDriver() {
|
|||||||
local path="$2"
|
local path="$2"
|
||||||
local target="$3"
|
local target="$3"
|
||||||
local driver="$4"
|
local driver="$4"
|
||||||
|
|
||||||
local folder="" desc
|
local folder="" desc
|
||||||
|
|
||||||
if [ -z "$id" ]; then
|
if [ -z "$id" ]; then
|
||||||
@@ -1074,8 +1172,7 @@ addDriver() {
|
|||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
"winvista"* )
|
"winvista"* )
|
||||||
[[ "${driver,,}" == "viorng" ]] && return 0
|
[[ "${driver,,}" == "viorng" ]] && return 0 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
local dest="$path/$target/$driver"
|
local dest="$path/$target/$driver"
|
||||||
@@ -1093,6 +1190,7 @@ addDrivers() {
|
|||||||
local file="${4:-}"
|
local file="${4:-}"
|
||||||
local index="${5:-}"
|
local index="${5:-}"
|
||||||
local log="${6:-Y}"
|
local log="${6:-Y}"
|
||||||
|
|
||||||
local drivers="$tmp/drivers"
|
local drivers="$tmp/drivers"
|
||||||
|
|
||||||
rm -rf "$drivers"
|
rm -rf "$drivers"
|
||||||
@@ -1114,6 +1212,7 @@ addDrivers() {
|
|||||||
|
|
||||||
local target="\$WinPEDriver\$"
|
local target="\$WinPEDriver\$"
|
||||||
local dest="$drivers/$target"
|
local dest="$drivers/$target"
|
||||||
|
|
||||||
mkdir -p "$dest" || return 1
|
mkdir -p "$dest" || return 1
|
||||||
|
|
||||||
if [ -n "$file" ]; then
|
if [ -n "$file" ]; then
|
||||||
@@ -1221,12 +1320,13 @@ updateImage() {
|
|||||||
local dir="$1"
|
local dir="$1"
|
||||||
local asset="$2"
|
local asset="$2"
|
||||||
local language="$3"
|
local language="$3"
|
||||||
|
|
||||||
|
local script=""
|
||||||
local tmp="/tmp/install"
|
local tmp="/tmp/install"
|
||||||
local xml="autounattend.xml"
|
local xml="autounattend.xml"
|
||||||
local bak="${xml//.xml/.org}"
|
local bak="${xml//.xml/.org}"
|
||||||
local dat="${xml//.xml/.dat}"
|
local dat="${xml//.xml/.dat}"
|
||||||
local desc path src wim name info
|
local desc path src wim name info
|
||||||
local script=""
|
|
||||||
|
|
||||||
skipVersion "${DETECTED,,}" && return 0
|
skipVersion "${DETECTED,,}" && return 0
|
||||||
|
|
||||||
@@ -1255,6 +1355,7 @@ updateImage() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Windows Setup normally resides in boot image 2; single-image media uses 1.
|
||||||
local idx="1"
|
local idx="1"
|
||||||
|
|
||||||
if ! info=$(wimlib-imagex info -xml "$wim" | iconv -f UTF-16LE -t UTF-8); then
|
if ! info=$(wimlib-imagex info -xml "$wim" | iconv -f UTF-16LE -t UTF-8); then
|
||||||
@@ -1275,6 +1376,8 @@ updateImage() {
|
|||||||
error "Failed to add OEM folder to image!"
|
error "Failed to add OEM folder to image!"
|
||||||
fi
|
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" "/$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" "/$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 extract "$wim" "$idx" "/$bak" "--dest-dir=$tmp" >/dev/null 2>&1; then
|
||||||
@@ -1314,7 +1417,7 @@ updateImage() {
|
|||||||
validateGeneratedXML "$answer" || return 1
|
validateGeneratedXML "$answer" || return 1
|
||||||
|
|
||||||
if [ -z "${CUSTOM_XML:-}" ]; then
|
if [ -z "${CUSTOM_XML:-}" ]; then
|
||||||
prepareSetupScript "$asset" "$tmp/setup" script || exit 84
|
script=$(prepareSetupScript "$asset" "$tmp/setup") || exit 84
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! wimlib-imagex update "$wim" "$idx" --command "add $answer /$xml" > /dev/null; then
|
if ! wimlib-imagex update "$wim" "$idx" --command "add $answer /$xml" > /dev/null; then
|
||||||
@@ -1328,6 +1431,8 @@ updateImage() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Manual mode removes generated automation and restores the original answer
|
||||||
|
# file when one was backed up earlier.
|
||||||
if enabled "$MANUAL"; then
|
if enabled "$MANUAL"; then
|
||||||
|
|
||||||
removeGeneratedXML "$asset" || return 1
|
removeGeneratedXML "$asset" || return 1
|
||||||
@@ -1342,6 +1447,8 @@ updateImage() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Prevent a root-level answer file from overriding the selected automatic or
|
||||||
|
# manual behavior when Windows Setup first boots.
|
||||||
name="$xml"
|
name="$xml"
|
||||||
enabled "$MANUAL" && name="$bak"
|
enabled "$MANUAL" && name="$bak"
|
||||||
path=$(find "$dir" -maxdepth 1 -type f -iname "$name" -print -quit) || return 1
|
path=$(find "$dir" -maxdepth 1 -type f -iname "$name" -print -quit) || return 1
|
||||||
@@ -1392,10 +1499,11 @@ reserveSambaPorts() {
|
|||||||
backup () {
|
backup () {
|
||||||
|
|
||||||
local iso="$1"
|
local iso="$1"
|
||||||
|
|
||||||
local count=1
|
local count=1
|
||||||
local name="unknown"
|
local name="unknown"
|
||||||
local root="$STORAGE/backups"
|
local root="$STORAGE/backups"
|
||||||
local file previous failed=""
|
local failed="" file previous
|
||||||
|
|
||||||
previous=$(readState "base") || return 1
|
previous=$(readState "base") || return 1
|
||||||
[ -n "$previous" ] && name="${previous%.*}"
|
[ -n "$previous" ] && name="${previous%.*}"
|
||||||
@@ -1437,6 +1545,8 @@ backup () {
|
|||||||
-not -iname '*.iso' -print0
|
-not -iname '*.iso' -print0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wait for the process-substitution find command so enumeration failures are
|
||||||
|
# detected rather than being mistaken for a successful backup.
|
||||||
local find_pid=$!
|
local find_pid=$!
|
||||||
|
|
||||||
if ! wait "$find_pid"; then
|
if ! wait "$find_pid"; then
|
||||||
@@ -1461,6 +1571,8 @@ restoreBootMode() {
|
|||||||
|
|
||||||
[ -n "$mode" ] || return 0
|
[ -n "$mode" ] || return 0
|
||||||
|
|
||||||
|
# A saved legacy mode always wins. A saved modern mode only replaces the
|
||||||
|
# default mode and never an explicit user-selected boot configuration.
|
||||||
if [[ "${mode,,}" == "windows_legacy" ]]; then
|
if [[ "${mode,,}" == "windows_legacy" ]]; then
|
||||||
BOOT_MODE="$mode"
|
BOOT_MODE="$mode"
|
||||||
return 0
|
return 0
|
||||||
@@ -1476,6 +1588,8 @@ restoreBootMode() {
|
|||||||
|
|
||||||
restoreMachine() {
|
restoreMachine() {
|
||||||
|
|
||||||
|
# Restore the saved machine only when q35 is still the default; an explicit
|
||||||
|
# user-selected machine must remain untouched.
|
||||||
[[ "${PLATFORM,,}" != "x64" ]] && return 0
|
[[ "${PLATFORM,,}" != "x64" ]] && return 0
|
||||||
[[ "${MACHINE,,}" != "q35" ]] && return 0
|
[[ "${MACHINE,,}" != "q35" ]] && return 0
|
||||||
|
|
||||||
|
|||||||
+215
-154
@@ -6,6 +6,7 @@ handleCurlError() {
|
|||||||
local code="$1"
|
local code="$1"
|
||||||
local server="$2"
|
local server="$2"
|
||||||
local reason="${3:-}"
|
local reason="${3:-}"
|
||||||
|
|
||||||
local signal
|
local signal
|
||||||
|
|
||||||
if [ -n "$reason" ] && (( code <= 125 )); then
|
if [ -n "$reason" ] && (( code <= 125 )); then
|
||||||
@@ -29,8 +30,7 @@ handleCurlError() {
|
|||||||
SEGV | ABRT) error "Curl crashed with signal $signal." ;;
|
SEGV | ABRT) error "Curl crashed with signal $signal." ;;
|
||||||
"") error "Curl terminated with exit status $code." ;;
|
"") error "Curl terminated with exit status $code." ;;
|
||||||
*) error "Curl terminated due to signal $signal." ;;
|
*) error "Curl terminated due to signal $signal." ;;
|
||||||
esac
|
esac ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
@@ -38,10 +38,9 @@ handleCurlError() {
|
|||||||
|
|
||||||
curlRequest() {
|
curlRequest() {
|
||||||
|
|
||||||
local output="$1"
|
local server="$1"
|
||||||
local server="$2"
|
local agent="$2"
|
||||||
local agent="$3"
|
shift 2
|
||||||
shift 3
|
|
||||||
|
|
||||||
local log reason response
|
local log reason response
|
||||||
|
|
||||||
@@ -50,6 +49,8 @@ curlRequest() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Preserve curl's status under errexit so its stderr can be translated
|
||||||
|
# into a useful error instead of terminating the script immediately.
|
||||||
{
|
{
|
||||||
response=$(LC_ALL=C curl \
|
response=$(LC_ALL=C curl \
|
||||||
--silent \
|
--silent \
|
||||||
@@ -76,10 +77,7 @@ curlRequest() {
|
|||||||
|
|
||||||
rm -f "$log"
|
rm -f "$log"
|
||||||
|
|
||||||
if [ -n "$output" ]; then
|
printf '%s' "$response"
|
||||||
printf -v "$output" '%s' "$response"
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,13 +91,13 @@ downloadWindowsLink() {
|
|||||||
local desc="$6"
|
local desc="$6"
|
||||||
local type="$7"
|
local type="$7"
|
||||||
|
|
||||||
local skuId skuJson
|
|
||||||
local linkJson link
|
|
||||||
local ovData ovTime session
|
|
||||||
local ovToken="" ovTicks=""
|
local ovToken="" ovTicks=""
|
||||||
local profile="606624d44113"
|
local profile="606624d44113"
|
||||||
|
local skuId skuJson linkJson
|
||||||
|
local link ovData ovTime session
|
||||||
|
|
||||||
# uuidgen: For MacOS (installed by default) and other systems (e.g. with no /proc) that don't have a kernel interface for generating random UUIDs
|
# Prefer the Linux kernel UUID source, with uuidgen as a portable fallback
|
||||||
|
# for macOS and systems without /proc.
|
||||||
if ! session=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random); then
|
if ! session=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random); then
|
||||||
error "Failed to generate session ID!"
|
error "Failed to generate session ID!"
|
||||||
return 1
|
return 1
|
||||||
@@ -112,32 +110,32 @@ downloadWindowsLink() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Microsoft download "protection" requires the sessionId to be whitelisted through vlscppe.microsoft.com/tags
|
# Register the session with Microsoft's anti-abuse endpoint before
|
||||||
|
# requesting SKU or download links.
|
||||||
|
|
||||||
local orgId="y6jn8c31"
|
local orgId="y6jn8c31"
|
||||||
local vlsUrl="https://vlscppe.microsoft.com/tags?org_id=$orgId&session_id=$session"
|
local vlsUrl="https://vlscppe.microsoft.com/tags?org_id=$orgId&session_id=$session"
|
||||||
|
|
||||||
enabled "$DEBUG" && echo "Getting Session ID: $session"
|
enabled "$DEBUG" && echo "Getting Session ID: $session"
|
||||||
|
|
||||||
# Permit Session ID
|
curlRequest "Microsoft" "$agent" \
|
||||||
curlRequest "" "Microsoft" "$agent" \
|
|
||||||
--output /dev/null \
|
--output /dev/null \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 100K \
|
--max-filesize 100K \
|
||||||
-- "$vlsUrl" || return 1
|
-- "$vlsUrl" || return 1
|
||||||
|
|
||||||
# Microsoft download "protection" also requires an ov-df.microsoft.com request/reply
|
# Complete Microsoft's ov-df challenge by retrieving a token and timing
|
||||||
# 1) Request mdt.js to get w and rticks. InstanceId is (currently) constant.
|
# value, then returning both with the current timestamp.
|
||||||
|
|
||||||
local instance="560dc9f3-1aa5-4a2f-b63c-9e18f8d0e175"
|
local instance="560dc9f3-1aa5-4a2f-b63c-9e18f8d0e175"
|
||||||
local ovUrl="https://ov-df.microsoft.com/mdt.js?instanceId=$instance&PageId=si&session_id=$session"
|
local ovUrl="https://ov-df.microsoft.com/mdt.js?instanceId=$instance&PageId=si&session_id=$session"
|
||||||
|
|
||||||
enabled "$DEBUG" && echo -n "Getting OV data: "
|
enabled "$DEBUG" && echo -n "Getting OV data: "
|
||||||
|
|
||||||
curlRequest ovData "Microsoft" "$agent" \
|
ovData=$(curlRequest "Microsoft" "$agent" \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 1M \
|
--max-filesize 1M \
|
||||||
-- "$ovUrl" || return 1
|
-- "$ovUrl") || return 1
|
||||||
|
|
||||||
if [[ $ovData =~ [\?\&]w=([A-Fa-f0-9]+) ]]; then
|
if [[ $ovData =~ [\?\&]w=([A-Fa-f0-9]+) ]]; then
|
||||||
ovToken="${BASH_REMATCH[1]}"
|
ovToken="${BASH_REMATCH[1]}"
|
||||||
@@ -156,14 +154,12 @@ downloadWindowsLink() {
|
|||||||
|
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
|
|
||||||
# 2) Send a reply with session ID, current epoch and previously retrieved w and rticks
|
|
||||||
|
|
||||||
ovTime=$(date +%s%3N)
|
ovTime=$(date +%s%3N)
|
||||||
ovUrl="https://ov-df.microsoft.com/?session_id=$session&CustomerId=$instance&PageId=si&w=$ovToken&mdt=$ovTime&rticks=$ovTicks"
|
ovUrl="https://ov-df.microsoft.com/?session_id=$session&CustomerId=$instance&PageId=si&w=$ovToken&mdt=$ovTime&rticks=$ovTicks"
|
||||||
|
|
||||||
enabled "$DEBUG" && echo "Sending OV reply: $instance"
|
enabled "$DEBUG" && echo "Sending OV reply: $instance"
|
||||||
|
|
||||||
curlRequest "" "Microsoft" "$agent" \
|
curlRequest "Microsoft" "$agent" \
|
||||||
--output /dev/null \
|
--output /dev/null \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 100K \
|
--max-filesize 100K \
|
||||||
@@ -173,12 +169,14 @@ downloadWindowsLink() {
|
|||||||
|
|
||||||
local skuUrl="https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=$profile&ProductEditionId=$productId&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
local skuUrl="https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=$profile&ProductEditionId=$productId&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
||||||
|
|
||||||
curlRequest skuJson "Microsoft" "$agent" \
|
skuJson=$(curlRequest "Microsoft" "$agent" \
|
||||||
--referer "$url" \
|
--referer "$url" \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 100K \
|
--max-filesize 100K \
|
||||||
-- "$skuUrl" || return 1
|
-- "$skuUrl") || return 1
|
||||||
|
|
||||||
|
# Guard jq under errexit so malformed API data can be handled as a normal
|
||||||
|
# missing-result error. The same pattern is reused for the link response.
|
||||||
{ skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null; local rc=$?; } || :
|
{ skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null; local rc=$?; } || :
|
||||||
|
|
||||||
if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]] || (( rc != 0 )); then
|
if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]] || (( rc != 0 )); then
|
||||||
@@ -190,19 +188,18 @@ downloadWindowsLink() {
|
|||||||
enabled "$DEBUG" && echo "$skuId"
|
enabled "$DEBUG" && echo "$skuId"
|
||||||
enabled "$DEBUG" && echo "Getting ISO download link..."
|
enabled "$DEBUG" && echo "Getting ISO download link..."
|
||||||
|
|
||||||
# Get ISO download link
|
# Microsoft normally applies request or IP blocking on this final connector
|
||||||
# If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)
|
# call rather than during the preceding session setup.
|
||||||
|
|
||||||
local linkUrl="https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=$profile&ProductEditionId=undefined&SKU=$skuId&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
local linkUrl="https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=$profile&ProductEditionId=undefined&SKU=$skuId&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
||||||
|
|
||||||
curlRequest linkJson "Microsoft" "$agent" \
|
linkJson=$(curlRequest "Microsoft" "$agent" \
|
||||||
--referer "$url" \
|
--referer "$url" \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 100K \
|
--max-filesize 100K \
|
||||||
-- "$linkUrl" || return 1
|
-- "$linkUrl") || return 1
|
||||||
|
|
||||||
if ! [ "$linkJson" ]; then
|
if ! [ "$linkJson" ]; then
|
||||||
# This should only happen if there's been some change to how this API works
|
|
||||||
error "Microsoft servers gave us an empty response to our request for an automated download."
|
error "Microsoft servers gave us an empty response to our request for an automated download."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -235,9 +232,8 @@ downloadWindows() {
|
|||||||
local lang="$2"
|
local lang="$2"
|
||||||
local desc="$3"
|
local desc="$3"
|
||||||
|
|
||||||
local agent language
|
local agent language page
|
||||||
local page productId
|
local productId type winVer
|
||||||
local type winVer
|
|
||||||
|
|
||||||
agent=$(getAgent)
|
agent=$(getAgent)
|
||||||
language=$(getLanguage "$lang" "name")
|
language=$(getLanguage "$lang" "name")
|
||||||
@@ -246,22 +242,18 @@ downloadWindows() {
|
|||||||
"win10x64" )
|
"win10x64" )
|
||||||
productId="2618"
|
productId="2618"
|
||||||
winVer="10"
|
winVer="10"
|
||||||
type="1"
|
type="1" ;;
|
||||||
;;
|
|
||||||
"win11x64" )
|
"win11x64" )
|
||||||
productId="3321"
|
productId="3321"
|
||||||
winVer="11"
|
winVer="11"
|
||||||
type="1"
|
type="1" ;;
|
||||||
;;
|
|
||||||
"win11arm64" )
|
"win11arm64" )
|
||||||
productId="3324"
|
productId="3324"
|
||||||
winVer="11arm64"
|
winVer="11arm64"
|
||||||
type="2"
|
type="2" ;;
|
||||||
;;
|
|
||||||
* )
|
* )
|
||||||
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
||||||
return 1
|
return 1 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
local url="https://www.microsoft.com/en-us/software-download/windows$winVer"
|
local url="https://www.microsoft.com/en-us/software-download/windows$winVer"
|
||||||
@@ -275,14 +267,16 @@ downloadWindows() {
|
|||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
|
# Product edition IDs can change. If the configured ID fails, recover the
|
||||||
|
# current value from Microsoft's public download page and retry once.
|
||||||
local msg="retrying using a different method..."
|
local msg="retrying using a different method..."
|
||||||
info "Microsoft download request failed, $msg"
|
info "Microsoft download request failed, $msg"
|
||||||
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
||||||
|
|
||||||
curlRequest page "Microsoft" "$agent" \
|
page=$(curlRequest "Microsoft" "$agent" \
|
||||||
--header "Accept:" \
|
--header "Accept:" \
|
||||||
--max-filesize 1M \
|
--max-filesize 1M \
|
||||||
-- "$url" || return 1
|
-- "$url") || return 1
|
||||||
|
|
||||||
enabled "$DEBUG" && echo -n "Getting Product edition ID: "
|
enabled "$DEBUG" && echo -n "Getting Product edition ID: "
|
||||||
productId=$(printf '%s' "$page" |
|
productId=$(printf '%s' "$page" |
|
||||||
@@ -310,6 +304,7 @@ downloadWindowsEval() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local desc="$3"
|
local desc="$3"
|
||||||
|
|
||||||
local culture compare type
|
local culture compare type
|
||||||
local agent language winVer
|
local agent language winVer
|
||||||
|
|
||||||
@@ -354,13 +349,12 @@ downloadWindowsEval() {
|
|||||||
|
|
||||||
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
||||||
|
|
||||||
curlRequest page "Microsoft" "$agent" \
|
page=$(curlRequest "Microsoft" "$agent" \
|
||||||
--location \
|
--location \
|
||||||
--max-filesize 1M \
|
--max-filesize 1M \
|
||||||
-- "$url" || return 1
|
-- "$url") || return 1
|
||||||
|
|
||||||
if ! [ "$page" ]; then
|
if ! [ "$page" ]; then
|
||||||
# This should only happen if there's been some change to where this download page is located
|
|
||||||
error "Windows server download page gave us an empty response"
|
error "Windows server download page gave us an empty response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -375,7 +369,8 @@ downloadWindowsEval() {
|
|||||||
grep -Eio "https://go\.microsoft\.com/fwlink(/p)?/\?[^\"'<>[:space:]]+" |
|
grep -Eio "https://go\.microsoft\.com/fwlink(/p)?/\?[^\"'<>[:space:]]+" |
|
||||||
grep -Ei '(^|[?&])culture='"${culture,,}"'(&|$)' |
|
grep -Ei '(^|[?&])culture='"${culture,,}"'(&|$)' |
|
||||||
grep -Ei '(^|[?&])country='"${country,,}"'(&|$)') || {
|
grep -Ei '(^|[?&])country='"${country,,}"'(&|$)') || {
|
||||||
# This should only happen if there's been some change to the download endpoint web address
|
# Distinguish a changed or missing English page from an unavailable
|
||||||
|
# translation for an otherwise supported product.
|
||||||
if [[ "${lang,,}" == "en" || "${lang,,}" == "en-"* ]]; then
|
if [[ "${lang,,}" == "en" || "${lang,,}" == "en-"* ]]; then
|
||||||
error "Windows server download page gave us no download link!"
|
error "Windows server download page gave us no download link!"
|
||||||
else
|
else
|
||||||
@@ -385,6 +380,8 @@ downloadWindowsEval() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Evaluation pages currently expose several matching fwlinks in a known
|
||||||
|
# product/platform order, so select the entry for the requested variant.
|
||||||
case "$type" in
|
case "$type" in
|
||||||
"iot" )
|
"iot" )
|
||||||
case "${PLATFORM,,}" in
|
case "${PLATFORM,,}" in
|
||||||
@@ -420,19 +417,21 @@ downloadWindowsEval() {
|
|||||||
|
|
||||||
[ -z "$link" ] && error "Could not parse download link from page!" && return 1
|
[ -z "$link" ] && error "Could not parse download link from page!" && return 1
|
||||||
|
|
||||||
# Follow redirect so proceeding log message is useful
|
# Resolve the fwlink now so later logging and platform validation use the
|
||||||
# This is a request we make that Fido doesn't
|
# actual ISO URL rather than Microsoft's generic redirect.
|
||||||
|
|
||||||
curlRequest link "Microsoft" "$agent" \
|
link=$(curlRequest "Microsoft" "$agent" \
|
||||||
--location \
|
--location \
|
||||||
--output /dev/null \
|
--output /dev/null \
|
||||||
--write-out "%{url_effective}" \
|
--write-out "%{url_effective}" \
|
||||||
--head \
|
--head \
|
||||||
-- "$link" || return 1
|
-- "$link") || return 1
|
||||||
|
|
||||||
local lower="${link,,}"
|
local lower="${link,,}"
|
||||||
local separator='(^|[[:space:]_./-])'
|
local separator='(^|[[:space:]_./-])'
|
||||||
|
|
||||||
|
# Guard against page-order changes resolving to the wrong architecture
|
||||||
|
# before downloading a multi-gigabyte image.
|
||||||
case "${PLATFORM,,}" in
|
case "${PLATFORM,,}" in
|
||||||
"x64" )
|
"x64" )
|
||||||
if [[ "$lower" =~ ${separator}(arm64|a64) ]]; then
|
if [[ "$lower" =~ ${separator}(arm64|a64) ]]; then
|
||||||
@@ -450,6 +449,8 @@ downloadWindowsEval() {
|
|||||||
fi ;;
|
fi ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# During debug verification, compare the resolved filename with the static
|
||||||
|
# catalog entry to expose unexpected changes on Microsoft's page.
|
||||||
if enabled "$DEBUG" && enabled "$VERIFY" && [[ "${lang,,}" == "en"* ]]; then
|
if enabled "$DEBUG" && enabled "$VERIFY" && [[ "${lang,,}" == "en"* ]]; then
|
||||||
|
|
||||||
compare=$(getMido "$id" "$lang" "")
|
compare=$(getMido "$id" "$lang" "")
|
||||||
@@ -494,14 +495,11 @@ getMidoDetected() {
|
|||||||
# Derive the normal answer-file identity from the requested download route.
|
# Derive the normal answer-file identity from the requested download route.
|
||||||
case "$default" in
|
case "$default" in
|
||||||
*"-enterprise-ltsc-eval" )
|
*"-enterprise-ltsc-eval" )
|
||||||
default="${default%-enterprise-ltsc-eval}-ltsc"
|
default="${default%-enterprise-ltsc-eval}-ltsc" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-iot-eval" )
|
*"-enterprise-iot-eval" )
|
||||||
default="${default%-enterprise-iot-eval}-iot"
|
default="${default%-enterprise-iot-eval}-iot" ;;
|
||||||
;;
|
|
||||||
*"-eval" )
|
*"-eval" )
|
||||||
default="${default%-eval}"
|
default="${default%-eval}" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Preserve a genuinely different DETECTED override.
|
# Preserve a genuinely different DETECTED override.
|
||||||
@@ -513,17 +511,13 @@ getMidoDetected() {
|
|||||||
# Select the answer-file identity for the source that actually succeeded.
|
# Select the answer-file identity for the source that actually succeeded.
|
||||||
case "$source" in
|
case "$source" in
|
||||||
*"-enterprise-ltsc-eval" )
|
*"-enterprise-ltsc-eval" )
|
||||||
detected="${source%-enterprise-ltsc-eval}-ltsc-eval"
|
detected="${source%-enterprise-ltsc-eval}-ltsc-eval" ;;
|
||||||
;;
|
|
||||||
*"-enterprise-iot-eval" )
|
*"-enterprise-iot-eval" )
|
||||||
detected="${source%-enterprise-iot-eval}-iot-eval"
|
detected="${source%-enterprise-iot-eval}-iot-eval" ;;
|
||||||
;;
|
|
||||||
*"-eval" )
|
*"-eval" )
|
||||||
detected="$source"
|
detected="$source" ;;
|
||||||
;;
|
|
||||||
* )
|
* )
|
||||||
detected="${current:-$default}"
|
detected="${current:-$default}" ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "$detected"
|
echo "$detected"
|
||||||
@@ -535,21 +529,21 @@ downloadWindowsLtsc() {
|
|||||||
local id="$1"
|
local id="$1"
|
||||||
local lang="$2"
|
local lang="$2"
|
||||||
local desc="$3"
|
local desc="$3"
|
||||||
|
|
||||||
local alternate alternate_desc
|
local alternate alternate_desc
|
||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
"win11${PLATFORM,,}-enterprise-iot-eval" )
|
"win11${PLATFORM,,}-enterprise-iot-eval" )
|
||||||
alternate="win11${PLATFORM,,}-enterprise-ltsc-eval"
|
alternate="win11${PLATFORM,,}-enterprise-ltsc-eval" ;;
|
||||||
;;
|
|
||||||
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
||||||
alternate="win11${PLATFORM,,}-enterprise-iot-eval"
|
alternate="win11${PLATFORM,,}-enterprise-iot-eval" ;;
|
||||||
;;
|
|
||||||
* )
|
* )
|
||||||
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
||||||
return 1
|
return 1 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# IoT and LTSC share related evaluation sources and may become unavailable
|
||||||
|
# independently, so use the sibling edition as a compatibility fallback.
|
||||||
if downloadWindowsEval "$id" "$lang" "$desc" > /dev/null 2>&1; then
|
if downloadWindowsEval "$id" "$lang" "$desc" > /dev/null 2>&1; then
|
||||||
MIDO_SOURCE="$id"
|
MIDO_SOURCE="$id"
|
||||||
return 0
|
return 0
|
||||||
@@ -574,6 +568,7 @@ getWindows() {
|
|||||||
local lang="$2"
|
local lang="$2"
|
||||||
local desc="$3"
|
local desc="$3"
|
||||||
local web_desc="$4"
|
local web_desc="$4"
|
||||||
|
|
||||||
local language edition
|
local language edition
|
||||||
|
|
||||||
MIDO_SOURCE=""
|
MIDO_SOURCE=""
|
||||||
@@ -586,6 +581,8 @@ getWindows() {
|
|||||||
local web_msg="Requesting $web_desc from the Microsoft servers..."
|
local web_msg="Requesting $web_desc from the Microsoft servers..."
|
||||||
info "$msg" && html "$web_msg"
|
info "$msg" && html "$web_msg"
|
||||||
|
|
||||||
|
# These sources are only published in English, so avoid trying download
|
||||||
|
# routes that cannot satisfy the requested language.
|
||||||
case "${version,,}" in
|
case "${version,,}" in
|
||||||
"win2008r2"* | \
|
"win2008r2"* | \
|
||||||
"win81${PLATFORM,,}"* | \
|
"win81${PLATFORM,,}"* | \
|
||||||
@@ -598,6 +595,8 @@ getWindows() {
|
|||||||
fi ;;
|
fi ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# ARM64 downloads exist only for the explicitly supported Windows 11
|
||||||
|
# routes; all other catalog entries remain x64-only.
|
||||||
case "${version,,}" in
|
case "${version,,}" in
|
||||||
"win10x64" ) ;;
|
"win10x64" ) ;;
|
||||||
"win11${PLATFORM,,}" ) ;;
|
"win11${PLATFORM,,}" ) ;;
|
||||||
@@ -610,6 +609,8 @@ getWindows() {
|
|||||||
fi ;;
|
fi ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Prefer live Microsoft download routes. Unsupported or failed live routes
|
||||||
|
# fall through to the configured static catalog below.
|
||||||
case "${version,,}" in
|
case "${version,,}" in
|
||||||
"win10x64" | "win11${PLATFORM,,}" )
|
"win10x64" | "win11${PLATFORM,,}" )
|
||||||
|
|
||||||
@@ -621,8 +622,7 @@ getWindows() {
|
|||||||
"win11${PLATFORM,,}-enterprise-iot-eval" | \
|
"win11${PLATFORM,,}-enterprise-iot-eval" | \
|
||||||
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
||||||
|
|
||||||
downloadWindowsLtsc "$version" "$lang" "$edition" && return 0
|
downloadWindowsLtsc "$version" "$lang" "$edition" && return 0 ;;
|
||||||
;;
|
|
||||||
|
|
||||||
"win11${PLATFORM,,}-enterprise"* )
|
"win11${PLATFORM,,}-enterprise"* )
|
||||||
|
|
||||||
@@ -643,10 +643,11 @@ getWindows() {
|
|||||||
|
|
||||||
* )
|
* )
|
||||||
error "Invalid VERSION specified, value \"$version\" is not recognized!"
|
error "Invalid VERSION specified, value \"$version\" is not recognized!"
|
||||||
return 1
|
return 1 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Static catalog URLs are the last resort after live Microsoft methods are
|
||||||
|
# unavailable or have failed.
|
||||||
MIDO_URL=$(getMido "$version" "$lang" "")
|
MIDO_URL=$(getMido "$version" "$lang" "")
|
||||||
[ -z "$MIDO_URL" ] && return 1
|
[ -z "$MIDO_URL" ] && return 1
|
||||||
|
|
||||||
@@ -665,11 +666,10 @@ getBuild() {
|
|||||||
|
|
||||||
local id="$1"
|
local id="$1"
|
||||||
local ret="$2"
|
local ret="$2"
|
||||||
local url=""
|
|
||||||
local name=""
|
|
||||||
local build="$3"
|
local build="$3"
|
||||||
local edition=""
|
|
||||||
local file="catalog.xml"
|
local file="catalog.xml"
|
||||||
|
local url="" name="" edition=""
|
||||||
|
|
||||||
case "${id,,}" in
|
case "${id,,}" in
|
||||||
"win11${PLATFORM,,}" )
|
"win11${PLATFORM,,}" )
|
||||||
@@ -695,10 +695,9 @@ getCatalog() {
|
|||||||
|
|
||||||
local id="$1"
|
local id="$1"
|
||||||
local ret="$2"
|
local ret="$2"
|
||||||
local url=""
|
|
||||||
local name=""
|
|
||||||
local edition=""
|
|
||||||
local file="catalog.cab"
|
local file="catalog.cab"
|
||||||
|
local url="" name="" edition=""
|
||||||
|
|
||||||
if [[ "${id,,}" == "win11"* ]] && ! isCompatible; then
|
if [[ "${id,,}" == "win11"* ]] && ! isCompatible; then
|
||||||
# ARMv8.0 cannot run Windows 11 builds 24H2 and up.
|
# ARMv8.0 cannot run Windows 11 builds 24H2 and up.
|
||||||
@@ -728,19 +727,108 @@ getCatalog() {
|
|||||||
"url" ) echo "$url" ;;
|
"url" ) echo "$url" ;;
|
||||||
"file" ) echo "$file" ;;
|
"file" ) echo "$file" ;;
|
||||||
"name" ) echo "$name" ;;
|
"name" ) echo "$name" ;;
|
||||||
"edition" ) echo '[Edition="'"${edition}"'"]' ;;
|
"edition" ) echo "$edition" ;;
|
||||||
*) echo "";;
|
*) echo "";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getXmlTag() {
|
parseESD() {
|
||||||
|
|
||||||
local tag="$1"
|
local xml="$1"
|
||||||
local file="$2"
|
local version="$2"
|
||||||
|
local lang="$3"
|
||||||
|
local desc="$4"
|
||||||
|
local edition="$5"
|
||||||
|
local culture="$6"
|
||||||
|
|
||||||
xmllint --nonet --xpath "//$tag" "$file" 2>/dev/null | sed -E -e "s/<[\/]?$tag>//g" || true
|
local xmlFile="${xml##*/}"
|
||||||
|
local file_path file_sum file_size file_edition
|
||||||
|
local file_culture file_match=0 language_match=0
|
||||||
|
local records architecture language separator=$'\x1f'
|
||||||
|
|
||||||
|
ESD=""
|
||||||
|
ESD_SUM=""
|
||||||
|
ESD_SIZE=""
|
||||||
|
|
||||||
|
# Microsoft catalogs have used different XML namespaces. Match elements by
|
||||||
|
# local name and flatten the catalog once so selection needs no temporary XML.
|
||||||
|
if ! records=$(xmlstarlet sel \
|
||||||
|
-T -t \
|
||||||
|
-m "//*[local-name()='File']" \
|
||||||
|
-v "normalize-space(*[local-name()='Architecture'])" \
|
||||||
|
-o "$separator" \
|
||||||
|
-v "normalize-space(*[local-name()='Edition'])" \
|
||||||
|
-o "$separator" \
|
||||||
|
-v "normalize-space(*[local-name()='LanguageCode'])" \
|
||||||
|
-o "$separator" \
|
||||||
|
-v "normalize-space(*[local-name()='FilePath'])" \
|
||||||
|
-o "$separator" \
|
||||||
|
-v "normalize-space(*[local-name()='Sha1'])" \
|
||||||
|
-o "$separator" \
|
||||||
|
-v "normalize-space(*[local-name()='Size'])" \
|
||||||
|
-n \
|
||||||
|
"$xml" 2>/dev/null); then
|
||||||
|
|
||||||
|
error "Failed to parse $xmlFile!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Track product/platform and language matches separately so failures can
|
||||||
|
# distinguish an unavailable edition from an unavailable translation.
|
||||||
|
while IFS="$separator" read -r \
|
||||||
|
architecture file_edition file_culture \
|
||||||
|
file_path file_sum file_size; do
|
||||||
|
|
||||||
|
[ -n "$architecture$file_path$file_sum$file_size$file_culture$file_edition" ] || continue
|
||||||
|
|
||||||
|
[ "${architecture,,}" = "${PLATFORM,,}" ] || continue
|
||||||
|
|
||||||
|
if [ -n "$edition" ] &&
|
||||||
|
[ "${file_edition,,}" != "${edition,,}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
file_match=1
|
||||||
|
|
||||||
|
[ "${file_culture,,}" = "${culture,,}" ] || continue
|
||||||
|
|
||||||
|
language_match=1
|
||||||
|
ESD="$file_path"
|
||||||
|
ESD_SUM="$file_sum"
|
||||||
|
ESD_SIZE="$file_size"
|
||||||
|
break
|
||||||
|
|
||||||
|
done <<< "$records"
|
||||||
|
|
||||||
|
if (( ! file_match )); then
|
||||||
|
desc=$(printEdition "$version" "$desc" "Y")
|
||||||
|
error "No download link available for $desc!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ! language_match )); then
|
||||||
|
desc=$(printEdition "$version" "$desc" "Y")
|
||||||
|
language=$(getLanguage "$lang" "desc")
|
||||||
|
error "No download in the $language language available for $desc!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ESD" ]; then
|
||||||
|
error "Failed to find ESD URL in $xmlFile!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ESD_SUM" ]; then
|
||||||
|
error "Failed to find ESD checksum in $xmlFile!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ESD_SIZE" ]; then
|
||||||
|
error "Failed to find ESD filesize in $xmlFile!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -751,12 +839,10 @@ getESD() {
|
|||||||
local version="$2"
|
local version="$2"
|
||||||
local lang="$3"
|
local lang="$3"
|
||||||
local desc="$4"
|
local desc="$4"
|
||||||
local file result culture
|
|
||||||
local language edition catalog
|
local file culture log
|
||||||
|
local edition catalog rc=0
|
||||||
local xmlFile="products.xml"
|
local xmlFile="products.xml"
|
||||||
local esdFile="esd_edition.xml"
|
|
||||||
local filterFile="products_filter.xml"
|
|
||||||
local log
|
|
||||||
|
|
||||||
file=$(getCatalog "$version" "file")
|
file=$(getCatalog "$version" "file")
|
||||||
catalog=$(getCatalog "$version" "url")
|
catalog=$(getCatalog "$version" "url")
|
||||||
@@ -783,10 +869,12 @@ getESD() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Preserve wget's status under errexit so its log can provide the actual
|
||||||
|
# server or filesystem failure reason.
|
||||||
{
|
{
|
||||||
LC_ALL=C wget "$catalog" -O "$dir/$file" --no-verbose --timeout=30 \
|
LC_ALL=C wget "$catalog" -O "$dir/$file" --no-verbose --timeout=30 \
|
||||||
--no-http-keep-alive --output-file="$log"
|
--no-http-keep-alive --output-file="$log"
|
||||||
local rc=$?
|
rc=$?
|
||||||
} || :
|
} || :
|
||||||
|
|
||||||
if (( rc != 0 )); then
|
if (( rc != 0 )); then
|
||||||
@@ -813,6 +901,8 @@ getESD() {
|
|||||||
|
|
||||||
rm -f "$log"
|
rm -f "$log"
|
||||||
|
|
||||||
|
# Normal catalogs arrive as CAB archives, while pinned build catalogs are
|
||||||
|
# already XML and only need the common filename.
|
||||||
if [[ "$file" == *".xml" ]]; then
|
if [[ "$file" == *".xml" ]]; then
|
||||||
|
|
||||||
if ! mv -f "$dir/$file" "$dir/$xmlFile"; then
|
if ! mv -f "$dir/$file" "$dir/$xmlFile"; then
|
||||||
@@ -832,61 +922,13 @@ getESD() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "$dir/$xmlFile" ] || [ ! -s "$dir/$xmlFile" ]; then
|
if [ ! -s "$dir/$xmlFile" ]; then
|
||||||
error "Failed to find $xmlFile in $file!"
|
error "Failed to find $xmlFile in $file!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local query='//File[Architecture="'${PLATFORM,,}'"]'"${edition}"''
|
if ! parseESD \
|
||||||
result=$(xmllint --nonet --xpath "${query}" "$dir/$xmlFile" 2>/dev/null || true)
|
"$dir/$xmlFile" "$version" "$lang" "$desc" "$edition" "$culture"; then
|
||||||
|
|
||||||
if [ -z "$result" ]; then
|
|
||||||
|
|
||||||
query='//File[Architecture="'${PLATFORM^^}'"]'"${edition}"''
|
|
||||||
result=$(xmllint --nonet --xpath "${query}" "$dir/$xmlFile" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -z "$result" ]; then
|
|
||||||
desc=$(printEdition "$version" "$desc" "Y")
|
|
||||||
language=$(getLanguage "$lang" "desc")
|
|
||||||
error "No download link available for $desc!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e '<Catalog>' > "$dir/$filterFile"
|
|
||||||
echo "$result" >> "$dir/$filterFile"
|
|
||||||
echo -e '</Catalog>'>> "$dir/$filterFile"
|
|
||||||
|
|
||||||
result=$(xmllint --nonet --xpath "//File[LanguageCode=\"${culture,,}\"]" "$dir/$filterFile" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -z "$result" ]; then
|
|
||||||
desc=$(printEdition "$version" "$desc" "Y")
|
|
||||||
language=$(getLanguage "$lang" "desc")
|
|
||||||
error "No download in the $language language available for $desc!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$result" > "$dir/$esdFile"
|
|
||||||
|
|
||||||
ESD=$(getXmlTag "FilePath" "$dir/$esdFile")
|
|
||||||
|
|
||||||
if [ -z "$ESD" ]; then
|
|
||||||
error "Failed to find ESD URL in $esdFile!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ESD_SUM=$(getXmlTag "Sha1" "$dir/$esdFile")
|
|
||||||
|
|
||||||
if [ -z "$ESD_SUM" ]; then
|
|
||||||
error "Failed to find ESD checksum in $esdFile!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ESD_SIZE=$(getXmlTag "Size" "$dir/$esdFile")
|
|
||||||
|
|
||||||
if [ -z "$ESD_SIZE" ]; then
|
|
||||||
error "Failed to find ESD filesize in $esdFile!"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -898,11 +940,12 @@ isCompressed() {
|
|||||||
|
|
||||||
local url="${1%%\?*}"
|
local url="${1%%\?*}"
|
||||||
|
|
||||||
|
# The ReactOS latest-build endpoint returns an archive without a filename
|
||||||
|
# extension, so recognize its path explicitly.
|
||||||
case "${url,,}" in
|
case "${url,,}" in
|
||||||
*.7z | *.zip | *.rar | *.tar | *.cab | *.cpio | \
|
*.7z | *.zip | *.rar | *.tar | *.cab | *.cpio | \
|
||||||
*.lzh | *.lha | *.xar | */latest-x86-gcc-lin-rel )
|
*.lzh | *.lha | *.xar | */latest-x86-gcc-lin-rel )
|
||||||
return 0
|
return 0 ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
@@ -926,6 +969,9 @@ verifyFile() {
|
|||||||
|
|
||||||
[ -z "$check" ] && return 0
|
[ -z "$check" ] && return 0
|
||||||
enabled "$VERIFY" || return 0
|
enabled "$VERIFY" || return 0
|
||||||
|
|
||||||
|
# Microsoft ESD catalogs publish SHA1, while current mirror metadata normally
|
||||||
|
# uses SHA256; the digest length identifies which algorithm is required.
|
||||||
[[ "${#check}" == "40" ]] && algo="SHA1"
|
[[ "${#check}" == "40" ]] && algo="SHA1"
|
||||||
|
|
||||||
local msg="Verifying downloaded ISO..."
|
local msg="Verifying downloaded ISO..."
|
||||||
@@ -975,10 +1021,13 @@ downloadFile() {
|
|||||||
local desc="$4"
|
local desc="$4"
|
||||||
local web_desc="$5"
|
local web_desc="$5"
|
||||||
local connections="${6:-1}"
|
local connections="${6:-1}"
|
||||||
|
|
||||||
|
local domain dots
|
||||||
local msg="Downloading $web_desc"
|
local msg="Downloading $web_desc"
|
||||||
local console_msg="Downloading $desc"
|
local console_msg="Downloading $desc"
|
||||||
local domain dots
|
|
||||||
|
|
||||||
|
# Keep mirror messages concise by reducing subdomains to the final two
|
||||||
|
# labels, while Microsoft downloads retain the generic description.
|
||||||
domain=$(echo "$url" | awk -F/ '{print $3}')
|
domain=$(echo "$url" | awk -F/ '{print $3}')
|
||||||
dots=$(echo "$domain" | tr -cd '.' | wc -c)
|
dots=$(echo "$domain" | tr -cd '.' | wc -c)
|
||||||
(( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)')
|
(( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)')
|
||||||
@@ -1007,8 +1056,11 @@ tryDownload() {
|
|||||||
local desc="$6"
|
local desc="$6"
|
||||||
local seconds="$7"
|
local seconds="$7"
|
||||||
local web_desc="$8"
|
local web_desc="$8"
|
||||||
|
|
||||||
local total minimum="104857600"
|
local total minimum="104857600"
|
||||||
|
|
||||||
|
# Compressed archives can legitimately be much smaller than the ISO they
|
||||||
|
# contain, so use a lower sanity threshold until extraction.
|
||||||
if isCompressed "$url"; then
|
if isCompressed "$url"; then
|
||||||
minimum="10485760"
|
minimum="10485760"
|
||||||
fi
|
fi
|
||||||
@@ -1059,8 +1111,8 @@ fallbackEnglish() {
|
|||||||
local lang="$3"
|
local lang="$3"
|
||||||
local desc="$4"
|
local desc="$4"
|
||||||
local web_desc="$5"
|
local web_desc="$5"
|
||||||
local culture web_msg
|
|
||||||
|
|
||||||
|
local culture web_msg
|
||||||
local msg="No working download method was found for $desc, falling back to English..."
|
local msg="No working download method was found for $desc, falling back to English..."
|
||||||
info "$msg"
|
info "$msg"
|
||||||
|
|
||||||
@@ -1086,12 +1138,11 @@ downloadImage() {
|
|||||||
local iso="$1"
|
local iso="$1"
|
||||||
local version="$2"
|
local version="$2"
|
||||||
local lang="$3"
|
local lang="$3"
|
||||||
|
|
||||||
local requested="$version"
|
local requested="$version"
|
||||||
local tried="n"
|
local detected="$DETECTED"
|
||||||
local success="n"
|
local tried="n" success="n" seconds="5"
|
||||||
local seconds="5"
|
local i url sum size base language desc web_desc
|
||||||
local detected="$DETECTED"
|
|
||||||
local url sum size base desc web_desc language i
|
|
||||||
|
|
||||||
if [[ "${version,,}" == "http"* ]]; then
|
if [[ "${version,,}" == "http"* ]]; then
|
||||||
|
|
||||||
@@ -1127,6 +1178,8 @@ downloadImage() {
|
|||||||
desc+=" in $language"
|
desc+=" in $language"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Prefer a live Microsoft URL and retry link generation once before moving
|
||||||
|
# on to ESD catalogs or mirrors.
|
||||||
if isMido "$version" "$lang"; then
|
if isMido "$version" "$lang"; then
|
||||||
|
|
||||||
tried="y"
|
tried="y"
|
||||||
@@ -1167,7 +1220,13 @@ downloadImage() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if switchEdition version; then
|
# Some editions share another download route. Update the effective version
|
||||||
|
# before looking up ESD catalogs and mirrors.
|
||||||
|
if version=$(switchEdition "$version"); then
|
||||||
|
|
||||||
|
if ! enabled "${DETECTED_ORG:-}"; then
|
||||||
|
DETECTED="${SUGGEST:-$version}"
|
||||||
|
fi
|
||||||
|
|
||||||
desc=$(printVariant "$DETECTED" "" "Y")
|
desc=$(printVariant "$DETECTED" "" "Y")
|
||||||
web_desc=$(printVariant "$DETECTED" "")
|
web_desc=$(printVariant "$DETECTED" "")
|
||||||
@@ -1196,6 +1255,8 @@ downloadImage() {
|
|||||||
|
|
||||||
if [[ "$success" == "y" ]]; then
|
if [[ "$success" == "y" ]]; then
|
||||||
|
|
||||||
|
# Standalone ESD media requires a different extraction path, so expose
|
||||||
|
# its real extension through the active ISO variable.
|
||||||
ISO="${ISO%.*}.esd"
|
ISO="${ISO%.*}.esd"
|
||||||
|
|
||||||
if tryDownload "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$lang" "$desc" "$seconds" "$web_desc"; then
|
if tryDownload "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$lang" "$desc" "$seconds" "$web_desc"; then
|
||||||
|
|||||||
+43
-1
@@ -23,6 +23,8 @@ bootStatus() {
|
|||||||
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
||||||
local line last recent
|
local line last recent
|
||||||
|
|
||||||
|
# Only inspect output produced after the most recent BIOS boot attempt so
|
||||||
|
# stale failures from an earlier device do not affect the current state.
|
||||||
line=$(grep -nE '^Booting from (Hard Disk|DVD/CD)' "$QEMU_PTY" | tail -1)
|
line=$(grep -nE '^Booting from (Hard Disk|DVD/CD)' "$QEMU_PTY" | tail -1)
|
||||||
[ -z "$line" ] && return 1
|
[ -z "$line" ] && return 1
|
||||||
|
|
||||||
@@ -38,6 +40,9 @@ bootStatus() {
|
|||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# These BIOS messages only describe the failed device attempt. QEMU may
|
||||||
|
# immediately continue with another boot target, so clear pending success
|
||||||
|
# instead of treating them as a terminal failure.
|
||||||
if grep -Fq \
|
if grep -Fq \
|
||||||
-e "Boot failed: not a bootable disk" \
|
-e "Boot failed: not a bootable disk" \
|
||||||
-e "Boot failed: Could not read from CDROM" \
|
-e "Boot failed: Could not read from CDROM" \
|
||||||
@@ -59,6 +64,8 @@ bootStatus() {
|
|||||||
|
|
||||||
local line last recent
|
local line last recent
|
||||||
|
|
||||||
|
# OVMF logs every boot option it tries. Track the newest attempt and only
|
||||||
|
# evaluate messages emitted from that point onward.
|
||||||
line=$(grep -nE \
|
line=$(grep -nE \
|
||||||
'BdsDxe: starting Boot[[:xdigit:]]{4} ' \
|
'BdsDxe: starting Boot[[:xdigit:]]{4} ' \
|
||||||
"$QEMU_PTY" | tail -1)
|
"$QEMU_PTY" | tail -1)
|
||||||
@@ -105,6 +112,8 @@ waitForBoot() {
|
|||||||
|
|
||||||
while isAlive "$pid"; do
|
while isAlive "$pid"; do
|
||||||
|
|
||||||
|
# Send the boot key once, either immediately after the prompt appears or
|
||||||
|
# shortly after firmware starts the DVD when the prompt is not logged.
|
||||||
if (( ! keySent )) && needsBootKey; then
|
if (( ! keySent )) && needsBootKey; then
|
||||||
|
|
||||||
if keyDelay=$(bootKeyDelay); then
|
if keyDelay=$(bootKeyDelay); then
|
||||||
@@ -151,6 +160,9 @@ waitForBoot() {
|
|||||||
|
|
||||||
marker=$(getBootMarker)
|
marker=$(getBootMarker)
|
||||||
|
|
||||||
|
# A firmware boot line alone is not proof that the guest started. Wait
|
||||||
|
# briefly for a more definitive success or failure message, restarting
|
||||||
|
# the grace period whenever firmware begins a different boot attempt.
|
||||||
if [[ "$marker" != "$pendingLine" ]] || (( status != pendingType )); then
|
if [[ "$marker" != "$pendingLine" ]] || (( status != pendingType )); then
|
||||||
pendingLine="$marker"
|
pendingLine="$marker"
|
||||||
pendingType=$status
|
pendingType=$status
|
||||||
@@ -173,6 +185,8 @@ waitForBoot() {
|
|||||||
|
|
||||||
5)
|
5)
|
||||||
|
|
||||||
|
# A failed device attempt is transitional because firmware may continue
|
||||||
|
# with another target. Discard any pending success decision.
|
||||||
pendingType=0
|
pendingType=0
|
||||||
pendingLine=""
|
pendingLine=""
|
||||||
pendingDeadline=0
|
pendingDeadline=0
|
||||||
@@ -213,6 +227,8 @@ legacyBootReady() {
|
|||||||
last="${line#*:}"
|
last="${line#*:}"
|
||||||
recent=$(tail -n +"${line%%:*}" "$QEMU_PTY")
|
recent=$(tail -n +"${line%%:*}" "$QEMU_PTY")
|
||||||
|
|
||||||
|
# ACPI shutdown is safe once BIOS has handed control to the hard disk, unless
|
||||||
|
# the same attempt already produced a known boot failure.
|
||||||
[[ "${last,,}" != "${hard,,}"* ]] && return 1
|
[[ "${last,,}" != "${hard,,}"* ]] && return 1
|
||||||
|
|
||||||
grep -Fq "Loading FreeLoader..." <<< "$recent" && return 0
|
grep -Fq "Loading FreeLoader..." <<< "$recent" && return 0
|
||||||
@@ -226,6 +242,8 @@ legacyBootReady() {
|
|||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
|
|
||||||
|
# The marker means installation completed previously, so shutdown no longer
|
||||||
|
# needs to infer guest readiness from firmware output.
|
||||||
[ -f "$STORAGE/windows.boot" ] && return 0
|
[ -f "$STORAGE/windows.boot" ] && return 0
|
||||||
[ ! -s "$QEMU_PTY" ] && return 1
|
[ ! -s "$QEMU_PTY" ] && return 1
|
||||||
|
|
||||||
@@ -239,6 +257,8 @@ ready() {
|
|||||||
'BdsDxe: starting Boot[[:xdigit:]]{4} ' \
|
'BdsDxe: starting Boot[[:xdigit:]]{4} ' \
|
||||||
"$QEMU_PTY" | tail -1)
|
"$QEMU_PTY" | tail -1)
|
||||||
|
|
||||||
|
# Only a Windows Boot Manager entry loaded from a hard disk proves that setup
|
||||||
|
# has progressed far enough for an ACPI shutdown request to be appropriate.
|
||||||
grep -Eq \
|
grep -Eq \
|
||||||
'BdsDxe: starting Boot[[:xdigit:]]{4} "Windows Boot Manager" from .*HD\(' \
|
'BdsDxe: starting Boot[[:xdigit:]]{4} "Windows Boot Manager" from .*HD\(' \
|
||||||
<<< "$last" && return 0
|
<<< "$last" && return 0
|
||||||
@@ -258,6 +278,8 @@ sendKey() {
|
|||||||
[ ! -S "$ACPI_SOCKET" ] && return 1
|
[ ! -S "$ACPI_SOCKET" ] && return 1
|
||||||
[[ "$delay" != "0" ]] && sleep "$delay"
|
[[ "$delay" != "0" ]] && sleep "$delay"
|
||||||
|
|
||||||
|
# Send all repeats through one monitor connection so timing remains stable
|
||||||
|
# and QEMU receives the sequence as one operation.
|
||||||
if ! output=$(
|
if ! output=$(
|
||||||
{
|
{
|
||||||
for ((i = 1; i <= repeat; i++)); do
|
for ((i = 1; i <= repeat; i++)); do
|
||||||
@@ -272,6 +294,8 @@ sendKey() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The human monitor may return success at the transport level while reporting
|
||||||
|
# a command error in its text response, so inspect that output explicitly.
|
||||||
if grep -Eqi \
|
if grep -Eqi \
|
||||||
-e 'unknown command' \
|
-e 'unknown command' \
|
||||||
-e 'unknown key' \
|
-e 'unknown key' \
|
||||||
@@ -312,11 +336,14 @@ bootKeyDelay() {
|
|||||||
|
|
||||||
[ ! -s "$QEMU_PTY" ] && return 1
|
[ ! -s "$QEMU_PTY" ] && return 1
|
||||||
|
|
||||||
|
# A visible prompt is the safest trigger and should be answered immediately.
|
||||||
if grep -Fq "Press any key to" "$QEMU_PTY"; then
|
if grep -Fq "Press any key to" "$QEMU_PTY"; then
|
||||||
echo 0
|
echo 0
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Some firmware or Windows versions do not log the prompt. In that case wait
|
||||||
|
# briefly after the DVD boot attempt and send several short key presses.
|
||||||
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
|
||||||
grep -Fq "Booting from DVD/CD" "$QEMU_PTY" || return 1
|
grep -Fq "Booting from DVD/CD" "$QEMU_PTY" || return 1
|
||||||
else
|
else
|
||||||
@@ -351,7 +378,8 @@ markWindowsBooted() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove CD-ROM ISO after install
|
# Do not remove installation media until firmware output confirms Windows is
|
||||||
|
# now booting from the installed disk rather than from setup media.
|
||||||
ready || return 0
|
ready || return 0
|
||||||
|
|
||||||
if ! touch "$file"; then
|
if ! touch "$file"; then
|
||||||
@@ -376,6 +404,8 @@ finish() {
|
|||||||
|
|
||||||
local reason=$1 failed=0
|
local reason=$1 failed=0
|
||||||
|
|
||||||
|
# QEMU_END distinguishes an expected shutdown path from an unexpected QEMU
|
||||||
|
# exit carrying the same process status.
|
||||||
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
|
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
|
||||||
failed=1
|
failed=1
|
||||||
fi
|
fi
|
||||||
@@ -412,6 +442,8 @@ abortDuringSetup() {
|
|||||||
|
|
||||||
local code="$1"
|
local code="$1"
|
||||||
|
|
||||||
|
# Before Windows boots from disk, ACPI may be ignored or interpreted by setup
|
||||||
|
# itself. Terminate QEMU directly instead of waiting for a graceful shutdown.
|
||||||
if [[ "${DETECTED,,}" != "reactos" ]] || [ -n "${CUSTOM:-}" ]; then
|
if [[ "${DETECTED,,}" != "reactos" ]] || [ -n "${CUSTOM:-}" ]; then
|
||||||
info "Cannot send ACPI signal during $(app) setup, aborting..."
|
info "Cannot send ACPI signal during $(app) setup, aborting..."
|
||||||
else
|
else
|
||||||
@@ -432,12 +464,16 @@ gracefulShutdown() {
|
|||||||
local sig="$1"
|
local sig="$1"
|
||||||
local pid code
|
local pid code
|
||||||
|
|
||||||
|
# Traps can run in subshells created by pipelines or command substitutions;
|
||||||
|
# only the original shell may coordinate QEMU shutdown.
|
||||||
[[ $BASHPID != "$TRAP_PID" ]] && return
|
[[ $BASHPID != "$TRAP_PID" ]] && return
|
||||||
|
|
||||||
code=$(signalCode "$sig")
|
code=$(signalCode "$sig")
|
||||||
|
|
||||||
if [ -f "$QEMU_END" ]; then
|
if [ -f "$QEMU_END" ]; then
|
||||||
|
|
||||||
|
# A second Ctrl-C during an active shutdown skips the remaining grace period
|
||||||
|
# and lets the shutdown loop force QEMU down immediately.
|
||||||
if (( code == 130 && SHUTDOWN_SIGNAL == code )); then
|
if (( code == 130 && SHUTDOWN_SIGNAL == code )); then
|
||||||
SHUTDOWN_SKIP=1
|
SHUTDOWN_SKIP=1
|
||||||
echo && info "Received SIGINT again, forcing shutdown..."
|
echo && info "Received SIGINT again, forcing shutdown..."
|
||||||
@@ -448,12 +484,16 @@ gracefulShutdown() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Signal handlers must complete their own error handling and cleanup without
|
||||||
|
# errexit terminating the shell partway through the shutdown sequence.
|
||||||
set +e
|
set +e
|
||||||
SHUTDOWN_SIGNAL=$code
|
SHUTDOWN_SIGNAL=$code
|
||||||
|
|
||||||
touch "$QEMU_END"
|
touch "$QEMU_END"
|
||||||
echo && info "Received $sig signal, sending ACPI shutdown signal..."
|
echo && info "Received $sig signal, sending ACPI shutdown signal..."
|
||||||
|
|
||||||
|
# Interactive startup may receive a signal before the PID file appears, so
|
||||||
|
# briefly wait for it there; non-interactive operation fails immediately.
|
||||||
if ! readQemuPid pid; then
|
if ! readQemuPid pid; then
|
||||||
if ! interactive || ! waitQemuPid pid; then
|
if ! interactive || ! waitQemuPid pid; then
|
||||||
warn "QEMU PID file does not exist?"
|
warn "QEMU PID file does not exist?"
|
||||||
@@ -479,6 +519,8 @@ gracefulShutdown() {
|
|||||||
enabled "$SHUTDOWN" || return 0
|
enabled "$SHUTDOWN" || return 0
|
||||||
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
|
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
|
||||||
|
|
||||||
|
# Keep Ctrl-C available to interactive users without installing an unnecessary
|
||||||
|
# SIGINT handler for background/container execution.
|
||||||
if interactive; then
|
if interactive; then
|
||||||
_trap gracefulShutdown SIGINT
|
_trap gracefulShutdown SIGINT
|
||||||
fi
|
fi
|
||||||
|
|||||||
+53
-24
@@ -22,29 +22,32 @@ configureNetwork() {
|
|||||||
|
|
||||||
if enabled "$DHCP"; then
|
if enabled "$DHCP"; then
|
||||||
|
|
||||||
hostname="$UPLINK"
|
SAMBA_HOSTNAME="$UPLINK"
|
||||||
interfaces="$DEV"
|
SAMBA_INTERFACES="$DEV"
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
hostname="host.lan"
|
SAMBA_HOSTNAME="host.lan"
|
||||||
|
|
||||||
|
# User-mode networking has no host bridge to bind to, so expose Samba only
|
||||||
|
# through loopback and let QEMU's forwarding provide guest access.
|
||||||
if isUserMode; then
|
if isUserMode; then
|
||||||
interfaces="lo"
|
SAMBA_INTERFACES="lo"
|
||||||
else
|
else
|
||||||
interfaces="$BRIDGE"
|
SAMBA_INTERFACES="$BRIDGE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${SAMBA_INTERFACE:-}" ]; then
|
if [ -n "${SAMBA_INTERFACE:-}" ]; then
|
||||||
interfaces+=",$SAMBA_INTERFACE"
|
SAMBA_INTERFACES+=",$SAMBA_INTERFACE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
netbios="${hostname%%.*}"
|
# NetBIOS names are limited to 15 visible characters.
|
||||||
netbios="${netbios:0:15}"
|
SAMBA_NETBIOS="${SAMBA_HOSTNAME%%.*}"
|
||||||
|
SAMBA_NETBIOS="${SAMBA_NETBIOS:0:15}"
|
||||||
|
|
||||||
[ -z "$netbios" ] && netbios="host"
|
[ -z "$SAMBA_NETBIOS" ] && SAMBA_NETBIOS="host"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -110,6 +113,8 @@ addShare() {
|
|||||||
empty="Y"
|
empty="Y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The generated fallback share contains only instructions and must never be
|
||||||
|
# writable from the guest.
|
||||||
if [[ "$dir" == "$tmp" ]]; then
|
if [[ "$dir" == "$tmp" ]]; then
|
||||||
|
|
||||||
readonly="Y"
|
readonly="Y"
|
||||||
@@ -118,6 +123,8 @@ addShare() {
|
|||||||
|
|
||||||
readonly="Y"
|
readonly="Y"
|
||||||
|
|
||||||
|
# Test actual write access instead of relying on mount flags or mode bits,
|
||||||
|
# which may not reflect bind-mount and host filesystem restrictions.
|
||||||
elif probe=$(mktemp "$dir/.samba-write-test.XXXXXX" 2>/dev/null); then
|
elif probe=$(mktemp "$dir/.samba-write-test.XXXXXX" 2>/dev/null); then
|
||||||
|
|
||||||
writable="Y"
|
writable="Y"
|
||||||
@@ -127,6 +134,8 @@ addShare() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Empty bind mounts are safe to initialize with shared-directory permissions.
|
||||||
|
# Retry the write probe afterward because the original mode may have blocked it.
|
||||||
elif [[ "$empty" == "Y" ]] && chmod 2777 "$dir" 2>/dev/null; then
|
elif [[ "$empty" == "Y" ]] && chmod 2777 "$dir" 2>/dev/null; then
|
||||||
|
|
||||||
if probe=$(mktemp "$dir/.samba-write-test.XXXXXX" 2>/dev/null); then
|
if probe=$(mktemp "$dir/.samba-write-test.XXXXXX" 2>/dev/null); then
|
||||||
@@ -146,6 +155,7 @@ addShare() {
|
|||||||
|
|
||||||
if [[ "$empty" == "Y" ]]; then
|
if [[ "$empty" == "Y" ]]; then
|
||||||
|
|
||||||
|
# Keep newly created content in the shared group through the setgid bit.
|
||||||
if ! chmod 2777 "$dir"; then
|
if ! chmod 2777 "$dir"; then
|
||||||
error "Failed to set permissions for directory $dir" && return 1
|
error "Failed to set permissions for directory $dir" && return 1
|
||||||
fi
|
fi
|
||||||
@@ -155,6 +165,8 @@ addShare() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Docker commonly creates a missing bind source as root. Transfer an empty
|
||||||
|
# directory to the default non-root owner used for shared content.
|
||||||
if [[ "$owner" == "0" ]]; then
|
if [[ "$owner" == "0" ]]; then
|
||||||
if ! chown "1000:1000" "$dir"; then
|
if ! chown "1000:1000" "$dir"; then
|
||||||
error "Failed to set ownership for directory $dir" && return 1
|
error "Failed to set ownership for directory $dir" && return 1
|
||||||
@@ -165,6 +177,7 @@ addShare() {
|
|||||||
|
|
||||||
elif [[ "$readonly" != "Y" ]]; then
|
elif [[ "$readonly" != "Y" ]]; then
|
||||||
|
|
||||||
|
# Preserve access to non-writable mounts by exporting them read-only.
|
||||||
readonly="Y"
|
readonly="Y"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
@@ -200,20 +213,27 @@ writeConfig() {
|
|||||||
if ! {
|
if ! {
|
||||||
echo "[global]"
|
echo "[global]"
|
||||||
echo " server string = Dockur"
|
echo " server string = Dockur"
|
||||||
echo " netbios name = $netbios"
|
echo " netbios name = $SAMBA_NETBIOS"
|
||||||
echo " workgroup = WORKGROUP"
|
echo " workgroup = WORKGROUP"
|
||||||
echo " interfaces = $interfaces"
|
echo " interfaces = $SAMBA_INTERFACES"
|
||||||
echo " bind interfaces only = yes"
|
echo " bind interfaces only = yes"
|
||||||
echo " security = user"
|
echo " security = user"
|
||||||
echo " guest account = nobody"
|
echo " guest account = nobody"
|
||||||
echo " map to guest = Bad User"
|
echo " map to guest = Bad User"
|
||||||
|
|
||||||
|
# Retain SMB1 negotiation for legacy Windows guests.
|
||||||
echo " server min protocol = NT1"
|
echo " server min protocol = NT1"
|
||||||
|
|
||||||
|
# Allow bind-mounted shares to follow symlinks outside their share root.
|
||||||
echo " follow symlinks = yes"
|
echo " follow symlinks = yes"
|
||||||
echo " wide links = yes"
|
echo " wide links = yes"
|
||||||
echo " unix extensions = no"
|
echo " unix extensions = no"
|
||||||
echo " inherit owner = yes"
|
echo " inherit owner = yes"
|
||||||
echo " create mask = 0666"
|
echo " create mask = 0666"
|
||||||
echo " directory mask = 02777"
|
echo " directory mask = 02777"
|
||||||
|
|
||||||
|
# Perform guest filesystem access as root so bind mounts with differing host
|
||||||
|
# ownership remain usable; share-level read-only checks still apply.
|
||||||
echo " force user = root"
|
echo " force user = root"
|
||||||
echo " force group = root"
|
echo " force group = root"
|
||||||
echo " force create mode = 0666"
|
echo " force create mode = 0666"
|
||||||
@@ -241,11 +261,13 @@ selectPrimaryShare() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
share="/shared"
|
# Prefer explicit root-level bind mounts, then storage-local compatibility
|
||||||
[ ! -d "$share" ] && [ -d "$STORAGE/shared" ] && share="$STORAGE/shared"
|
# paths. When none exist, publish an instructional read-only share.
|
||||||
[ ! -d "$share" ] && [ -d "/data" ] && share="/data"
|
SAMBA_SHARE="/shared"
|
||||||
[ ! -d "$share" ] && [ -d "$STORAGE/data" ] && share="$STORAGE/data"
|
[ ! -d "$SAMBA_SHARE" ] && [ -d "$STORAGE/shared" ] && SAMBA_SHARE="$STORAGE/shared"
|
||||||
[ ! -d "$share" ] && share="$tmp"
|
[ ! -d "$SAMBA_SHARE" ] && [ -d "/data" ] && SAMBA_SHARE="/data"
|
||||||
|
[ ! -d "$SAMBA_SHARE" ] && [ -d "$STORAGE/data" ] && SAMBA_SHARE="$STORAGE/data"
|
||||||
|
[ ! -d "$SAMBA_SHARE" ] && SAMBA_SHARE="$tmp"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -256,6 +278,8 @@ addOptionalShare() {
|
|||||||
local ref="/shared$index"
|
local ref="/shared$index"
|
||||||
local name="Data$index"
|
local name="Data$index"
|
||||||
|
|
||||||
|
# Optional shares are best-effort and must not prevent the primary share or
|
||||||
|
# Samba service from starting.
|
||||||
if [ -d "$ref" ]; then
|
if [ -d "$ref" ]; then
|
||||||
addShare "$ref" "$ref" "$name" "Shared" "$SAMBA_CONFIG" || :
|
addShare "$ref" "$ref" "$name" "Shared" "$SAMBA_CONFIG" || :
|
||||||
elif [ -d "/data$index" ]; then
|
elif [ -d "/data$index" ]; then
|
||||||
@@ -267,13 +291,13 @@ addOptionalShare() {
|
|||||||
|
|
||||||
prepareSambaDirs() {
|
prepareSambaDirs() {
|
||||||
|
|
||||||
# Create directories if missing
|
|
||||||
mkdir -p \
|
mkdir -p \
|
||||||
/var/lib/samba/sysvol \
|
/var/lib/samba/sysvol \
|
||||||
/var/lib/samba/private \
|
/var/lib/samba/private \
|
||||||
/var/lib/samba/bind-dns || return 1
|
/var/lib/samba/bind-dns || return 1
|
||||||
|
|
||||||
# Try to repair Samba permissions
|
# Runtime directories may retain restrictive modes from earlier daemon runs
|
||||||
|
# or package defaults, so repair only the known Samba lock and core paths.
|
||||||
[ -d /run/samba/msg.lock ] && chmod -R 0755 /run/samba/msg.lock 2>/dev/null || :
|
[ -d /run/samba/msg.lock ] && chmod -R 0755 /run/samba/msg.lock 2>/dev/null || :
|
||||||
[ -d /var/log/samba/cores ] && chmod -R 0700 /var/log/samba/cores 2>/dev/null || :
|
[ -d /var/log/samba/cores ] && chmod -R 0700 /var/log/samba/cores 2>/dev/null || :
|
||||||
[ -d /var/cache/samba/msg.lock ] && chmod -R 0755 /var/cache/samba/msg.lock 2>/dev/null || :
|
[ -d /var/cache/samba/msg.lock ] && chmod -R 0755 /var/cache/samba/msg.lock 2>/dev/null || :
|
||||||
@@ -300,6 +324,8 @@ startDaemon() {
|
|||||||
|
|
||||||
rm -f "$log" || :
|
rm -f "$log" || :
|
||||||
|
|
||||||
|
# Keep initialization alive after a daemon startup failure so its log can be
|
||||||
|
# streamed and the actual Samba error remains visible to the user.
|
||||||
if ! "$@"; then
|
if ! "$@"; then
|
||||||
SAMBA_DEBUG="Y"
|
SAMBA_DEBUG="Y"
|
||||||
error "Failed to start $name daemon!"
|
error "Failed to start $name daemon!"
|
||||||
@@ -319,7 +345,6 @@ startSamba() {
|
|||||||
|
|
||||||
startNetbios() {
|
startNetbios() {
|
||||||
|
|
||||||
# Enable NetBIOS on Windows 7 and lower
|
|
||||||
enabled "$DEBUG" && echo "Starting NetBIOS daemon..."
|
enabled "$DEBUG" && echo "Starting NetBIOS daemon..."
|
||||||
|
|
||||||
startDaemon "NetBIOS" "/var/log/samba/log.nmbd" \
|
startDaemon "NetBIOS" "/var/log/samba/log.nmbd" \
|
||||||
@@ -330,11 +355,12 @@ startNetbios() {
|
|||||||
|
|
||||||
startWsddn() {
|
startWsddn() {
|
||||||
|
|
||||||
# Enable Web Service Discovery on Vista and up
|
|
||||||
enabled "$DEBUG" && echo "Starting wsddn daemon..."
|
enabled "$DEBUG" && echo "Starting wsddn daemon..."
|
||||||
|
|
||||||
|
# wsddn accepts one interface, while Samba may bind to an additional
|
||||||
|
# user-supplied interface as well.
|
||||||
startDaemon "wsddn" "/var/log/wsddn.log" \
|
startDaemon "wsddn" "/var/log/wsddn.log" \
|
||||||
wsddn -i "${interfaces%%,*}" -H "$hostname" \
|
wsddn -i "${SAMBA_INTERFACES%%,*}" -H "$SAMBA_HOSTNAME" \
|
||||||
--unixd --log-file=/var/log/wsddn.log --pid-file="$DDN_PID"
|
--unixd --log-file=/var/log/wsddn.log --pid-file="$DDN_PID"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -346,19 +372,22 @@ html "Initializing shared folder..."
|
|||||||
enabled "$DEBUG" && echo "Starting Samba daemon..."
|
enabled "$DEBUG" && echo "Starting Samba daemon..."
|
||||||
|
|
||||||
writeConfig || return 0
|
writeConfig || return 0
|
||||||
|
|
||||||
# Add shared folders
|
|
||||||
selectPrimaryShare || return 0
|
selectPrimaryShare || return 0
|
||||||
|
|
||||||
addShare "$share" "/shared" "Data" "Shared" "$SAMBA_CONFIG" || return 0
|
addShare "$SAMBA_SHARE" "/shared" "Data" "Shared" "$SAMBA_CONFIG" || return 0
|
||||||
addOptionalShare "2" || :
|
addOptionalShare "2" || :
|
||||||
addOptionalShare "3" || :
|
addOptionalShare "3" || :
|
||||||
|
|
||||||
prepareSambaDirs || return 0
|
prepareSambaDirs || return 0
|
||||||
|
|
||||||
startSamba || return 0
|
startSamba || return 0
|
||||||
|
|
||||||
|
# User-mode networking does not expose a LAN interface where discovery
|
||||||
|
# broadcasts would be useful.
|
||||||
isUserMode && return 0
|
isUserMode && return 0
|
||||||
|
|
||||||
|
# Older Windows versions discover shares through NetBIOS, while modern Windows
|
||||||
|
# uses Web Services Discovery.
|
||||||
if [[ "${BOOT_MODE:-}" == "windows_legacy" ]]; then
|
if [[ "${BOOT_MODE:-}" == "windows_legacy" ]]; then
|
||||||
startNetbios || :
|
startNetbios || :
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user