mirror of
https://github.com/dockur/windows.git
synced 2026-08-03 12:37:20 +01:00
feat: Migrate unattended commands from XML to scripts (#2034)
This commit is contained in:
+416
-86
@@ -30,7 +30,7 @@ stageAnswer() {
|
||||
local language="$2"
|
||||
local stage="$3"
|
||||
local answer="$stage/Autounattend.xml"
|
||||
local name
|
||||
local script="" name
|
||||
|
||||
if enabled "$MANUAL"; then
|
||||
removeGeneratedXML "$asset" || return 1
|
||||
@@ -59,9 +59,9 @@ stageAnswer() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! updateDiskID "$answer" "${DISK_TYPE:-}"; then
|
||||
if ! updateDiskID "$answer" "${DISK_TYPE:-}" "setup"; then
|
||||
error "Failed to adjust the Windows installation disk!"
|
||||
return 1
|
||||
exit 85
|
||||
fi
|
||||
|
||||
if ! setConfigurationXML "$answer"; then
|
||||
@@ -71,6 +71,10 @@ stageAnswer() {
|
||||
|
||||
validateGeneratedXML "$answer" || return 1
|
||||
|
||||
if [ -z "${CUSTOM_XML:-}" ]; then
|
||||
prepareSetupScript "$asset" "$stage" script || exit 84
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -332,14 +336,14 @@ updateXML() {
|
||||
if [ -n "$domain" ]; then
|
||||
prepareDomainAccount "$domain" account auth || return 1
|
||||
else
|
||||
updateLocalAccountXML "$asset" || return 1
|
||||
updateLocalAccount "$asset" || return 1
|
||||
fi
|
||||
|
||||
sed -i -E \
|
||||
"s|<PlainText>[^<]*</PlainText>|<PlainText>false</PlainText>|g" \
|
||||
"$asset" || return 1
|
||||
|
||||
updateMembershipXML \
|
||||
updateMembership \
|
||||
"$asset" \
|
||||
"$domain" \
|
||||
"$workgroup" \
|
||||
@@ -347,15 +351,301 @@ updateXML() {
|
||||
"$auth" || return 1
|
||||
|
||||
updateAutologinXML "$asset" || return 1
|
||||
enableLog "$asset" || return 1
|
||||
updateEditionXML "$asset" || return 1
|
||||
updateProductKeyXML "$asset" || return 1
|
||||
removeSharedFolderXML "$asset" || return 1
|
||||
validateGeneratedXML "$asset" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
prepareSetupScript() {
|
||||
|
||||
local asset="$1"
|
||||
local stage="$2"
|
||||
local result_name="$3"
|
||||
local staged=""
|
||||
|
||||
printf -v "$result_name" '%s' ""
|
||||
|
||||
stageSetupScript "$asset" "$stage" staged || return 1
|
||||
[ -n "$staged" ] || return 0
|
||||
|
||||
updateSetupScript "$staged" "$asset" || return 1
|
||||
finalizeSetupScript "$staged" || return 1
|
||||
|
||||
printf -v "$result_name" '%s' "$staged"
|
||||
return 0
|
||||
}
|
||||
|
||||
updateSetupScript() {
|
||||
|
||||
local script="$1"
|
||||
local asset="$2"
|
||||
local domain="${DOMAIN:-}"
|
||||
local user="${USERNAME:-}"
|
||||
local content id
|
||||
|
||||
if [ ! -s "$script" ]; then
|
||||
error "Failed to find staged setup script: $script"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -n "$domain" ]; then
|
||||
removeSetupBlock "$script" "LOCAL_ACCOUNT" || return 1
|
||||
elif [ -n "$user" ]; then
|
||||
validateUsername "$user" "local" || return 1
|
||||
|
||||
id=$(basename "$asset") || return 1
|
||||
id="${id%.*}"
|
||||
|
||||
case "${id,,}" in
|
||||
"win10"* | "win11"* | \
|
||||
"win2016"* | "win2019"* | "win2022"* | "win2025"* )
|
||||
printf -v content '%s\n%s' \
|
||||
'rem Prevent the local user password from expiring.' \
|
||||
"powershell.exe -ExecutionPolicy Unrestricted -NoLogo -NoProfile -NonInteractive Set-LocalUser -Name \"$user\" -PasswordNeverExpires 1"
|
||||
;;
|
||||
* )
|
||||
printf -v content '%s\n%s' \
|
||||
'rem Prevent the local user password from expiring.' \
|
||||
"wmic useraccount where name=\"$user\" set PasswordExpires=false"
|
||||
;;
|
||||
esac
|
||||
|
||||
replaceSetupBlock "$script" "LOCAL_ACCOUNT" "$content" || return 1
|
||||
fi
|
||||
|
||||
enableLog "$script" || return 1
|
||||
updateProductKey "$script" || return 1
|
||||
removeSharedFolder "$script" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
findSetupScript() {
|
||||
|
||||
local asset="$1"
|
||||
local dir name id normal candidate
|
||||
local candidates=()
|
||||
|
||||
[ -z "${CUSTOM_XML:-}" ] || return 0
|
||||
[ -n "$asset" ] || return 1
|
||||
|
||||
dir=$(dirname "$asset") || return 1
|
||||
name=$(basename "$asset") || return 1
|
||||
id="${name%.*}"
|
||||
normal="$id"
|
||||
|
||||
candidates+=("$dir/$id.cmd")
|
||||
|
||||
if [[ "${normal,,}" == *"-eval" ]]; then
|
||||
normal="${normal::-5}"
|
||||
candidates+=("$dir/$normal.cmd")
|
||||
fi
|
||||
|
||||
# Generated edition-specific answer files inherit the script belonging to
|
||||
# their generic source template.
|
||||
case "${normal,,}" in
|
||||
"win7"* | "win8"* | "win10"* | "win11"* | "winvista"* | "win20"* )
|
||||
candidates+=("$dir/${normal%%-*}.cmd")
|
||||
;;
|
||||
esac
|
||||
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [ -f "$candidate" ] && [ -s "$candidate" ]; then
|
||||
printf '%s' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
error "Failed to find setup script for answer file: $asset"
|
||||
return 1
|
||||
}
|
||||
|
||||
stageSetupScript() {
|
||||
|
||||
local asset="$1"
|
||||
local stage="$2"
|
||||
local result_name="$3"
|
||||
local source target
|
||||
|
||||
printf -v "$result_name" '%s' ""
|
||||
|
||||
source=$(findSetupScript "$asset") || return 1
|
||||
[ -n "$source" ] || return 0
|
||||
|
||||
target="$stage/\$OEM\$/\$\$/Setup/Scripts/SetupComplete.cmd"
|
||||
|
||||
if ! mkdir -p "$(dirname "$target")"; then
|
||||
error "Failed to create setup script directory!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! cp -L -- "$source" "$target"; then
|
||||
error "Failed to stage setup script: $source"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Work on a normalized copy so marker updates are independent of the line
|
||||
# endings stored in Git. The staged result is converted back to CRLF later.
|
||||
if ! sed -i 's/\r$//' "$target"; then
|
||||
error "Failed to normalize setup script: $target"
|
||||
return 1
|
||||
fi
|
||||
|
||||
validateSetupScript "$target" || return 1
|
||||
|
||||
printf -v "$result_name" '%s' "$target"
|
||||
return 0
|
||||
}
|
||||
|
||||
installSetupScript() {
|
||||
|
||||
local script="$1"
|
||||
local root="$2"
|
||||
local target
|
||||
|
||||
[ -n "$script" ] || return 0
|
||||
|
||||
if [ ! -s "$script" ]; then
|
||||
error "Failed to find staged setup script: $script"
|
||||
return 1
|
||||
fi
|
||||
|
||||
target="$root/\$OEM\$/\$\$/Setup/Scripts/SetupComplete.cmd"
|
||||
|
||||
if ! mkdir -p "$(dirname "$target")"; then
|
||||
error "Failed to create setup script directory!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! cp -f -- "$script" "$target"; then
|
||||
error "Failed to add setup script to Windows image!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
replaceSetupBlock() {
|
||||
|
||||
local file="$1"
|
||||
local block="$2"
|
||||
local content="$3"
|
||||
local begin="rem BEGIN $block"
|
||||
local end="rem END $block"
|
||||
local line inside=0 tmp
|
||||
|
||||
validateSetupBlock "$file" "$block" || return 1
|
||||
|
||||
if ! tmp=$(mktemp "${file}.XXXXXX"); then
|
||||
error "Failed to create temporary setup script!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
|
||||
if [ "$line" = "$begin" ]; then
|
||||
if ! printf '%s\n' "$line" >> "$tmp" ||
|
||||
! printf '%s\n' "$content" >> "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
inside=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$line" = "$end" ]; then
|
||||
inside=0
|
||||
if ! printf '%s\n' "$line" >> "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if (( ! inside )); then
|
||||
if ! printf '%s\n' "$line" >> "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
done < "$file"
|
||||
|
||||
if ! chmod --reference="$file" "$tmp" ||
|
||||
! mv -f -- "$tmp" "$file"; then
|
||||
rm -f "$tmp"
|
||||
error "Failed to replace the $block block in setup script: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
removeSetupBlock() {
|
||||
|
||||
local file="$1"
|
||||
local block="$2"
|
||||
local begin="rem BEGIN $block"
|
||||
local end="rem END $block"
|
||||
local line inside=0 tmp
|
||||
|
||||
validateSetupBlock "$file" "$block" || return 1
|
||||
|
||||
if ! tmp=$(mktemp "${file}.XXXXXX"); then
|
||||
error "Failed to create temporary setup script!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
|
||||
if [ "$line" = "$begin" ]; then
|
||||
inside=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$line" = "$end" ]; then
|
||||
inside=0
|
||||
continue
|
||||
fi
|
||||
|
||||
if (( ! inside )); then
|
||||
if ! printf '%s\n' "$line" >> "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
done < "$file"
|
||||
|
||||
if ! chmod --reference="$file" "$tmp" ||
|
||||
! mv -f -- "$tmp" "$file"; then
|
||||
rm -f "$tmp"
|
||||
error "Failed to remove the $block block from setup script: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
finalizeSetupScript() {
|
||||
|
||||
local file="$1"
|
||||
|
||||
[ -n "$file" ] || return 0
|
||||
if [ ! -s "$file" ]; then
|
||||
error "Failed to find staged setup script: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! unix2dos -q "$file"; then
|
||||
error "Failed to convert setup script to DOS format: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validateGeneratedXML() {
|
||||
|
||||
local asset="$1"
|
||||
@@ -368,6 +658,55 @@ validateGeneratedXML() {
|
||||
return 0
|
||||
}
|
||||
|
||||
validateSetupScript() {
|
||||
|
||||
local file="$1"
|
||||
local block
|
||||
local blocks=(
|
||||
LOCAL_ACCOUNT
|
||||
PRODUCT_KEY
|
||||
SHARED_FOLDER
|
||||
OEM_SCRIPT
|
||||
)
|
||||
|
||||
[ -s "$file" ] || return 1
|
||||
|
||||
for block in "${blocks[@]}"; do
|
||||
validateSetupBlock "$file" "$block" || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validateSetupBlock() {
|
||||
|
||||
local file="$1"
|
||||
local block="$2"
|
||||
local begin="rem BEGIN $block"
|
||||
local end="rem END $block"
|
||||
local begin_count end_count begin_line end_line
|
||||
|
||||
[ -s "$file" ] || return 1
|
||||
|
||||
begin_count=$(grep -Fxc -- "$begin" "$file" || true)
|
||||
end_count=$(grep -Fxc -- "$end" "$file" || true)
|
||||
|
||||
if [ "$begin_count" -ne 1 ] || [ "$end_count" -ne 1 ]; then
|
||||
error "Invalid $block markers in setup script: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
begin_line=$(grep -nFx -- "$begin" "$file" | cut -d: -f1) || return 1
|
||||
end_line=$(grep -nFx -- "$end" "$file" | cut -d: -f1) || return 1
|
||||
|
||||
if [ "$begin_line" -ge "$end_line" ]; then
|
||||
error "Invalid $block marker order in setup script: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validateXMLSettings() {
|
||||
|
||||
validateResolution "WIDTH" "$WIDTH" 320 || return 1
|
||||
@@ -881,7 +1220,7 @@ updateLocaleXML() {
|
||||
return 0
|
||||
}
|
||||
|
||||
updateLocalAccountXML() {
|
||||
updateLocalAccount() {
|
||||
|
||||
local asset="$1"
|
||||
local user="${USERNAME:-}"
|
||||
@@ -892,9 +1231,6 @@ updateLocalAccountXML() {
|
||||
|
||||
if [ -n "$user" ]; then
|
||||
user_xml=$(escapeXMLSed "$user") || return 1
|
||||
|
||||
sed -i "s|-name \"Docker\"|-name \"\$env:USERNAME\"|g" "$asset" || return 1
|
||||
sed -i 's|where name="Docker"|where name="%USERNAME%"|g' "$asset" || return 1
|
||||
sed -i "s|<Name>Docker</Name>|<Name>$user_xml</Name>|g" "$asset" || return 1
|
||||
sed -i "s|<FullName>Docker</FullName>|<FullName>$user_xml</FullName>|g" "$asset" || return 1
|
||||
sed -i "s|<Username>Docker</Username>|<Username>$user_xml</Username>|g" "$asset" || return 1
|
||||
@@ -919,7 +1255,7 @@ updateLocalAccountXML() {
|
||||
return 0
|
||||
}
|
||||
|
||||
updateMembershipXML() {
|
||||
updateMembership() {
|
||||
|
||||
local asset="$1"
|
||||
local domain="$2"
|
||||
@@ -941,7 +1277,7 @@ updateMembershipXML() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
removeLocalAccountXML "$asset" || return 1
|
||||
removeLocalAccount "$asset" || return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -986,28 +1322,22 @@ updateEditionXML() {
|
||||
return 0
|
||||
}
|
||||
|
||||
updateProductKeyXML() {
|
||||
updateProductKey() {
|
||||
|
||||
local asset="$1"
|
||||
local key
|
||||
local script="$1"
|
||||
local key="${KEY:-}"
|
||||
local content
|
||||
|
||||
return 0 # TODO
|
||||
if [ -z "$key" ]; then
|
||||
removeSetupBlock "$script" "PRODUCT_KEY" || return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ -n "${KEY:-}" ] || return 0
|
||||
printf -v content '%s\n%s' \
|
||||
'rem Install the product key without activating Windows immediately.' \
|
||||
"cscript.exe //B //Nologo \"%SystemRoot%\\System32\\slmgr.vbs\" /ipk \"$key\""
|
||||
|
||||
key=$(escapeXMLSed "$KEY") || return 1
|
||||
|
||||
sed -i -E \
|
||||
'/^[[:space:]]*<ProductKey>[[:space:]]*$/,/^[[:space:]]*<\/ProductKey>[[:space:]]*$/d' \
|
||||
"$asset" || return 1
|
||||
|
||||
sed -i -E \
|
||||
"s|<ProductKey>[^<]*</ProductKey>|<ProductKey>$key</ProductKey>|g" \
|
||||
"$asset" || return 1
|
||||
|
||||
sed -i \
|
||||
"s|</UserData>| <ProductKey>\n <Key>$key</Key>\n <WillShowUI>OnError</WillShowUI>\n </ProductKey>\n </UserData>|g" \
|
||||
"$asset" || return 1
|
||||
replaceSetupBlock "$script" "PRODUCT_KEY" "$content" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -1016,23 +1346,56 @@ updateDiskID() {
|
||||
|
||||
local asset="$1"
|
||||
local disk_type="${2,,}"
|
||||
|
||||
case "$disk_type" in
|
||||
"" | "scsi" | "virtio-scsi" | "blk" | "virtio-blk" ) ;;
|
||||
* ) return 0 ;;
|
||||
esac
|
||||
local mode="${3:-setup}"
|
||||
local target="0"
|
||||
local matches ids current count rc
|
||||
|
||||
[ -s "$asset" ] || return 1
|
||||
|
||||
# Only adjust files that explicitly target Disk 0.
|
||||
grep -Fq '<DiskID>0</DiskID>' "$asset" || return 0
|
||||
case "$mode" in
|
||||
"setup" )
|
||||
case "$disk_type" in
|
||||
"" | "scsi" | "virtio-scsi" | "blk" | "virtio-blk" ) target="1" ;;
|
||||
esac
|
||||
;;
|
||||
"image" ) ;;
|
||||
* ) return 1 ;;
|
||||
esac
|
||||
|
||||
# Leave multi-disk configurations untouched.
|
||||
if grep -Eq '<DiskID>[[:space:]]*[1-9][0-9]*[[:space:]]*</DiskID>' "$asset"; then
|
||||
return 0
|
||||
matches=$(grep -oE '<DiskID>[[:space:]]*[0-9]+[[:space:]]*</DiskID>' "$asset") || {
|
||||
rc=$?
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
matches=""
|
||||
else
|
||||
error "Failed to read DiskID values from answer file: $asset"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Some custom answer files do not contain a disk configuration.
|
||||
[ -n "$matches" ] || return 0
|
||||
|
||||
ids=$(printf '%s\n' "$matches" |
|
||||
sed -E 's#.*<DiskID>[[:space:]]*([0-9]+)[[:space:]]*</DiskID>.*#\1#' |
|
||||
sort -u) || return 1
|
||||
|
||||
count=$(printf '%s\n' "$ids" | wc -l) || return 1
|
||||
|
||||
# Leave explicit multi-disk configurations untouched.
|
||||
[ "$count" -eq 1 ] || return 0
|
||||
|
||||
current="$ids"
|
||||
[ "$current" = "$target" ] && return 0
|
||||
|
||||
if [ "$current" != "1" ]; then
|
||||
error "The answer file must use DiskID 1 as its template value: $asset"
|
||||
return 1
|
||||
fi
|
||||
|
||||
sed -i 's#<DiskID>0</DiskID>#<DiskID>1</DiskID>#g' "$asset" || return 1
|
||||
if ! sed -i 's#<DiskID>1</DiskID>#<DiskID>0</DiskID>#g' "$asset"; then
|
||||
error "Failed to update DiskID in answer file: $asset"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -1095,33 +1458,21 @@ setConfigurationXML() {
|
||||
return 0
|
||||
}
|
||||
|
||||
removeSharedFolderXML() {
|
||||
removeSharedFolder() {
|
||||
|
||||
local asset="$1"
|
||||
local script="$1"
|
||||
|
||||
if ! disabled "${SHORTCUT:-}" &&
|
||||
! disabled "${SAMBA:-}"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! sed -i -E '
|
||||
/<SynchronousCommand([[:space:]>])/ {
|
||||
:command
|
||||
N
|
||||
/<\/SynchronousCommand>/!b command
|
||||
/<Description>Create desktop shortcut to shared folder<\/Description>/d
|
||||
/<Description>Map shared folder<\/Description>/d
|
||||
}
|
||||
' "$asset"; then
|
||||
|
||||
error "Failed to remove shared folder shortcuts from answer file!"
|
||||
return 1
|
||||
fi
|
||||
removeSetupBlock "$script" "SHARED_FOLDER" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
removeLocalAccountXML() {
|
||||
removeLocalAccount() {
|
||||
|
||||
local asset="$1"
|
||||
|
||||
@@ -1134,42 +1485,21 @@ removeLocalAccountXML() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! sed -i -E '
|
||||
/<SynchronousCommand([[:space:]>])/ {
|
||||
:command
|
||||
N
|
||||
/<\/SynchronousCommand>/!b command
|
||||
/<Description>Password Never Expires<\/Description>/d
|
||||
}
|
||||
' "$asset"; then
|
||||
|
||||
error "Failed to remove local account commands from answer file!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
enableLog() {
|
||||
|
||||
local file="$1"
|
||||
local old='C:\OEM\install.bat"</CommandLine>'
|
||||
local msg="failed to enable install logging in the answer file!"
|
||||
local script="$1"
|
||||
local content
|
||||
|
||||
enabled "${LOG:-}" || return 0
|
||||
[ -f "$file" ] || return 1
|
||||
|
||||
if ! grep -Fq "$old" "$file"; then
|
||||
enabled "$DEBUG" && warn "$msg"
|
||||
return 0
|
||||
fi
|
||||
printf -v content '%s\n%s' \
|
||||
'rem Launch the custom script asynchronously in a separate visible window.' \
|
||||
'if exist "C:\OEM\install.bat" start "Install" cmd.exe /d /c ""C:\OEM\install.bat" > "C:\OEM\install.log" 2>&1"'
|
||||
|
||||
if ! sed -i \
|
||||
's|C:\\OEM\\install\.bat"</CommandLine>|C:\\OEM\\install.bat \> C:\\OEM\\install.log 2\>\&1"</CommandLine>|' \
|
||||
"$file"; then
|
||||
|
||||
warn "$msg"
|
||||
fi
|
||||
replaceSetupBlock "$script" "OEM_SCRIPT" "$content" || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
+58
-21
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
installWindows() {
|
||||
startWindows() {
|
||||
|
||||
parseVersion || return 58
|
||||
parseLanguage || return 62
|
||||
@@ -81,8 +81,7 @@ installWindows() {
|
||||
fi
|
||||
|
||||
if ! createSetupImage "$TMP/setup" "$STORAGE/setup.img"; then
|
||||
abortInstall "$dir" "$ISO" "$boot" || return 86
|
||||
return 0
|
||||
exit 86
|
||||
fi
|
||||
|
||||
useOriginalImage "$ISO" || return 88
|
||||
@@ -120,6 +119,7 @@ bootWindows() {
|
||||
restoreMachineState || return 1
|
||||
restoreBootMode || return 1
|
||||
restoreMachine || return 1
|
||||
reserveSambaPorts || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -406,6 +406,8 @@ finishInstall() {
|
||||
fi
|
||||
fi
|
||||
|
||||
reserveSambaPorts || return 1
|
||||
|
||||
rm -rf "$TMP"
|
||||
return 0
|
||||
}
|
||||
@@ -439,7 +441,10 @@ findFile() {
|
||||
|
||||
local size
|
||||
size="$(stat -c%s "$file")"
|
||||
[ -z "$size" ] || [[ "$size" == "0" ]] && return 0
|
||||
|
||||
if [ -z "$size" ] || [[ "$size" == "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
ISO="$file"
|
||||
CUSTOM="$file"
|
||||
@@ -779,25 +784,36 @@ setMachine() {
|
||||
local iso="$2"
|
||||
local dir="$3"
|
||||
local desc="$4"
|
||||
local legacy=""
|
||||
|
||||
ETFS="boot/etfsboot.com"
|
||||
|
||||
local version=""
|
||||
case "${id,,}" in
|
||||
"win2k"* ) legacy="2k" ;;
|
||||
"winxp"* ) legacy="xp" ;;
|
||||
"win2003"* ) legacy="2k3" ;;
|
||||
"win2k"* ) version="2k" ;;
|
||||
"winxp"* ) version="xp" ;;
|
||||
"win2003"* ) version="2k3" ;;
|
||||
esac
|
||||
|
||||
if [ -n "$legacy" ]; then
|
||||
if ! legacyInstall "$iso" "$dir" "$desc" "$legacy"; then
|
||||
if [ -n "$version" ]; then
|
||||
|
||||
if ! legacyInstall "$iso" "$dir" "$desc" "$version"; then
|
||||
error "Failed to prepare $desc ISO!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if isLegacy "$id"; then
|
||||
|
||||
writeState "mode" "windows_legacy" || return 1
|
||||
|
||||
case "${id,,}" in
|
||||
"win9"* | "win2k"* | "reactos" )
|
||||
writeState "vga" "cirrus" || return 1 ;;
|
||||
* )
|
||||
writeState "vga" "std" || return 1 ;;
|
||||
esac
|
||||
|
||||
fi
|
||||
|
||||
restoreBootMode || return 1
|
||||
@@ -806,16 +822,14 @@ setMachine() {
|
||||
|
||||
"win9"* )
|
||||
|
||||
writeState "usb" "no" || return 1
|
||||
writeState "usb" "N" || return 1
|
||||
writeState "net" "pcnet" || return 1
|
||||
writeState "type" "auto" || return 1
|
||||
writeState "vga" "cirrus" || return 1
|
||||
writeState "old" "pc-i440fx-2.4" || return 1 ;;
|
||||
|
||||
"win2k"* )
|
||||
|
||||
writeState "old" "pc" || return 1
|
||||
writeState "vga" "cirrus" || return 1
|
||||
writeState "type" "auto" || return 1
|
||||
writeState "net" "rtl8139" || return 1
|
||||
writeState "usb" "pci-ohci" || return 1 ;;
|
||||
@@ -832,7 +846,6 @@ setMachine() {
|
||||
|
||||
writeState "old" "pc" || return 1
|
||||
writeState "type" "auto" || return 1
|
||||
writeState "vga" "cirrus" || return 1
|
||||
writeState "net" "rtl8139" || return 1
|
||||
writeState "usb" "pci-ohci" || return 1 ;;
|
||||
|
||||
@@ -1128,12 +1141,11 @@ addDrivers() {
|
||||
mkdir -p "$dst" || return 1
|
||||
cp -Lr "$dest/." "$dst" || return 1
|
||||
|
||||
case "${version,,}" in
|
||||
"win11x64"* | "win2025"* )
|
||||
# Workaround Virtio GPU driver bug
|
||||
rm -rf "$dest/viogpudo"
|
||||
;;
|
||||
esac
|
||||
# Install the VirtIO display driver explicitly from SetupComplete.cmd so it
|
||||
# cannot disrupt Windows Setup by loading through the WinPE driver path.
|
||||
if ! isLegacy "$version"; then
|
||||
rm -rf "$dest/viogpudo"
|
||||
fi
|
||||
|
||||
if [ -n "$file" ]; then
|
||||
|
||||
@@ -1200,6 +1212,7 @@ updateImage() {
|
||||
local bak="${xml//.xml/.org}"
|
||||
local dat="${xml//.xml/.dat}"
|
||||
local desc path src wim name info
|
||||
local script=""
|
||||
|
||||
skipVersion "${DETECTED,,}" && return 0
|
||||
|
||||
@@ -1279,12 +1292,23 @@ updateImage() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! updateDiskID "$answer" "${DISK_TYPE:-}" "image"; then
|
||||
error "Failed to adjust the Windows installation disk!"
|
||||
exit 85
|
||||
fi
|
||||
|
||||
validateGeneratedXML "$answer" || return 1
|
||||
|
||||
if [ -z "${CUSTOM_XML:-}" ]; then
|
||||
prepareSetupScript "$asset" "$tmp/setup" script || exit 84
|
||||
fi
|
||||
|
||||
if ! wimlib-imagex update "$wim" "$idx" --command "add $answer /$xml" > /dev/null; then
|
||||
MANUAL="Y"
|
||||
warn "failed to add answer file ($name) to ISO image, $FB"
|
||||
else
|
||||
installSetupScript "$script" "$src" || exit 84
|
||||
|
||||
wimlib-imagex update "$wim" "$idx" --command "add $answer /$dat" > /dev/null || true
|
||||
fi
|
||||
|
||||
@@ -1338,6 +1362,19 @@ removeImage() {
|
||||
return 0
|
||||
}
|
||||
|
||||
reserveSambaPorts() {
|
||||
|
||||
disabled "${SAMBA:-Y}" && return 0
|
||||
disabled "${NETWORK:-Y}" && return 0
|
||||
enabled "${DHCP:-N}" && return 0
|
||||
|
||||
# NAT can fall back to user-mode networking after this point,
|
||||
# so always protect the Samba listeners for non-DHCP networking.
|
||||
HOST_PORTS="${HOST_PORTS:+$HOST_PORTS,}139/tcp,445/tcp"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
backup () {
|
||||
|
||||
local iso="$1"
|
||||
@@ -1450,6 +1487,6 @@ restoreMachineState() {
|
||||
return 0
|
||||
}
|
||||
|
||||
installWindows
|
||||
startWindows
|
||||
|
||||
return 0
|
||||
|
||||
+11
-2
@@ -577,6 +577,8 @@ getWindows() {
|
||||
local language edition
|
||||
|
||||
MIDO_SOURCE=""
|
||||
MIDO_STATIC="N"
|
||||
|
||||
language=$(getLanguage "$lang" "desc")
|
||||
edition=$(printEdition "$version" "$desc" "Y")
|
||||
|
||||
@@ -648,6 +650,8 @@ getWindows() {
|
||||
MIDO_URL=$(getMido "$version" "$lang" "")
|
||||
[ -z "$MIDO_URL" ] && return 1
|
||||
|
||||
MIDO_STATIC="Y"
|
||||
|
||||
if [[ "${version,,}" == "win2008r2"* ]]; then
|
||||
MIDO_SOURCE="win2008r2-eval"
|
||||
return 0
|
||||
@@ -1143,13 +1147,18 @@ downloadImage() {
|
||||
sum=""
|
||||
size=""
|
||||
|
||||
# Skip verification if the retrieved URL differs from the static URL.
|
||||
# Apply the metadata belonging to the configured static URL.
|
||||
if [[ "${MIDO_URL%%\?*}" == "${url%%\?*}" ]]; then
|
||||
size=$(getMido "$version" "$lang" "size")
|
||||
sum=$(getMido "$version" "$lang" "sum")
|
||||
fi
|
||||
|
||||
if tryDownload "$iso" "$MIDO_URL" "$sum" "$size" "$lang" "$desc" "$seconds" "$web_desc"; then
|
||||
local download_desc="$desc"
|
||||
if enabled "$MIDO_STATIC"; then
|
||||
download_desc+=" using a static link"
|
||||
fi
|
||||
|
||||
if tryDownload "$iso" "$MIDO_URL" "$sum" "$size" "$lang" "$download_desc" "$seconds" "$web_desc"; then
|
||||
# Commit the candidate only after the image was downloaded and verified.
|
||||
DETECTED="$detected"
|
||||
return 0
|
||||
|
||||
+26
-5
@@ -76,6 +76,10 @@ bootStatus() {
|
||||
'BdsDxe: failed to start Boot[[:xdigit:]]{4} "UEFI QEMU .*DVD-ROM.*: Time out' \
|
||||
<<< "$recent" && return 2
|
||||
|
||||
grep -Fq \
|
||||
"BdsDxe: No bootable option or device was found." \
|
||||
<<< "$recent" && return 2
|
||||
|
||||
grep -Fq "UEFI Interactive Shell" <<< "$recent" && return 2
|
||||
|
||||
grep -Eq \
|
||||
@@ -104,9 +108,17 @@ waitForBoot() {
|
||||
if (( ! keySent )) && needsBootKey; then
|
||||
|
||||
if keyDelay=$(bootKeyDelay); then
|
||||
if sendKey spc "$keyDelay" 500; then
|
||||
keySent=1
|
||||
|
||||
if [[ "$keyDelay" == "0" ]]; then
|
||||
if sendKey spc 0 500; then
|
||||
keySent=1
|
||||
fi
|
||||
else
|
||||
if sendKey spc "$keyDelay" 250 4 0.75; then
|
||||
keySent=1
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
@@ -239,14 +251,23 @@ sendKey() {
|
||||
local key="$1"
|
||||
local delay="${2:-0}"
|
||||
local hold="${3:-100}"
|
||||
local output
|
||||
local repeat="${4:-1}"
|
||||
local interval="${5:-0}"
|
||||
local i output
|
||||
|
||||
[ ! -S "$ACPI_SOCKET" ] && return 1
|
||||
[[ "$delay" != "0" ]] && sleep "$delay"
|
||||
|
||||
if ! output=$(
|
||||
printf 'sendkey %s %s\n' "$key" "$hold" |
|
||||
nc -q 1 -w 1 -U "$ACPI_SOCKET" 2>&1
|
||||
{
|
||||
for ((i = 1; i <= repeat; i++)); do
|
||||
printf 'sendkey %s %s\n' "$key" "$hold"
|
||||
|
||||
if (( i < repeat )); then
|
||||
sleep "$interval"
|
||||
fi
|
||||
done
|
||||
} | nc -q 1 -w 1 -U "$ACPI_SOCKET" 2>&1
|
||||
); then
|
||||
return 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user