# Windows host disk writer. The default only inspects and prints a disk-specific
# plan. Never invoke -Write until a human has selected and authorized that disk.
[CmdletBinding()]
param(
[string]$Image,
[int]$DiskNumber = -1,
[string]$PlanPath,
[switch]$Write,
[string]$ConfirmToken,
[ValidateSet('LegacyPhysical256','CSharpExperimental128','IndependentCSharp128')][string]$ImageProfile = 'LegacyPhysical256',
[string]$BootResult,
[string]$PythonPath = 'python',
[string]$ResultPath,
[switch]$LoadFunctionsOnly
)
$ErrorActionPreference = 'Stop'
function Get-XfeToken([string]$Value) {
$hash = [Security.Cryptography.SHA256]::Create()
try { return ([BitConverter]::ToString($hash.ComputeHash([Text.Encoding]::UTF8.GetBytes($Value)))).Replace('-', '').ToLowerInvariant() }
finally { $hash.Dispose() }
}
function Open-XfeLockedImage([string]$Path,[string]$ExpectedHash) {
# Sharing read access only prevents concurrent writes, replacement or delete.
# Hash and the later disk copy use this very same handle, held until finally.
$stream=[IO.File]::Open($Path,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::Read)
$hash=[Security.Cryptography.SHA256]::Create()
try {
$actual=([BitConverter]::ToString($hash.ComputeHash($stream))).Replace('-','').ToLowerInvariant()
if($actual -cne $ExpectedHash){throw 'Image changed since the plan; refusing before any target mutation.'}
[void]$stream.Seek(0,[IO.SeekOrigin]::Begin)
return $stream
} catch {$stream.Dispose();throw} finally {$hash.Dispose()}
}
function Assert-XfeDisk($Disk, $Partitions, [uint64]$ImageBytes) {
if ($null -eq $Disk -or $Disk.Number -lt 0) { throw 'A concrete target disk is required.' }
if ($Disk.IsSystem -or $Disk.IsBoot) { throw 'Refusing the Windows system or boot disk.' }
if ([string]::IsNullOrWhiteSpace([string]$Disk.UniqueId)) { throw 'Disk UniqueId is unavailable; refusing ambiguous identity.' }
if ([uint64]$Disk.Size -lt $ImageBytes) { throw 'The selected disk is smaller than the image.' }
if ([uint64]$Disk.LogicalSectorSize -ne 512) { throw 'Only 512-byte logical sectors are supported by this candidate.' }
if ($Disk.IsReadOnly) { throw 'The selected disk is read-only.' }
if ([string]$Disk.BusType -in @('Virtual', 'File Backed Virtual', 'Spaces', 'iSCSI')) { throw 'Refusing virtual, Storage Spaces or remote disks in the physical writer.' }
foreach ($part in @($Partitions)) {
if ($part.IsSystem -or $part.IsBoot) { throw 'A partition on the selected disk is marked as boot or system.' }
if ($part.DriveLetter -and ([string]$part.DriveLetter + ':') -ieq $env:SystemDrive) { throw 'The selected disk contains the running Windows volume.' }
foreach ($access in @($part.AccessPaths)) {
if ($access -and $access.TrimEnd('\') -ieq $env:SystemDrive) { throw 'The selected disk contains the running Windows volume.' }
}
}
}
function Get-XfeCrc32([byte[]]$Bytes) {
[uint32]$crc = [uint32]::MaxValue
foreach ($b in $Bytes) {
$crc = $crc -bxor [uint32]$b
for ($i = 0; $i -lt 8; $i++) {
if ($crc -band 1) { $crc = [uint32](($crc -shr 1) -bxor [uint32]3988292384) }
else { $crc = $crc -shr 1 }
}
}
return $crc -bxor [uint32]::MaxValue
}
function Read-XfeBytes($Stream, [uint64]$Offset, [int]$Count) {
[void]$Stream.Seek([int64]$Offset, [IO.SeekOrigin]::Begin)
$data = New-Object byte[] $Count
$done = 0
while ($done -lt $Count) { $n = $Stream.Read($data, $done, $Count - $done); if ($n -eq 0) { throw 'Unexpected end of disk or image.' }; $done += $n }
return ,$data
}
function Write-XfeBytes($Stream, [uint64]$Offset, [byte[]]$Bytes) {
[void]$Stream.Seek([int64]$Offset, [IO.SeekOrigin]::Begin)
$Stream.Write($Bytes, 0, $Bytes.Length)
}
function Set-XfeU64([byte[]]$Bytes, [int]$Offset, [uint64]$Value) { [BitConverter]::GetBytes($Value).CopyTo($Bytes, $Offset) }
function Set-XfeU32([byte[]]$Bytes, [int]$Offset, [uint32]$Value) { [BitConverter]::GetBytes($Value).CopyTo($Bytes, $Offset) }
function Set-XfeHeaderCrc([byte[]]$Header) {
Set-XfeU32 $Header 16 0
Set-XfeU32 $Header 16 (Get-XfeCrc32 ([byte[]]$Header[0..91]))
}
function Assert-XfeEqual([byte[]]$Expected, [byte[]]$Actual) {
if ($Expected.Length -ne $Actual.Length) { throw 'Written disk verification length mismatch.' }
for ($i=0; $i -lt $Expected.Length; $i++) { if ($Expected[$i] -ne $Actual[$i]) { throw "Written disk verification mismatch at byte $i." } }
}
function Assert-XfeImage([string]$Path) {
$stream=[IO.File]::Open($Path,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::Read)
try {
if($stream.Length -ne 256MB){throw 'Only generated 256 MiB candidate images are accepted.'}
[uint64]$sectors=$stream.Length/512
[uint64]$last=$sectors-1
$mbr=Read-XfeBytes $stream 0 512
if($mbr[450] -ne 238 -or $mbr[510] -ne 85 -or $mbr[511] -ne 170 -or [BitConverter]::ToUInt32($mbr,454) -ne 1 -or [BitConverter]::ToUInt32($mbr,458) -ne $last){throw 'Invalid protective MBR.'}
if(@($mbr[462..509] | Where-Object {$_ -ne 0}).Count -ne 0){throw 'Hybrid MBR images are refused.'}
$primary=Read-XfeBytes $stream 512 512
$backup=Read-XfeBytes $stream ($last*512) 512
foreach($pair in @(@($primary,[uint64]1,$last,[uint64]2),@($backup,$last,[uint64]1,($last-32)))){
[byte[]]$h=$pair[0]
if([Text.Encoding]::ASCII.GetString($h,0,8) -cne 'EFI PART' -or [BitConverter]::ToUInt32($h,8) -ne 65536 -or [BitConverter]::ToUInt32($h,12) -ne 92 -or [BitConverter]::ToUInt32($h,20) -ne 0 -or [BitConverter]::ToUInt64($h,24) -ne $pair[1] -or [BitConverter]::ToUInt64($h,32) -ne $pair[2] -or [BitConverter]::ToUInt64($h,40) -ne 34 -or [BitConverter]::ToUInt64($h,48) -ne ($last-33) -or [BitConverter]::ToUInt64($h,72) -ne $pair[3] -or [BitConverter]::ToUInt32($h,80) -ne 128 -or [BitConverter]::ToUInt32($h,84) -ne 128){throw 'Unsupported GPT header or geometry.'}
[uint32]$crc=[BitConverter]::ToUInt32($h,16)
[byte[]]$header=$h[0..91]
Set-XfeU32 $header 16 0
if((Get-XfeCrc32 $header) -ne $crc){throw 'GPT header CRC mismatch.'}
}
Assert-XfeEqual ([byte[]]$primary[56..71]) ([byte[]]$backup[56..71])
Assert-XfeEqual ([byte[]]$primary[88..91]) ([byte[]]$backup[88..91])
$table=Read-XfeBytes $stream 1024 16384
Assert-XfeEqual $table (Read-XfeBytes $stream (($last-32)*512) 16384)
if((Get-XfeCrc32 $table) -ne [BitConverter]::ToUInt32($primary,88)){throw 'GPT table CRC mismatch.'}
$kinds=@('c12a7328-f81f-11d2-ba4b-00a0c93ec93b','58464544-4154-4100-8000-584645535953','21686148-6449-6e6f-744e-656564454649')
$starts=@([uint64]2048,[uint64]264192,[uint64]329728)
$counts=@([uint64]262144,[uint64]65536,[uint64]2048)
$identities=@()
for($i=0;$i -lt 3;$i++){
$at=$i*128
Assert-XfeEqual ([Guid]$kinds[$i]).ToByteArray() ([byte[]]$table[$at..($at+15)])
$id=([BitConverter]::ToString($table,$at+16,16))
if($id -eq '00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00' -or $identities -contains $id){throw 'Invalid or duplicate GPT partition identity.'}
$identities+=$id
if([BitConverter]::ToUInt64($table,$at+32) -ne $starts[$i] -or [BitConverter]::ToUInt64($table,$at+40) -ne ($starts[$i]+$counts[$i]-1) -or [BitConverter]::ToUInt64($table,$at+48) -ne 0){throw 'GPT partition bounds or attributes mismatch.'}
}
if(@($table[384..16383] | Where-Object {$_ -ne 0}).Count -ne 0){throw 'Unexpected additional GPT partition.'}
if([Text.Encoding]::Unicode.GetString($table,184,72).TrimEnd([char]0) -cne 'XFEDATA'){throw 'XFEDATA label mismatch.'}
$sb=Read-XfeBytes $stream (264192*512+1024) 1024
if([BitConverter]::ToUInt16($sb,56) -ne 61267 -or [BitConverter]::ToUInt32($sb,24) -ne 2 -or [BitConverter]::ToUInt16($sb,88) -ne 128 -or [BitConverter]::ToUInt32($sb,96) -ne 2){throw 'Unsupported XFEDATA ext2 format.'}
} finally {$stream.Dispose()}
}
function Assert-XfeExperimentalDisk($Disk, $Partitions, [uint64]$ImageBytes) {
Assert-XfeDisk $Disk $Partitions $ImageBytes
if ([string]$Disk.BusType -cne 'USB' -or ([string]$Disk.SerialNumber).Trim() -cne '230222336207' -or ([string]$Disk.UniqueId).Trim() -cne '5000000000002AEF') {
throw 'The experimental profile is restricted to the exact dedicated XFEstudio USB identity.'
}
if (-not $Disk.IsOffline) { throw 'The dedicated experimental USB must already be offline.' }
}
function Get-XfePartitionFingerprint($Partitions) {
$values = @($Partitions | Sort-Object PartitionNumber | ForEach-Object {
[ordered]@{Number=[uint32]$_.PartitionNumber;Offset=[uint64]$_.Offset;Size=[uint64]$_.Size;
GptType=([string]$_.GptType).ToLowerInvariant();IsBoot=[bool]$_.IsBoot;IsSystem=[bool]$_.IsSystem;
DriveLetter=([string]$_.DriveLetter).Trim([char]0);
AccessPaths=@($_.AccessPaths | Where-Object {-not [string]::IsNullOrEmpty([string]$_)} | Sort-Object)}
})
return Get-XfeToken (ConvertTo-Json -InputObject $values -Depth 5 -Compress)
}
function Get-XfeExperimentalEvidence([string]$Path, [string]$ReceiptPath, [string]$PythonCommand = 'python', [string]$Profile = 'CSharpExperimental128') {
if ([string]::IsNullOrWhiteSpace($ReceiptPath)) { throw 'CSharpExperimental128 requires -BootResult with the exact passing boot receipt.' }
$verifier = Join-Path $PSScriptRoot 'physical-media-verify.py'
$savedPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$output = @(& $PythonCommand $verifier $Path $ReceiptPath --profile $Profile 2>&1)
$exitCode = $LASTEXITCODE
} finally { $ErrorActionPreference = $savedPreference }
if ($exitCode -ne 0) { throw ('Independent C# image/evidence verification failed: ' + ($output -join [Environment]::NewLine)) }
$evidence = ($output -join [Environment]::NewLine) | ConvertFrom-Json
if ($evidence.Kind -cne 'XfeCSharpExperimentalPhysicalMediaEvidence' -or $evidence.Profile -cne $Profile -or $evidence.FullReleaseQualified -ne $false -or
$evidence.RamOnlyConfigurationVerified -ne $true -or $evidence.StorageEnabled -ne $false -or $evidence.PhysicalInputHardwareAcceptancePassed -ne $false) {
throw 'Unexpected independent experimental image verification result.'
}
return $evidence
}
function Get-XfeAdjustedGpt($Source, [uint64]$TargetBytes) {
if ($TargetBytes -lt [uint64]$Source.Length -or $TargetBytes % 512 -ne 0) { throw 'Invalid target addressing boundary.' }
[uint64]$last = $TargetBytes / 512 - 1
$primary = Read-XfeBytes $Source 512 512
$backup = Read-XfeBytes $Source ($Source.Length - 512) 512
$table = Read-XfeBytes $Source 1024 16384
$mbr = Read-XfeBytes $Source 0 512
Set-XfeU64 $primary 32 $last
Set-XfeU64 $primary 48 ($last - 33)
Set-XfeHeaderCrc $primary
Set-XfeU64 $backup 24 $last
Set-XfeU64 $backup 48 ($last - 33)
Set-XfeU64 $backup 72 ($last - 32)
Set-XfeHeaderCrc $backup
Set-XfeU32 $mbr 458 ([uint32][Math]::Min($last, [uint64][uint32]::MaxValue))
return [pscustomobject]@{LastLba=$last;Mbr=$mbr;Primary=$primary;Backup=$backup;Table=$table}
}
function Set-XfeAdjustedGpt($Target, $Geometry) {
Write-XfeBytes $Target 0 $Geometry.Mbr
Write-XfeBytes $Target 512 $Geometry.Primary
Write-XfeBytes $Target (($Geometry.LastLba - 32) * 512) $Geometry.Table
Write-XfeBytes $Target ($Geometry.LastLba * 512) $Geometry.Backup
}
function Set-XfeExpectedRange([byte[]]$Buffer, [uint64]$BufferOffset, [uint64]$PatchOffset, [byte[]]$Patch) {
[uint64]$start = [Math]::Max($BufferOffset, $PatchOffset)
[uint64]$end = [Math]::Min($BufferOffset + [uint64]$Buffer.Length, $PatchOffset + [uint64]$Patch.Length)
if ($start -lt $end) { [Array]::Copy($Patch, [int]($start-$PatchOffset), $Buffer, [int]($start-$BufferOffset), [int]($end-$start)) }
}
function Test-XfeAdjustedReadback($Source, $Target, [uint64]$TargetBytes) {
$geometry = Get-XfeAdjustedGpt $Source $TargetBytes
Assert-XfeEqual $geometry.Mbr (Read-XfeBytes $Target 0 512)
Assert-XfeEqual $geometry.Primary (Read-XfeBytes $Target 512 512)
Assert-XfeEqual $geometry.Table (Read-XfeBytes $Target 1024 16384)
Assert-XfeEqual $geometry.Table (Read-XfeBytes $Target (($geometry.LastLba-32)*512) 16384)
Assert-XfeEqual $geometry.Backup (Read-XfeBytes $Target ($geometry.LastLba*512) 512)
$expectedHash = [Security.Cryptography.SHA256]::Create()
$actualHash = [Security.Cryptography.SHA256]::Create()
try {
[uint64]$offset = 0
while ($offset -lt [uint64]$Source.Length) {
$count = [int][Math]::Min([uint64]1MB, [uint64]$Source.Length-$offset)
$expected = Read-XfeBytes $Source $offset $count
Set-XfeExpectedRange $expected $offset 0 $geometry.Mbr
Set-XfeExpectedRange $expected $offset 512 $geometry.Primary
Set-XfeExpectedRange $expected $offset (($geometry.LastLba-32)*512) $geometry.Table
Set-XfeExpectedRange $expected $offset ($geometry.LastLba*512) $geometry.Backup
$actual = Read-XfeBytes $Target $offset $count
[void]$expectedHash.TransformBlock($expected,0,$count,$expected,0)
[void]$actualHash.TransformBlock($actual,0,$count,$actual,0)
$offset += $count
}
[void]$expectedHash.TransformFinalBlock([byte[]]@(),0,0)
[void]$actualHash.TransformFinalBlock([byte[]]@(),0,0)
$expectedHex = ([BitConverter]::ToString($expectedHash.Hash)).Replace('-','').ToLowerInvariant()
$actualHex = ([BitConverter]::ToString($actualHash.Hash)).Replace('-','').ToLowerInvariant()
if ($expectedHex -cne $actualHex) { throw 'Complete capacity-adjusted image SHA256 mismatch.' }
return [pscustomobject]@{ImageBytesVerified=[uint64]$Source.Length;ExpectedAdjustedImageSha256=$expectedHex;
ActualAdjustedImageSha256=$actualHex;ActualLastLba=$geometry.LastLba;ActualTailBackupGptVerified=$true;FullImageVerified=$true}
} finally { $expectedHash.Dispose(); $actualHash.Dispose() }
}
function Get-XfeMediaIdentity($Disk, $Partitions, [string]$ImageHash, [string]$Profile, $Evidence) {
$identity = '{0}|{1}|{2}|{3}|{4}|{5}|{6}' -f $Disk.Number, $Disk.UniqueId, ([string]$Disk.SerialNumber).Trim(), $Disk.Size, $Disk.BusType, $Disk.LogicalSectorSize, $ImageHash
if ($Profile -in @('CSharpExperimental128','IndependentCSharp128')) {
$writerHash = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'physical-media.ps1') -Algorithm SHA256).Hash.ToLowerInvariant()
$verifierHash = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'physical-media-verify.py') -Algorithm SHA256).Hash.ToLowerInvariant()
$identity += '|{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}' -f $Profile,$Evidence.ImageManifestSha256,$Evidence.BootResultSha256,$Evidence.SourceSnapshotSha256,(Get-XfePartitionFingerprint $Partitions),$writerHash,$verifierHash,$Evidence.KernelProjectSha256
}
return $identity
}
if ($LoadFunctionsOnly) { return }
$log = [ordered]@{Kind='XfePhysicalMediaOperation';Profile=$ImageProfile;WriteRequested=[bool]$Write;Status='failed';
StartedUtc=[DateTime]::UtcNow.ToString('o');FullReleaseQualified=$false;StabilityPassed=$false;
CudaExecutionVerified=$false;GpuRasterizationVerified=$false;PhysicalDiskOpened=$false;HandlesClosed=$true;Stage='preflight'}
if (-not $ResultPath) {
$logDirectory = Join-Path (Split-Path -Parent $PSScriptRoot) 'build/usb-media'
[void][IO.Directory]::CreateDirectory($logDirectory)
$ResultPath = Join-Path $logDirectory ('physical-media-' + [Guid]::NewGuid().ToString('N') + '.json')
}
$logFile = [IO.File]::Open([IO.Path]::GetFullPath($ResultPath),[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
try {
if (-not $Image -or $DiskNumber -lt 0) { throw 'Specify -Image FILE.img and -DiskNumber N. Without -Write this only produces a review plan.' }
if ($env:OS -ne 'Windows_NT') { throw 'This script requires Windows Storage cmdlets.' }
$imagePath = (Resolve-Path -LiteralPath $Image).ProviderPath
$imageInfo = Get-Item -LiteralPath $imagePath
if ($imageInfo.PSIsContainer) { throw 'Expected a regular image file.' }
$manifestPath = $imagePath + '.json'
$manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
$imageHash = (Get-FileHash -LiteralPath $imagePath -Algorithm SHA256).Hash.ToLowerInvariant()
$evidence = $null
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) {
$evidence = Get-XfeExperimentalEvidence $imagePath $BootResult $PythonPath $ImageProfile
if ($evidence.ImageSha256 -cne $imageHash -or $evidence.ImageBytes -ne $imageInfo.Length) { throw 'Independent image verification disagrees with the selected source.' }
$log.ExperimentalEvidence = $evidence
$log.RamOnlyConfigurationVerified=$true
$log.StorageEnabled=$false
$log.PhysicalInputHardwareAcceptancePassed=$false
} else {
if ($BootResult) { throw '-BootResult is valid only for the explicit C# experimental profiles.' }
if ($imageInfo.Length -ne 256MB -or $manifest.schema -ne 1 -or $manifest.architecture -notin @('x86_64','i686') -or $manifest.image_bytes -ne $imageInfo.Length -or $manifest.image_sha256 -cne $imageHash -or $manifest.data_type_guid -ne '58464544-4154-4100-8000-584645535953') { throw 'Image manifest or SHA256 mismatch; regenerate using physical-image.' }
Assert-XfeImage $imagePath
}
$disk = Get-Disk -Number $DiskNumber -ErrorAction Stop
$parts = @(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $disk $parts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $disk $parts $imageInfo.Length }
$identity = Get-XfeMediaIdentity $disk $parts $imageHash $ImageProfile $evidence
$token = Get-XfeToken $identity
$plan = [ordered]@{
schema = 1
operation = 'ERASE partition table and replace disk contents with XFESystem candidate'
dry_run = -not [bool]$Write
disk_number = $disk.Number
disk_unique_id = [string]$disk.UniqueId
disk_serial = ([string]$disk.SerialNumber).Trim()
disk_friendly_name = [string]$disk.FriendlyName
disk_bus = [string]$disk.BusType
disk_bytes = [uint64]$disk.Size
logical_sector_bytes = $disk.LogicalSectorSize
existing_partitions = @($parts | Select-Object PartitionNumber, DriveLetter, Type, Size)
image = $imagePath
image_bytes = $imageInfo.Length
image_sha256 = $imageHash
image_profile = $ImageProfile
experimental_evidence = $evidence
full_release_qualified = $false
stability_passed = $false
cuda_execution_verified = $false
gpu_rasterization_verified = $false
partition_fingerprint = Get-XfePartitionFingerprint $parts
gpt_backup_relocated_to_target_end = $true
final_disk_state = 'offline'
confirm_token = $token
}
$planJson = $plan | ConvertTo-Json -Depth 6
$log.Plan = $plan
if ($PlanPath) {
$planFile = [IO.File]::Open([IO.Path]::GetFullPath($PlanPath), [IO.FileMode]::CreateNew, [IO.FileAccess]::Write)
try { $b = [Text.Encoding]::UTF8.GetBytes($planJson); $planFile.Write($b, 0, $b.Length) }
finally { $planFile.Dispose() }
}
$planJson
if (-not $Write) {
$log.Status='planned'
Write-Host 'DRY RUN ONLY: no disk was opened for writing or changed. Review disk number, identity, capacity and ALL existing partitions.'
Write-Host 'Only after the human explicitly authorizes erasing this exact disk, rerun with -Write -ConfirmToken <full token>.'
return
}
if ([string]::IsNullOrWhiteSpace($ConfirmToken) -or $ConfirmToken -cne $token) { throw 'The full confirmation token from the disk-specific plan is required. No write was performed.' }
$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'Writing a physical disk requires an elevated PowerShell window after explicit disk approval.' }
# Check again immediately before the mutation. An old plan cannot select a disk
# whose number was recycled after hotplug or whose size/identity changed.
$freshDisk = Get-Disk -Number $DiskNumber -ErrorAction Stop
$freshParts = @(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $freshDisk $freshParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $freshDisk $freshParts $imageInfo.Length }
$freshIdentity = Get-XfeMediaIdentity $freshDisk $freshParts $imageHash $ImageProfile $evidence
if ($freshIdentity -cne $identity) { throw 'Target identity changed since the plan; refusing the write.' }
$rawPath = '\\.\PhysicalDrive' + $DiskNumber
$target = $null
$source = $null
$evidenceLocks = @()
try {
$log.Stage='locking-source-and-evidence'
$source = Open-XfeLockedImage $imagePath $imageHash
$log.HandlesClosed=$false
if ($evidence) {
$configurationPath=if($ImageProfile -ceq 'IndependentCSharp128'){$evidence.KernelConfigurationPath}else{$evidence.KernelProjectPath}
if([string]::IsNullOrWhiteSpace($configurationPath)){throw 'The source-bound kernel configuration path is unavailable; refusing before target mutation.'}
foreach ($pair in @(@($evidence.ImageManifestPath,$evidence.ImageManifestSha256),@($evidence.BootResultPath,$evidence.BootResultSha256),@($evidence.SourceSnapshotPath,$evidence.SourceSnapshotSha256),@($configurationPath,$evidence.KernelProjectSha256))) {
$evidenceLocks += Open-XfeLockedImage $pair[0] $pair[1]
}
}
# Recheck the target after hashing while the source is locked. No disk state
# changes until both exact identities and the locked source hash are valid.
$lockedDisk=Get-Disk -Number $DiskNumber -ErrorAction Stop
$lockedParts=@(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $lockedDisk $lockedParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $lockedDisk $lockedParts $imageInfo.Length }
$lockedIdentity=Get-XfeMediaIdentity $lockedDisk $lockedParts $imageHash $ImageProfile $evidence
if($lockedIdentity -cne $identity){throw 'Target identity changed while locking the source; refusing before any target mutation.'}
Set-Disk -Number $DiskNumber -IsOffline $true -ErrorAction Stop
$log.Stage='copying-physical-image'
# A physical drive does not provide regular-file EOF semantics. The default
# 4096-byte FileStream buffer can read past the device when verifying its
# final 512-byte GPT sector. All raw requests are sector-aligned; keep the
# managed buffer at one sector so those reads bypass speculative buffering.
$target = [IO.FileStream]::new($rawPath, [IO.FileMode]::Open, [IO.FileAccess]::ReadWrite, [IO.FileShare]::ReadWrite, 512, [IO.FileOptions]::None)
$log.PhysicalDiskOpened=$true
# Opening a physical-drive handle does not itself write bytes.
# Recheck the number after the handle exists, before the first write, too.
$openedDisk=Get-Disk -Number $DiskNumber -ErrorAction Stop
$openedParts=@(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $openedDisk $openedParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $openedDisk $openedParts $imageInfo.Length }
if ((Get-XfeMediaIdentity $openedDisk $openedParts $imageHash $ImageProfile $evidence) -cne $identity) { throw 'Target identity changed while opening its handle; refusing before any disk byte was written.' }
$buffer = New-Object byte[] 1MB
[uint64]$copied = 0
while (($n = $source.Read($buffer, 0, $buffer.Length)) -gt 0) { $target.Write($buffer, 0, $n); $copied += $n }
if ($copied -ne $imageInfo.Length) { throw 'Incomplete source image read.' }
$target.Flush($true)
$log.Stage='verifying-original-image'
# Verify the complete copy before changing the GPT end-of-disk coordinates.
[void]$target.Seek(0, [IO.SeekOrigin]::Begin)
$hash = [Security.Cryptography.SHA256]::Create()
try {
[uint64]$remaining = $copied
while ($remaining -gt 0) { $want = [int][Math]::Min([uint64]$buffer.Length, $remaining); $got = $target.Read($buffer, 0, $want); if ($got -le 0) { throw 'Read-back verification reached end of disk.' }; [void]$hash.TransformBlock($buffer, 0, $got, $buffer, 0); $remaining -= $got }
[void]$hash.TransformFinalBlock([byte[]]@(), 0, 0)
$readHash = ([BitConverter]::ToString($hash.Hash)).Replace('-', '').ToLowerInvariant()
if ($readHash -cne $imageHash) { throw 'Full image read-back SHA256 mismatch.' }
} finally { $hash.Dispose() }
$log.Stage='relocating-gpt'
$geometry = Get-XfeAdjustedGpt $source ([uint64]$lockedDisk.Size)
Set-XfeAdjustedGpt $target $geometry
$target.Flush($true)
$log.Stage='verifying-complete-adjusted-image-and-tail'
$log.Verification = Test-XfeAdjustedReadback $source $target ([uint64]$lockedDisk.Size)
$log.Status='verified'
$log.FinalDiskState='offline'
Write-Host "Written and read-back verified disk $DiskNumber. GPT backup relocated to the physical disk end. Disk remains OFFLINE; disconnect the USB safely or reboot to UEFI."
} finally {
if ($source) { $source.Dispose() }
if ($target) { $target.Dispose() }
foreach ($handle in $evidenceLocks) { $handle.Dispose() }
$log.HandlesClosed=$true
}
} catch {
$log.Status='failed'
$log.Error=$_.Exception.Message
$log.Stack=$_.ScriptStackTrace
throw
} finally {
$log.FinishedUtc=[DateTime]::UtcNow.ToString('o')
try { $bytes=[Text.Encoding]::UTF8.GetBytes(($log | ConvertTo-Json -Depth 12));$logFile.Write($bytes,0,$bytes.Length);$logFile.Flush($true) }
finally { $logFile.Dispose() }
}
# Windows host disk writer. The default only inspects and prints a disk-specific
# plan. Never invoke -Write until a human has selected and authorized that disk.
[CmdletBinding()]
param(
[string]$Image,
[int]$DiskNumber = -1,
[string]$PlanPath,
[switch]$Write,
[string]$ConfirmToken,
[ValidateSet('LegacyPhysical256','CSharpExperimental128','IndependentCSharp128')][string]$ImageProfile = 'LegacyPhysical256',
[string]$BootResult,
[string]$PythonPath = 'python',
[string]$ResultPath,
[switch]$LoadFunctionsOnly
)
$ErrorActionPreference = 'Stop'
function Get-XfeToken([string]$Value) {
$hash = [Security.Cryptography.SHA256]::Create()
try { return ([BitConverter]::ToString($hash.ComputeHash([Text.Encoding]::UTF8.GetBytes($Value)))).Replace('-', '').ToLowerInvariant() }
finally { $hash.Dispose() }
}
function Open-XfeLockedImage([string]$Path,[string]$ExpectedHash) {
# Sharing read access only prevents concurrent writes, replacement or delete.
# Hash and the later disk copy use this very same handle, held until finally.
$stream=[IO.File]::Open($Path,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::Read)
$hash=[Security.Cryptography.SHA256]::Create()
try {
$actual=([BitConverter]::ToString($hash.ComputeHash($stream))).Replace('-','').ToLowerInvariant()
if($actual -cne $ExpectedHash){throw 'Image changed since the plan; refusing before any target mutation.'}
[void]$stream.Seek(0,[IO.SeekOrigin]::Begin)
return $stream
} catch {$stream.Dispose();throw} finally {$hash.Dispose()}
}
function Assert-XfeDisk($Disk, $Partitions, [uint64]$ImageBytes) {
if ($null -eq $Disk -or $Disk.Number -lt 0) { throw 'A concrete target disk is required.' }
if ($Disk.IsSystem -or $Disk.IsBoot) { throw 'Refusing the Windows system or boot disk.' }
if ([string]::IsNullOrWhiteSpace([string]$Disk.UniqueId)) { throw 'Disk UniqueId is unavailable; refusing ambiguous identity.' }
if ([uint64]$Disk.Size -lt $ImageBytes) { throw 'The selected disk is smaller than the image.' }
if ([uint64]$Disk.LogicalSectorSize -ne 512) { throw 'Only 512-byte logical sectors are supported by this candidate.' }
if ($Disk.IsReadOnly) { throw 'The selected disk is read-only.' }
if ([string]$Disk.BusType -in @('Virtual', 'File Backed Virtual', 'Spaces', 'iSCSI')) { throw 'Refusing virtual, Storage Spaces or remote disks in the physical writer.' }
foreach ($part in @($Partitions)) {
if ($part.IsSystem -or $part.IsBoot) { throw 'A partition on the selected disk is marked as boot or system.' }
if ($part.DriveLetter -and ([string]$part.DriveLetter + ':') -ieq $env:SystemDrive) { throw 'The selected disk contains the running Windows volume.' }
foreach ($access in @($part.AccessPaths)) {
if ($access -and $access.TrimEnd('\') -ieq $env:SystemDrive) { throw 'The selected disk contains the running Windows volume.' }
}
}
}
function Get-XfeCrc32([byte[]]$Bytes) {
[uint32]$crc = [uint32]::MaxValue
foreach ($b in $Bytes) {
$crc = $crc -bxor [uint32]$b
for ($i = 0; $i -lt 8; $i++) {
if ($crc -band 1) { $crc = [uint32](($crc -shr 1) -bxor [uint32]3988292384) }
else { $crc = $crc -shr 1 }
}
}
return $crc -bxor [uint32]::MaxValue
}
function Read-XfeBytes($Stream, [uint64]$Offset, [int]$Count) {
[void]$Stream.Seek([int64]$Offset, [IO.SeekOrigin]::Begin)
$data = New-Object byte[] $Count
$done = 0
while ($done -lt $Count) { $n = $Stream.Read($data, $done, $Count - $done); if ($n -eq 0) { throw 'Unexpected end of disk or image.' }; $done += $n }
return ,$data
}
function Write-XfeBytes($Stream, [uint64]$Offset, [byte[]]$Bytes) {
[void]$Stream.Seek([int64]$Offset, [IO.SeekOrigin]::Begin)
$Stream.Write($Bytes, 0, $Bytes.Length)
}
function Set-XfeU64([byte[]]$Bytes, [int]$Offset, [uint64]$Value) { [BitConverter]::GetBytes($Value).CopyTo($Bytes, $Offset) }
function Set-XfeU32([byte[]]$Bytes, [int]$Offset, [uint32]$Value) { [BitConverter]::GetBytes($Value).CopyTo($Bytes, $Offset) }
function Set-XfeHeaderCrc([byte[]]$Header) {
Set-XfeU32 $Header 16 0
Set-XfeU32 $Header 16 (Get-XfeCrc32 ([byte[]]$Header[0..91]))
}
function Assert-XfeEqual([byte[]]$Expected, [byte[]]$Actual) {
if ($Expected.Length -ne $Actual.Length) { throw 'Written disk verification length mismatch.' }
for ($i=0; $i -lt $Expected.Length; $i++) { if ($Expected[$i] -ne $Actual[$i]) { throw "Written disk verification mismatch at byte $i." } }
}
function Assert-XfeImage([string]$Path) {
$stream=[IO.File]::Open($Path,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::Read)
try {
if($stream.Length -ne 256MB){throw 'Only generated 256 MiB candidate images are accepted.'}
[uint64]$sectors=$stream.Length/512
[uint64]$last=$sectors-1
$mbr=Read-XfeBytes $stream 0 512
if($mbr[450] -ne 238 -or $mbr[510] -ne 85 -or $mbr[511] -ne 170 -or [BitConverter]::ToUInt32($mbr,454) -ne 1 -or [BitConverter]::ToUInt32($mbr,458) -ne $last){throw 'Invalid protective MBR.'}
if(@($mbr[462..509] | Where-Object {$_ -ne 0}).Count -ne 0){throw 'Hybrid MBR images are refused.'}
$primary=Read-XfeBytes $stream 512 512
$backup=Read-XfeBytes $stream ($last*512) 512
foreach($pair in @(@($primary,[uint64]1,$last,[uint64]2),@($backup,$last,[uint64]1,($last-32)))){
[byte[]]$h=$pair[0]
if([Text.Encoding]::ASCII.GetString($h,0,8) -cne 'EFI PART' -or [BitConverter]::ToUInt32($h,8) -ne 65536 -or [BitConverter]::ToUInt32($h,12) -ne 92 -or [BitConverter]::ToUInt32($h,20) -ne 0 -or [BitConverter]::ToUInt64($h,24) -ne $pair[1] -or [BitConverter]::ToUInt64($h,32) -ne $pair[2] -or [BitConverter]::ToUInt64($h,40) -ne 34 -or [BitConverter]::ToUInt64($h,48) -ne ($last-33) -or [BitConverter]::ToUInt64($h,72) -ne $pair[3] -or [BitConverter]::ToUInt32($h,80) -ne 128 -or [BitConverter]::ToUInt32($h,84) -ne 128){throw 'Unsupported GPT header or geometry.'}
[uint32]$crc=[BitConverter]::ToUInt32($h,16)
[byte[]]$header=$h[0..91]
Set-XfeU32 $header 16 0
if((Get-XfeCrc32 $header) -ne $crc){throw 'GPT header CRC mismatch.'}
}
Assert-XfeEqual ([byte[]]$primary[56..71]) ([byte[]]$backup[56..71])
Assert-XfeEqual ([byte[]]$primary[88..91]) ([byte[]]$backup[88..91])
$table=Read-XfeBytes $stream 1024 16384
Assert-XfeEqual $table (Read-XfeBytes $stream (($last-32)*512) 16384)
if((Get-XfeCrc32 $table) -ne [BitConverter]::ToUInt32($primary,88)){throw 'GPT table CRC mismatch.'}
$kinds=@('c12a7328-f81f-11d2-ba4b-00a0c93ec93b','58464544-4154-4100-8000-584645535953','21686148-6449-6e6f-744e-656564454649')
$starts=@([uint64]2048,[uint64]264192,[uint64]329728)
$counts=@([uint64]262144,[uint64]65536,[uint64]2048)
$identities=@()
for($i=0;$i -lt 3;$i++){
$at=$i*128
Assert-XfeEqual ([Guid]$kinds[$i]).ToByteArray() ([byte[]]$table[$at..($at+15)])
$id=([BitConverter]::ToString($table,$at+16,16))
if($id -eq '00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00' -or $identities -contains $id){throw 'Invalid or duplicate GPT partition identity.'}
$identities+=$id
if([BitConverter]::ToUInt64($table,$at+32) -ne $starts[$i] -or [BitConverter]::ToUInt64($table,$at+40) -ne ($starts[$i]+$counts[$i]-1) -or [BitConverter]::ToUInt64($table,$at+48) -ne 0){throw 'GPT partition bounds or attributes mismatch.'}
}
if(@($table[384..16383] | Where-Object {$_ -ne 0}).Count -ne 0){throw 'Unexpected additional GPT partition.'}
if([Text.Encoding]::Unicode.GetString($table,184,72).TrimEnd([char]0) -cne 'XFEDATA'){throw 'XFEDATA label mismatch.'}
$sb=Read-XfeBytes $stream (264192*512+1024) 1024
if([BitConverter]::ToUInt16($sb,56) -ne 61267 -or [BitConverter]::ToUInt32($sb,24) -ne 2 -or [BitConverter]::ToUInt16($sb,88) -ne 128 -or [BitConverter]::ToUInt32($sb,96) -ne 2){throw 'Unsupported XFEDATA ext2 format.'}
} finally {$stream.Dispose()}
}
function Assert-XfeExperimentalDisk($Disk, $Partitions, [uint64]$ImageBytes) {
Assert-XfeDisk $Disk $Partitions $ImageBytes
if ([string]$Disk.BusType -cne 'USB' -or ([string]$Disk.SerialNumber).Trim() -cne '230222336207' -or ([string]$Disk.UniqueId).Trim() -cne '5000000000002AEF') {
throw 'The experimental profile is restricted to the exact dedicated XFEstudio USB identity.'
}
if (-not $Disk.IsOffline) { throw 'The dedicated experimental USB must already be offline.' }
}
function Get-XfePartitionFingerprint($Partitions) {
$values = @($Partitions | Sort-Object PartitionNumber | ForEach-Object {
[ordered]@{Number=[uint32]$_.PartitionNumber;Offset=[uint64]$_.Offset;Size=[uint64]$_.Size;
GptType=([string]$_.GptType).ToLowerInvariant();IsBoot=[bool]$_.IsBoot;IsSystem=[bool]$_.IsSystem;
DriveLetter=([string]$_.DriveLetter).Trim([char]0);
AccessPaths=@($_.AccessPaths | Where-Object {-not [string]::IsNullOrEmpty([string]$_)} | Sort-Object)}
})
return Get-XfeToken (ConvertTo-Json -InputObject $values -Depth 5 -Compress)
}
function Get-XfeExperimentalEvidence([string]$Path, [string]$ReceiptPath, [string]$PythonCommand = 'python', [string]$Profile = 'CSharpExperimental128') {
if ([string]::IsNullOrWhiteSpace($ReceiptPath)) { throw 'CSharpExperimental128 requires -BootResult with the exact passing boot receipt.' }
$verifier = Join-Path $PSScriptRoot 'physical-media-verify.py'
$savedPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$output = @(& $PythonCommand $verifier $Path $ReceiptPath --profile $Profile 2>&1)
$exitCode = $LASTEXITCODE
} finally { $ErrorActionPreference = $savedPreference }
if ($exitCode -ne 0) { throw ('Independent C# image/evidence verification failed: ' + ($output -join [Environment]::NewLine)) }
$evidence = ($output -join [Environment]::NewLine) | ConvertFrom-Json
if ($evidence.Kind -cne 'XfeCSharpExperimentalPhysicalMediaEvidence' -or $evidence.Profile -cne $Profile -or $evidence.FullReleaseQualified -ne $false -or
$evidence.RamOnlyConfigurationVerified -ne $true -or $evidence.StorageEnabled -ne $false -or $evidence.PhysicalInputHardwareAcceptancePassed -ne $false) {
throw 'Unexpected independent experimental image verification result.'
}
return $evidence
}
function Get-XfeAdjustedGpt($Source, [uint64]$TargetBytes) {
if ($TargetBytes -lt [uint64]$Source.Length -or $TargetBytes % 512 -ne 0) { throw 'Invalid target addressing boundary.' }
[uint64]$last = $TargetBytes / 512 - 1
$primary = Read-XfeBytes $Source 512 512
$backup = Read-XfeBytes $Source ($Source.Length - 512) 512
$table = Read-XfeBytes $Source 1024 16384
$mbr = Read-XfeBytes $Source 0 512
Set-XfeU64 $primary 32 $last
Set-XfeU64 $primary 48 ($last - 33)
Set-XfeHeaderCrc $primary
Set-XfeU64 $backup 24 $last
Set-XfeU64 $backup 48 ($last - 33)
Set-XfeU64 $backup 72 ($last - 32)
Set-XfeHeaderCrc $backup
Set-XfeU32 $mbr 458 ([uint32][Math]::Min($last, [uint64][uint32]::MaxValue))
return [pscustomobject]@{LastLba=$last;Mbr=$mbr;Primary=$primary;Backup=$backup;Table=$table}
}
function Set-XfeAdjustedGpt($Target, $Geometry) {
Write-XfeBytes $Target 0 $Geometry.Mbr
Write-XfeBytes $Target 512 $Geometry.Primary
Write-XfeBytes $Target (($Geometry.LastLba - 32) * 512) $Geometry.Table
Write-XfeBytes $Target ($Geometry.LastLba * 512) $Geometry.Backup
}
function Set-XfeExpectedRange([byte[]]$Buffer, [uint64]$BufferOffset, [uint64]$PatchOffset, [byte[]]$Patch) {
[uint64]$start = [Math]::Max($BufferOffset, $PatchOffset)
[uint64]$end = [Math]::Min($BufferOffset + [uint64]$Buffer.Length, $PatchOffset + [uint64]$Patch.Length)
if ($start -lt $end) { [Array]::Copy($Patch, [int]($start-$PatchOffset), $Buffer, [int]($start-$BufferOffset), [int]($end-$start)) }
}
function Test-XfeAdjustedReadback($Source, $Target, [uint64]$TargetBytes) {
$geometry = Get-XfeAdjustedGpt $Source $TargetBytes
Assert-XfeEqual $geometry.Mbr (Read-XfeBytes $Target 0 512)
Assert-XfeEqual $geometry.Primary (Read-XfeBytes $Target 512 512)
Assert-XfeEqual $geometry.Table (Read-XfeBytes $Target 1024 16384)
Assert-XfeEqual $geometry.Table (Read-XfeBytes $Target (($geometry.LastLba-32)*512) 16384)
Assert-XfeEqual $geometry.Backup (Read-XfeBytes $Target ($geometry.LastLba*512) 512)
$expectedHash = [Security.Cryptography.SHA256]::Create()
$actualHash = [Security.Cryptography.SHA256]::Create()
try {
[uint64]$offset = 0
while ($offset -lt [uint64]$Source.Length) {
$count = [int][Math]::Min([uint64]1MB, [uint64]$Source.Length-$offset)
$expected = Read-XfeBytes $Source $offset $count
Set-XfeExpectedRange $expected $offset 0 $geometry.Mbr
Set-XfeExpectedRange $expected $offset 512 $geometry.Primary
Set-XfeExpectedRange $expected $offset (($geometry.LastLba-32)*512) $geometry.Table
Set-XfeExpectedRange $expected $offset ($geometry.LastLba*512) $geometry.Backup
$actual = Read-XfeBytes $Target $offset $count
[void]$expectedHash.TransformBlock($expected,0,$count,$expected,0)
[void]$actualHash.TransformBlock($actual,0,$count,$actual,0)
$offset += $count
}
[void]$expectedHash.TransformFinalBlock([byte[]]@(),0,0)
[void]$actualHash.TransformFinalBlock([byte[]]@(),0,0)
$expectedHex = ([BitConverter]::ToString($expectedHash.Hash)).Replace('-','').ToLowerInvariant()
$actualHex = ([BitConverter]::ToString($actualHash.Hash)).Replace('-','').ToLowerInvariant()
if ($expectedHex -cne $actualHex) { throw 'Complete capacity-adjusted image SHA256 mismatch.' }
return [pscustomobject]@{ImageBytesVerified=[uint64]$Source.Length;ExpectedAdjustedImageSha256=$expectedHex;
ActualAdjustedImageSha256=$actualHex;ActualLastLba=$geometry.LastLba;ActualTailBackupGptVerified=$true;FullImageVerified=$true}
} finally { $expectedHash.Dispose(); $actualHash.Dispose() }
}
function Get-XfeMediaIdentity($Disk, $Partitions, [string]$ImageHash, [string]$Profile, $Evidence) {
$identity = '{0}|{1}|{2}|{3}|{4}|{5}|{6}' -f $Disk.Number, $Disk.UniqueId, ([string]$Disk.SerialNumber).Trim(), $Disk.Size, $Disk.BusType, $Disk.LogicalSectorSize, $ImageHash
if ($Profile -in @('CSharpExperimental128','IndependentCSharp128')) {
$writerHash = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'physical-media.ps1') -Algorithm SHA256).Hash.ToLowerInvariant()
$verifierHash = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'physical-media-verify.py') -Algorithm SHA256).Hash.ToLowerInvariant()
$identity += '|{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}' -f $Profile,$Evidence.ImageManifestSha256,$Evidence.BootResultSha256,$Evidence.SourceSnapshotSha256,(Get-XfePartitionFingerprint $Partitions),$writerHash,$verifierHash,$Evidence.KernelProjectSha256
}
return $identity
}
if ($LoadFunctionsOnly) { return }
$log = [ordered]@{Kind='XfePhysicalMediaOperation';Profile=$ImageProfile;WriteRequested=[bool]$Write;Status='failed';
StartedUtc=[DateTime]::UtcNow.ToString('o');FullReleaseQualified=$false;StabilityPassed=$false;
CudaExecutionVerified=$false;GpuRasterizationVerified=$false;PhysicalDiskOpened=$false;HandlesClosed=$true;Stage='preflight'}
if (-not $ResultPath) {
$logDirectory = Join-Path (Split-Path -Parent $PSScriptRoot) 'build/usb-media'
[void][IO.Directory]::CreateDirectory($logDirectory)
$ResultPath = Join-Path $logDirectory ('physical-media-' + [Guid]::NewGuid().ToString('N') + '.json')
}
$logFile = [IO.File]::Open([IO.Path]::GetFullPath($ResultPath),[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
try {
if (-not $Image -or $DiskNumber -lt 0) { throw 'Specify -Image FILE.img and -DiskNumber N. Without -Write this only produces a review plan.' }
if ($env:OS -ne 'Windows_NT') { throw 'This script requires Windows Storage cmdlets.' }
$imagePath = (Resolve-Path -LiteralPath $Image).ProviderPath
$imageInfo = Get-Item -LiteralPath $imagePath
if ($imageInfo.PSIsContainer) { throw 'Expected a regular image file.' }
$manifestPath = $imagePath + '.json'
$manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
$imageHash = (Get-FileHash -LiteralPath $imagePath -Algorithm SHA256).Hash.ToLowerInvariant()
$evidence = $null
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) {
$evidence = Get-XfeExperimentalEvidence $imagePath $BootResult $PythonPath $ImageProfile
if ($evidence.ImageSha256 -cne $imageHash -or $evidence.ImageBytes -ne $imageInfo.Length) { throw 'Independent image verification disagrees with the selected source.' }
$log.ExperimentalEvidence = $evidence
$log.RamOnlyConfigurationVerified=$true
$log.StorageEnabled=$false
$log.PhysicalInputHardwareAcceptancePassed=$false
} else {
if ($BootResult) { throw '-BootResult is valid only for the explicit C# experimental profiles.' }
if ($imageInfo.Length -ne 256MB -or $manifest.schema -ne 1 -or $manifest.architecture -notin @('x86_64','i686') -or $manifest.image_bytes -ne $imageInfo.Length -or $manifest.image_sha256 -cne $imageHash -or $manifest.data_type_guid -ne '58464544-4154-4100-8000-584645535953') { throw 'Image manifest or SHA256 mismatch; regenerate using physical-image.' }
Assert-XfeImage $imagePath
}
$disk = Get-Disk -Number $DiskNumber -ErrorAction Stop
$parts = @(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $disk $parts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $disk $parts $imageInfo.Length }
$identity = Get-XfeMediaIdentity $disk $parts $imageHash $ImageProfile $evidence
$token = Get-XfeToken $identity
$plan = [ordered]@{
schema = 1
operation = 'ERASE partition table and replace disk contents with XFESystem candidate'
dry_run = -not [bool]$Write
disk_number = $disk.Number
disk_unique_id = [string]$disk.UniqueId
disk_serial = ([string]$disk.SerialNumber).Trim()
disk_friendly_name = [string]$disk.FriendlyName
disk_bus = [string]$disk.BusType
disk_bytes = [uint64]$disk.Size
logical_sector_bytes = $disk.LogicalSectorSize
existing_partitions = @($parts | Select-Object PartitionNumber, DriveLetter, Type, Size)
image = $imagePath
image_bytes = $imageInfo.Length
image_sha256 = $imageHash
image_profile = $ImageProfile
experimental_evidence = $evidence
full_release_qualified = $false
stability_passed = $false
cuda_execution_verified = $false
gpu_rasterization_verified = $false
partition_fingerprint = Get-XfePartitionFingerprint $parts
gpt_backup_relocated_to_target_end = $true
final_disk_state = 'offline'
confirm_token = $token
}
$planJson = $plan | ConvertTo-Json -Depth 6
$log.Plan = $plan
if ($PlanPath) {
$planFile = [IO.File]::Open([IO.Path]::GetFullPath($PlanPath), [IO.FileMode]::CreateNew, [IO.FileAccess]::Write)
try { $b = [Text.Encoding]::UTF8.GetBytes($planJson); $planFile.Write($b, 0, $b.Length) }
finally { $planFile.Dispose() }
}
$planJson
if (-not $Write) {
$log.Status='planned'
Write-Host 'DRY RUN ONLY: no disk was opened for writing or changed. Review disk number, identity, capacity and ALL existing partitions.'
Write-Host 'Only after the human explicitly authorizes erasing this exact disk, rerun with -Write -ConfirmToken <full token>.'
return
}
if ([string]::IsNullOrWhiteSpace($ConfirmToken) -or $ConfirmToken -cne $token) { throw 'The full confirmation token from the disk-specific plan is required. No write was performed.' }
$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'Writing a physical disk requires an elevated PowerShell window after explicit disk approval.' }
# Check again immediately before the mutation. An old plan cannot select a disk
# whose number was recycled after hotplug or whose size/identity changed.
$freshDisk = Get-Disk -Number $DiskNumber -ErrorAction Stop
$freshParts = @(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $freshDisk $freshParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $freshDisk $freshParts $imageInfo.Length }
$freshIdentity = Get-XfeMediaIdentity $freshDisk $freshParts $imageHash $ImageProfile $evidence
if ($freshIdentity -cne $identity) { throw 'Target identity changed since the plan; refusing the write.' }
$rawPath = '\\.\PhysicalDrive' + $DiskNumber
$target = $null
$source = $null
$evidenceLocks = @()
try {
$log.Stage='locking-source-and-evidence'
$source = Open-XfeLockedImage $imagePath $imageHash
$log.HandlesClosed=$false
if ($evidence) {
$configurationPath=if($ImageProfile -ceq 'IndependentCSharp128'){$evidence.KernelConfigurationPath}else{$evidence.KernelProjectPath}
if([string]::IsNullOrWhiteSpace($configurationPath)){throw 'The source-bound kernel configuration path is unavailable; refusing before target mutation.'}
foreach ($pair in @(@($evidence.ImageManifestPath,$evidence.ImageManifestSha256),@($evidence.BootResultPath,$evidence.BootResultSha256),@($evidence.SourceSnapshotPath,$evidence.SourceSnapshotSha256),@($configurationPath,$evidence.KernelProjectSha256))) {
$evidenceLocks += Open-XfeLockedImage $pair[0] $pair[1]
}
}
# Recheck the target after hashing while the source is locked. No disk state
# changes until both exact identities and the locked source hash are valid.
$lockedDisk=Get-Disk -Number $DiskNumber -ErrorAction Stop
$lockedParts=@(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $lockedDisk $lockedParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $lockedDisk $lockedParts $imageInfo.Length }
$lockedIdentity=Get-XfeMediaIdentity $lockedDisk $lockedParts $imageHash $ImageProfile $evidence
if($lockedIdentity -cne $identity){throw 'Target identity changed while locking the source; refusing before any target mutation.'}
Set-Disk -Number $DiskNumber -IsOffline $true -ErrorAction Stop
$log.Stage='copying-physical-image'
# A physical drive does not provide regular-file EOF semantics. The default
# 4096-byte FileStream buffer can read past the device when verifying its
# final 512-byte GPT sector. All raw requests are sector-aligned; keep the
# managed buffer at one sector so those reads bypass speculative buffering.
$target = [IO.FileStream]::new($rawPath, [IO.FileMode]::Open, [IO.FileAccess]::ReadWrite, [IO.FileShare]::ReadWrite, 512, [IO.FileOptions]::None)
$log.PhysicalDiskOpened=$true
# Opening a physical-drive handle does not itself write bytes.
# Recheck the number after the handle exists, before the first write, too.
$openedDisk=Get-Disk -Number $DiskNumber -ErrorAction Stop
$openedParts=@(Get-Partition -DiskNumber $DiskNumber -ErrorAction Stop)
Assert-XfeDisk $openedDisk $openedParts $imageInfo.Length
if ($ImageProfile -in @('CSharpExperimental128','IndependentCSharp128')) { Assert-XfeExperimentalDisk $openedDisk $openedParts $imageInfo.Length }
if ((Get-XfeMediaIdentity $openedDisk $openedParts $imageHash $ImageProfile $evidence) -cne $identity) { throw 'Target identity changed while opening its handle; refusing before any disk byte was written.' }
$buffer = New-Object byte[] 1MB
[uint64]$copied = 0
while (($n = $source.Read($buffer, 0, $buffer.Length)) -gt 0) { $target.Write($buffer, 0, $n); $copied += $n }
if ($copied -ne $imageInfo.Length) { throw 'Incomplete source image read.' }
$target.Flush($true)
$log.Stage='verifying-original-image'
# Verify the complete copy before changing the GPT end-of-disk coordinates.
[void]$target.Seek(0, [IO.SeekOrigin]::Begin)
$hash = [Security.Cryptography.SHA256]::Create()
try {
[uint64]$remaining = $copied
while ($remaining -gt 0) { $want = [int][Math]::Min([uint64]$buffer.Length, $remaining); $got = $target.Read($buffer, 0, $want); if ($got -le 0) { throw 'Read-back verification reached end of disk.' }; [void]$hash.TransformBlock($buffer, 0, $got, $buffer, 0); $remaining -= $got }
[void]$hash.TransformFinalBlock([byte[]]@(), 0, 0)
$readHash = ([BitConverter]::ToString($hash.Hash)).Replace('-', '').ToLowerInvariant()
if ($readHash -cne $imageHash) { throw 'Full image read-back SHA256 mismatch.' }
} finally { $hash.Dispose() }
$log.Stage='relocating-gpt'
$geometry = Get-XfeAdjustedGpt $source ([uint64]$lockedDisk.Size)
Set-XfeAdjustedGpt $target $geometry
$target.Flush($true)
$log.Stage='verifying-complete-adjusted-image-and-tail'
$log.Verification = Test-XfeAdjustedReadback $source $target ([uint64]$lockedDisk.Size)
$log.Status='verified'
$log.FinalDiskState='offline'
Write-Host "Written and read-back verified disk $DiskNumber. GPT backup relocated to the physical disk end. Disk remains OFFLINE; disconnect the USB safely or reboot to UEFI."
} finally {
if ($source) { $source.Dispose() }
if ($target) { $target.Dispose() }
foreach ($handle in $evidenceLocks) { $handle.Dispose() }
$log.HandlesClosed=$true
}
} catch {
$log.Status='failed'
$log.Error=$_.Exception.Message
$log.Stack=$_.ScriptStackTrace
throw
} finally {
$log.FinishedUtc=[DateTime]::UtcNow.ToString('o')
try { $bytes=[Text.Encoding]::UTF8.GetBytes(($log | ConvertTo-Json -Depth 12));$logFile.Write($bytes,0,$bytes.Length);$logFile.Flush($true) }
finally { $logFile.Dispose() }
}