94 lines
5.4 KiB
PowerShell
94 lines
5.4 KiB
PowerShell
function Assert-ConfigValue {
|
|
param([string]$Value, [string]$Name)
|
|
if ([string]::IsNullOrWhiteSpace($Value) -or $Value.IndexOfAny([char[]]@("`r", "`n", [char]0)) -ge 0) { throw "Invalid configuration value: $Name" }
|
|
}
|
|
. (Join-Path $PSScriptRoot 'Package.Validation.ps1')
|
|
. (Join-Path $PSScriptRoot 'ProductKeys.Common.ps1')
|
|
. (Join-Path $PSScriptRoot 'Software.Selection.ps1')
|
|
function Get-DefaultImageFileName {
|
|
param([string]$ImageName, [datetime]$Timestamp=(Get-Date))
|
|
Assert-ConfigValue -Value $ImageName -Name 'ImageName'
|
|
if ($ImageName -match '^Windows\s+Server\s+(\d{4})\b') { $prefix = 'WinServer' + $Matches[1] }
|
|
elseif ($ImageName -match '^Windows\s+(\d+(?:\.\d+)?)\b') { $prefix = 'Win' + $Matches[1] }
|
|
else {
|
|
$prefix = $ImageName -replace '[^\p{L}\p{Nd}._-]', ''
|
|
if (-not $prefix) { $prefix = 'Windows' }
|
|
}
|
|
return $prefix + '-' + $Timestamp.ToString('yyyyMMddHHmm', [Globalization.CultureInfo]::InvariantCulture) + '.qcow2'
|
|
}
|
|
function Find-WindowsInstallerIso {
|
|
param([string]$ProjectRoot,[string]$AssetsRoot)
|
|
foreach($directory in @((Join-Path $ProjectRoot 'data\iso'),(Join-Path $AssetsRoot 'iso'))){
|
|
if(-not(Test-Path -LiteralPath $directory -PathType Container)){continue}
|
|
$candidate=Get-ChildItem -LiteralPath $directory -Filter '*.iso' -File|Where-Object Name -notlike '*virtio*'|Sort-Object Name|Select-Object -First 1
|
|
if($candidate){return $candidate.FullName}
|
|
}
|
|
}
|
|
function Find-VirtioInstallerIso {
|
|
param([string]$ProjectRoot,[string]$AssetsRoot)
|
|
foreach($directory in @((Join-Path $ProjectRoot 'data\iso'),(Join-Path $AssetsRoot 'iso'))){
|
|
if(-not(Test-Path -LiteralPath $directory -PathType Container)){continue}
|
|
$candidate=Get-ChildItem -LiteralPath $directory -Filter '*virtio*.iso' -File|Sort-Object Name|Select-Object -First 1
|
|
if($candidate){return $candidate.FullName}
|
|
}
|
|
}
|
|
function Set-ImageConfigValue {
|
|
param([string]$Text, [string]$Key, [string]$Value)
|
|
Assert-ConfigValue -Value $Value -Name $Key
|
|
$pattern = '(?m)^' + [regex]::Escape($Key) + '=.*$'
|
|
if (-not [regex]::IsMatch($Text, $pattern)) { throw "Configuration key not found: $Key" }
|
|
$replacement = $Key + '=' + $Value
|
|
return [regex]::Replace($Text, $pattern, [Text.RegularExpressions.MatchEvaluator]{ param($match) $replacement })
|
|
}
|
|
function Expand-VirtioGuestTools {
|
|
param([string]$IsoPath,[string]$DestinationDirectory)
|
|
$member='virtio-win-guest-tools.exe'
|
|
$tar=Get-Command tar.exe -ErrorAction SilentlyContinue
|
|
if($tar){
|
|
$priorErrorAction=$ErrorActionPreference
|
|
try{
|
|
$ErrorActionPreference='Continue'
|
|
$output=@(& $tar.Source -xf $IsoPath -C $DestinationDirectory -- $member 2>&1|ForEach-Object{$_.ToString()})
|
|
$exitCode=$LASTEXITCODE
|
|
}finally{$ErrorActionPreference=$priorErrorAction}
|
|
if($exitCode -ne 0){throw ('Cannot extract VirtIO guest tools from the selected ISO: '+($output -join ' '))}
|
|
}else{
|
|
$mountedHere=$false
|
|
try{
|
|
$image=Get-DiskImage -ImagePath $IsoPath
|
|
if(-not$image.Attached){Mount-DiskImage -ImagePath $IsoPath -StorageType ISO|Out-Null;$mountedHere=$true}
|
|
$volumes=@(Get-DiskImage -ImagePath $IsoPath|Get-Volume|Where-Object DriveLetter)
|
|
if($volumes.Count -ne 1){throw 'Cannot identify the VirtIO ISO volume.'}
|
|
$source=Join-Path ($volumes[0].DriveLetter+':\') $member
|
|
Copy-Item -LiteralPath $source -Destination (Join-Path $DestinationDirectory $member)
|
|
}finally{if($mountedHere){Dismount-DiskImage -ImagePath $IsoPath|Out-Null}}
|
|
}
|
|
$extracted=Join-Path $DestinationDirectory $member
|
|
if(-not(Test-Path -LiteralPath $extracted -PathType Leaf)){throw 'The selected VirtIO ISO does not contain virtio-win-guest-tools.exe.'}
|
|
return $extracted
|
|
}
|
|
function Initialize-VirtioGuestTools {
|
|
param($Settings,[string]$VirtioIsoPath,[switch]$RefreshFromIso)
|
|
$package=Get-SelectedImagePackages -SkipChrome -SkipVSCode
|
|
$destination=Join-Path $Settings.AssetsRoot ('custom-resources\'+$package.File)
|
|
if(-not$RefreshFromIso){
|
|
try{Assert-ImagePackage -Path $destination -Package $package;return $destination}catch{}
|
|
}
|
|
if(-not(Test-Path -LiteralPath $VirtioIsoPath -PathType Leaf) -or [IO.Path]::GetExtension($VirtioIsoPath) -ine '.iso'){throw 'Select an existing VirtIO ISO.'}
|
|
$workRoot=[IO.Path]::GetFullPath((Join-Path $Settings.ProjectRoot 'local\virtio-extract')).TrimEnd('\')
|
|
$temporary=Join-Path $workRoot ([guid]::NewGuid().ToString('N'))
|
|
if(-not([IO.Path]::GetFullPath($temporary).StartsWith($workRoot+'\',[StringComparison]::OrdinalIgnoreCase))){throw 'Unexpected VirtIO extraction path.'}
|
|
New-Item -ItemType Directory -Path $temporary -Force|Out-Null
|
|
try{
|
|
$extracted=Expand-VirtioGuestTools -IsoPath ([IO.Path]::GetFullPath($VirtioIsoPath)) -DestinationDirectory $temporary
|
|
Assert-ImagePackage -Path $extracted -Package $package
|
|
New-Item -ItemType Directory -Path (Split-Path -Parent $destination) -Force|Out-Null
|
|
Copy-Item -LiteralPath $extracted -Destination $destination -Force
|
|
return $destination
|
|
}finally{
|
|
$resolved=(Resolve-Path -LiteralPath $temporary -ErrorAction Stop).ProviderPath
|
|
if(-not$resolved.StartsWith($workRoot+'\',[StringComparison]::OrdinalIgnoreCase)){throw 'VirtIO extraction cleanup escaped its workspace.'}
|
|
Remove-Item -LiteralPath $resolved -Recurse -Force
|
|
}
|
|
}
|