feat: register local agent project workspaces
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
param(
|
||||
[ValidateSet('Setup', 'Mount', 'Status', 'Remove')]
|
||||
[string]$Action = 'Setup',
|
||||
[switch]$DirectElevated,
|
||||
[switch]$ConfirmRemove
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$sandboxRoot = Join-Path $env:LOCALAPPDATA 'SovereignAI\agent-sandbox'
|
||||
$vhdPath = Join-Path $sandboxRoot 'execution.vhdx'
|
||||
$mountPath = Join-Path $sandboxRoot 'workspace'
|
||||
$volumeLabel = 'SOVEREIGNAI_EXEC'
|
||||
$virtualSizeBytes = 1GB
|
||||
$volumeSizeToleranceBytes = 8MB
|
||||
|
||||
function Test-Administrator {
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Invoke-DiskPartScript([string[]]$Commands) {
|
||||
$scriptPath = Join-Path $env:TEMP ("SovereignAI-DiskPart-{0}.txt" -f [guid]::NewGuid().ToString('N'))
|
||||
try {
|
||||
Set-Content -LiteralPath $scriptPath -Value ($Commands + 'exit') -Encoding ascii
|
||||
$output = & "$env:WINDIR\System32\diskpart.exe" /s $scriptPath 2>&1 | Out-String
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0 -or $output -match '(?im)^DiskPart has encountered an error') {
|
||||
throw "DiskPart failed (exit $exitCode): $output"
|
||||
}
|
||||
return $output
|
||||
} finally {
|
||||
if (Test-Path -LiteralPath $scriptPath -PathType Leaf) {
|
||||
Remove-Item -LiteralPath $scriptPath -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ExecutionVolume {
|
||||
$volumes = @(Get-Volume -FileSystemLabel $volumeLabel -ErrorAction SilentlyContinue)
|
||||
if ($volumes.Count -gt 1) { throw "More than one volume is labeled $volumeLabel; refusing to choose." }
|
||||
if ($volumes.Count -eq 0) { return $null }
|
||||
return $volumes[0]
|
||||
}
|
||||
|
||||
function Assert-ExecutionVolume($Volume) {
|
||||
if (-not $Volume) { throw "The expected $volumeLabel volume is not attached." }
|
||||
if ($Volume.FileSystem -ne 'NTFS') { throw "Expected NTFS but found '$($Volume.FileSystem)'." }
|
||||
if ($Volume.Size -gt ($virtualSizeBytes + $volumeSizeToleranceBytes) -or
|
||||
$Volume.Size -lt ($virtualSizeBytes - 32MB)) {
|
||||
throw "Unexpected sandbox volume size: $($Volume.Size) bytes."
|
||||
}
|
||||
$mountedVolumePath = (& "$env:WINDIR\System32\mountvol.exe" $mountPath /L 2>&1 | Out-String).Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or
|
||||
-not $mountedVolumePath.Equals($Volume.Path.Trim(), [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "The sandbox mount path does not resolve to the expected VHDX volume. mountvol='$mountedVolumePath', volume='$($Volume.Path)'."
|
||||
}
|
||||
$diskImage = Get-DiskImage -ImagePath $vhdPath -ErrorAction SilentlyContinue
|
||||
if (-not $diskImage -or -not $diskImage.Attached) {
|
||||
throw 'The VHDX backing file is not attached.'
|
||||
}
|
||||
$disk = $diskImage | Get-Disk
|
||||
if ($disk.Size -gt $virtualSizeBytes -or $disk.Size -lt ($virtualSizeBytes - 1MB)) {
|
||||
throw "The VHDX virtual capacity is unexpected: $($disk.Size) bytes."
|
||||
}
|
||||
return [pscustomobject]@{
|
||||
vhdx_path = $vhdPath
|
||||
mount_path = $mountPath
|
||||
virtual_size_bytes = $disk.Size
|
||||
filesystem_size_bytes = $Volume.Size
|
||||
filesystem = $Volume.FileSystem
|
||||
filesystem_label = $Volume.FileSystemLabel
|
||||
free_bytes = $Volume.SizeRemaining
|
||||
attached = $diskImage.Attached
|
||||
}
|
||||
}
|
||||
|
||||
function Mount-ExecutionDisk {
|
||||
if (-not (Test-Path -LiteralPath $vhdPath -PathType Leaf)) {
|
||||
throw "The sandbox VHDX does not exist: $vhdPath"
|
||||
}
|
||||
$volume = Get-ExecutionVolume
|
||||
if (-not $volume) {
|
||||
Ensure-MountDirectory
|
||||
$commands = @(
|
||||
"select vdisk file=`"$vhdPath`"",
|
||||
'attach vdisk',
|
||||
'select partition 1',
|
||||
"assign mount=`"$mountPath`""
|
||||
)
|
||||
Invoke-DiskPartScript $commands | Out-Null
|
||||
$volume = Get-ExecutionVolume
|
||||
}
|
||||
Assert-ExecutionVolume $volume | Out-Null
|
||||
return $volume
|
||||
}
|
||||
|
||||
function Ensure-MountDirectory {
|
||||
if (-not (Test-Path -LiteralPath $sandboxRoot -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $sandboxRoot -Force | Out-Null
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $mountPath -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $mountPath | Out-Null
|
||||
}
|
||||
$mountItem = Get-Item -LiteralPath $mountPath -Force
|
||||
if (($mountItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
|
||||
throw "The sandbox mount directory is unexpectedly a reparse point: $mountPath"
|
||||
}
|
||||
if (Get-ChildItem -LiteralPath $mountPath -Force) {
|
||||
throw "The intended sandbox mount directory is not empty: $mountPath"
|
||||
}
|
||||
}
|
||||
|
||||
if ($Action -eq 'Status') {
|
||||
$volume = Get-ExecutionVolume
|
||||
if (-not $volume) {
|
||||
[pscustomobject]@{ configured = (Test-Path -LiteralPath $vhdPath); attached = $false; vhdx_path = $vhdPath; mount_path = $mountPath } | ConvertTo-Json
|
||||
exit 0
|
||||
}
|
||||
Assert-ExecutionVolume $volume | ConvertTo-Json -Depth 4
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($Action -eq 'Remove' -and -not $ConfirmRemove) {
|
||||
throw 'Removal deletes the dedicated sandbox disk and all data stored on it. Re-run with -ConfirmRemove to proceed.'
|
||||
}
|
||||
|
||||
if (-not $DirectElevated -and -not (Test-Administrator)) {
|
||||
$powershell = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$arguments = @(
|
||||
'-NoProfile',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', ('"{0}"' -f $MyInvocation.MyCommand.Path),
|
||||
'-Action', $Action,
|
||||
'-DirectElevated'
|
||||
)
|
||||
if ($ConfirmRemove) { $arguments += '-ConfirmRemove' }
|
||||
try {
|
||||
$child = Start-Process -FilePath $powershell -ArgumentList ($arguments -join ' ') `
|
||||
-Verb RunAs -WindowStyle Hidden -PassThru -Wait
|
||||
} catch {
|
||||
throw "Windows did not grant the required administrator token: $($_.Exception.Message)"
|
||||
}
|
||||
exit $child.ExitCode
|
||||
}
|
||||
if (-not (Test-Administrator)) { throw 'This action requires an elevated administrator token.' }
|
||||
|
||||
switch ($Action) {
|
||||
'Setup' {
|
||||
if (Test-Path -LiteralPath $vhdPath -PathType Leaf) {
|
||||
$volume = Get-ExecutionVolume
|
||||
if (-not $volume) {
|
||||
$volume = Mount-ExecutionDisk
|
||||
}
|
||||
Assert-ExecutionVolume $volume | ConvertTo-Json -Depth 4
|
||||
break
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $vhdPath) {
|
||||
throw "A non-file already occupies the expected VHDX path: $vhdPath"
|
||||
}
|
||||
if (Get-ExecutionVolume) {
|
||||
throw "A volume labeled $volumeLabel exists but is not backed by the expected VHDX path."
|
||||
}
|
||||
$driveRoot = [System.IO.Path]::GetPathRoot($env:LOCALAPPDATA)
|
||||
$availableBytes = [System.IO.DriveInfo]::new($driveRoot).AvailableFreeSpace
|
||||
if ($availableBytes -lt (2 * $virtualSizeBytes)) {
|
||||
throw "Less than 2 GiB is free on $driveRoot; refusing to create a 1 GiB sandbox disk."
|
||||
}
|
||||
Ensure-MountDirectory
|
||||
|
||||
try {
|
||||
$commands = @(
|
||||
"create vdisk file=`"$vhdPath`" maximum=1024 type=expandable",
|
||||
"select vdisk file=`"$vhdPath`"",
|
||||
'attach vdisk',
|
||||
'create partition primary',
|
||||
"format fs=ntfs quick label=$volumeLabel",
|
||||
"assign mount=`"$mountPath`""
|
||||
)
|
||||
$output = Invoke-DiskPartScript $commands
|
||||
$volume = Get-ExecutionVolume
|
||||
Assert-ExecutionVolume $volume | ConvertTo-Json -Depth 4
|
||||
Write-Host 'The VHDX has a 1 GiB virtual maximum. Its data volume is mounted only at the dedicated workspace path.'
|
||||
Write-Host 'Agent command execution remains disabled until the broker and API enforce snapshots, cleanup, and per-run approval.'
|
||||
} catch {
|
||||
if (Test-Path -LiteralPath $vhdPath -PathType Leaf) {
|
||||
try {
|
||||
$mountItem = Get-Item -LiteralPath $mountPath -Force -ErrorAction SilentlyContinue
|
||||
if ($mountItem -and ($mountItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
& "$env:WINDIR\System32\mountvol.exe" $mountPath /D | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "MountVol could not remove the partial mount ($LASTEXITCODE)." }
|
||||
}
|
||||
Invoke-DiskPartScript @("select vdisk file=`"$vhdPath`"", 'detach vdisk') | Out-Null
|
||||
Remove-Item -LiteralPath $vhdPath -Force
|
||||
} catch {
|
||||
Write-Warning "Automatic cleanup could not remove the failed VHDX; inspect $vhdPath. $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
throw
|
||||
}
|
||||
}
|
||||
'Mount' {
|
||||
$volume = Mount-ExecutionDisk
|
||||
Assert-ExecutionVolume $volume | ConvertTo-Json -Depth 4
|
||||
}
|
||||
'Remove' {
|
||||
if (-not (Test-Path -LiteralPath $vhdPath -PathType Leaf)) {
|
||||
Write-Host 'No sandbox VHDX exists at the expected path.'
|
||||
break
|
||||
}
|
||||
$resolvedRoot = [System.IO.Path]::GetFullPath($sandboxRoot).TrimEnd('\')
|
||||
$resolvedVhd = [System.IO.Path]::GetFullPath($vhdPath)
|
||||
if (-not $resolvedVhd.StartsWith($resolvedRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Refusing to remove a VHDX outside the dedicated sandbox directory: $resolvedVhd"
|
||||
}
|
||||
$volume = Get-ExecutionVolume
|
||||
if ($volume) {
|
||||
Assert-ExecutionVolume $volume | Out-Null
|
||||
& "$env:WINDIR\System32\mountvol.exe" $mountPath /D | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "MountVol could not remove the sandbox mount ($LASTEXITCODE)." }
|
||||
Invoke-DiskPartScript @("select vdisk file=`"$vhdPath`"", 'detach vdisk') | Out-Null
|
||||
}
|
||||
Remove-Item -LiteralPath $vhdPath -Force
|
||||
if ((Test-Path -LiteralPath $mountPath -PathType Container) -and
|
||||
-not (Get-ChildItem -LiteralPath $mountPath -Force)) {
|
||||
Remove-Item -LiteralPath $mountPath -Force
|
||||
}
|
||||
if ((Test-Path -LiteralPath $sandboxRoot -PathType Container) -and
|
||||
-not (Get-ChildItem -LiteralPath $sandboxRoot -Force)) {
|
||||
Remove-Item -LiteralPath $sandboxRoot -Force
|
||||
}
|
||||
Write-Host 'Removed the exact dedicated execution VHDX and its mount directory.'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
param(
|
||||
[switch]$DirectElevated,
|
||||
[string]$ReportPath
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Test-Administrator {
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
if (-not $DirectElevated) {
|
||||
if (Test-Administrator) {
|
||||
$DirectElevated = $true
|
||||
} else {
|
||||
$ReportPath = Join-Path $env:TEMP ("SovereignAI-VHDQuota-{0}.json" -f [guid]::NewGuid().ToString('N'))
|
||||
$powershell = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$scriptPath = $MyInvocation.MyCommand.Path
|
||||
$arguments = @(
|
||||
'-NoProfile',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', ('"{0}"' -f $scriptPath),
|
||||
'-DirectElevated',
|
||||
'-ReportPath', ('"{0}"' -f $ReportPath)
|
||||
) -join ' '
|
||||
try {
|
||||
$child = Start-Process -FilePath $powershell -ArgumentList $arguments `
|
||||
-Verb RunAs -WindowStyle Hidden -PassThru -Wait
|
||||
} catch {
|
||||
Write-Error "Windows did not grant the required administrator token: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
if (Test-Path -LiteralPath $ReportPath -PathType Leaf) {
|
||||
Get-Content -LiteralPath $ReportPath -Raw
|
||||
Remove-Item -LiteralPath $ReportPath -Force
|
||||
} else {
|
||||
Write-Error "The elevated quota probe exited $($child.ExitCode) without producing its report."
|
||||
}
|
||||
exit $child.ExitCode
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Administrator)) {
|
||||
throw 'The direct probe requires an elevated administrator token.'
|
||||
}
|
||||
|
||||
$probeParent = Join-Path $env:LOCALAPPDATA 'SovereignAI\execution-sandbox-probes'
|
||||
$probeDirectory = Join-Path $probeParent ("vhd-quota-{0}" -f [guid]::NewGuid().ToString('N'))
|
||||
$vhdPath = Join-Path $probeDirectory 'quota-probe.vhdx'
|
||||
$mountPath = Join-Path $probeDirectory 'volume'
|
||||
$diskpartScript = Join-Path $probeDirectory 'diskpart.txt'
|
||||
$detachScript = Join-Path $probeDirectory 'detach.txt'
|
||||
$testFile = Join-Path $mountPath 'capacity-probe.bin'
|
||||
$maximumMegabytes = 64
|
||||
$report = $null
|
||||
$resultCode = 0
|
||||
$cleanupError = $null
|
||||
$mounted = $false
|
||||
|
||||
try {
|
||||
$systemDrive = [System.IO.Path]::GetPathRoot($env:LOCALAPPDATA)
|
||||
$availableBytes = [System.IO.DriveInfo]::new($systemDrive).AvailableFreeSpace
|
||||
if ($availableBytes -lt 256MB) {
|
||||
throw 'Less than 256 MiB is free on the system drive; refusing to create the 64 MiB probe disk.'
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $probeDirectory -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path $mountPath -Force | Out-Null
|
||||
$diskpartCommands = @(
|
||||
"create vdisk file=`"$vhdPath`" maximum=$maximumMegabytes type=expandable",
|
||||
"select vdisk file=`"$vhdPath`"",
|
||||
'attach vdisk',
|
||||
'create partition primary',
|
||||
'format fs=ntfs quick label=SOVEREIGNAI_PROBE',
|
||||
"assign mount=`"$mountPath`"",
|
||||
'exit'
|
||||
)
|
||||
Set-Content -LiteralPath $diskpartScript -Value $diskpartCommands -Encoding ascii
|
||||
$diskpartOutput = & "$env:WINDIR\System32\diskpart.exe" /s $diskpartScript 2>&1 | Out-String
|
||||
$diskpartExitCode = $LASTEXITCODE
|
||||
if ($diskpartExitCode -ne 0) {
|
||||
throw "DiskPart failed with exit code $diskpartExitCode. $diskpartOutput"
|
||||
}
|
||||
|
||||
$deadline = (Get-Date).AddSeconds(15)
|
||||
$volumes = @(Get-Volume -FileSystemLabel 'SOVEREIGNAI_PROBE' -ErrorAction SilentlyContinue)
|
||||
while ($volumes.Count -eq 0 -and (Get-Date) -lt $deadline) {
|
||||
Start-Sleep -Milliseconds 250
|
||||
$volumes = @(Get-Volume -FileSystemLabel 'SOVEREIGNAI_PROBE' -ErrorAction SilentlyContinue)
|
||||
}
|
||||
if ($volumes.Count -ne 1) {
|
||||
$mountvolOutput = & "$env:WINDIR\System32\mountvol.exe" $mountPath /L 2>&1 | Out-String
|
||||
throw "Expected exactly one formatted probe volume. mountvol: $mountvolOutput DiskPart: $diskpartOutput"
|
||||
}
|
||||
$volume = $volumes[0]
|
||||
$mountvolOutput = (& "$env:WINDIR\System32\mountvol.exe" $mountPath /L 2>&1 | Out-String).Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or
|
||||
-not $mountvolOutput.Trim().Equals($volume.Path.Trim(), [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "The test mount path does not resolve to the formatted probe VHDX. mountvol='$mountvolOutput'; volume='$($volume.Path)'. DiskPart: $diskpartOutput"
|
||||
}
|
||||
if ($volume.Size -gt (($maximumMegabytes + 2) * 1MB) -or $volume.Size -lt (48MB)) {
|
||||
$mountvolOutput = & "$env:WINDIR\System32\mountvol.exe" $mountPath /L 2>&1 | Out-String
|
||||
throw "Unexpected probe volume capacity: $($volume.Size) bytes. mountvol: $mountvolOutput DiskPart: $diskpartOutput"
|
||||
}
|
||||
$mounted = $true
|
||||
|
||||
$buffer = [byte[]]::new(1MB)
|
||||
for ($index = 0; $index -lt $buffer.Length; $index++) {
|
||||
$buffer[$index] = [byte](($index * 31 + 97) % 251)
|
||||
}
|
||||
$writtenBytes = [long]0
|
||||
$diskFullException = $null
|
||||
$stream = [System.IO.FileStream]::new(
|
||||
$testFile,
|
||||
[System.IO.FileMode]::CreateNew,
|
||||
[System.IO.FileAccess]::Write,
|
||||
[System.IO.FileShare]::None,
|
||||
$buffer.Length,
|
||||
[System.IO.FileOptions]::SequentialScan
|
||||
)
|
||||
try {
|
||||
while ($true) {
|
||||
$stream.Write($buffer, 0, $buffer.Length)
|
||||
$writtenBytes += $buffer.Length
|
||||
}
|
||||
} catch [System.IO.IOException] {
|
||||
$diskFullException = $_.Exception
|
||||
} finally {
|
||||
$stream.Dispose()
|
||||
}
|
||||
|
||||
$errorCode = if ($diskFullException) { $diskFullException.HResult -band 0xffff } else { $null }
|
||||
$volume = Get-Volume -UniqueId $volume.UniqueId
|
||||
$freeAtLimit = $volume.SizeRemaining
|
||||
if (-not $diskFullException -or $errorCode -ne 112) {
|
||||
throw "The write did not stop with ERROR_DISK_FULL (112). HResult=$($diskFullException.HResult); message=$($diskFullException.Message)"
|
||||
}
|
||||
if ($freeAtLimit -gt 1MB) {
|
||||
throw "The write stopped with $freeAtLimit bytes still free; the volume-capacity boundary was not confirmed."
|
||||
}
|
||||
|
||||
$vhdBytes = (Get-Item -LiteralPath $vhdPath).Length
|
||||
$report = [pscustomobject]@{
|
||||
passed = $true
|
||||
administrator_token = $true
|
||||
diskpart_exit_code = $diskpartExitCode
|
||||
volume_label = $volume.FileSystemLabel
|
||||
virtual_volume_bytes = $volume.Size
|
||||
bytes_written_before_error = $writtenBytes
|
||||
error_code = $errorCode
|
||||
error_message = $diskFullException.Message
|
||||
free_bytes_at_limit = $freeAtLimit
|
||||
dynamic_vhdx_bytes_at_limit = $vhdBytes
|
||||
probe_vhdx_max_megabytes = $maximumMegabytes
|
||||
isolation = 'temporary NTFS filesystem inside a dynamically expanding VHDX with a fixed virtual capacity'
|
||||
}
|
||||
} catch {
|
||||
$resultCode = 1
|
||||
$report = [pscustomobject]@{
|
||||
passed = $false
|
||||
administrator_token = (Test-Administrator)
|
||||
error = $_.Exception.Message
|
||||
probe_vhdx_max_megabytes = $maximumMegabytes
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if ($stream) { $stream.Dispose() }
|
||||
if (Test-Path -LiteralPath $testFile -PathType Leaf) {
|
||||
Remove-Item -LiteralPath $testFile -Force
|
||||
}
|
||||
if (Test-Path -LiteralPath $vhdPath -PathType Leaf) {
|
||||
$mountItem = Get-Item -LiteralPath $mountPath -Force -ErrorAction SilentlyContinue
|
||||
if ($mountItem -and ($mountItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
& "$env:WINDIR\System32\mountvol.exe" $mountPath /D | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "MountVol could not remove the exact temporary mount point ($LASTEXITCODE)." }
|
||||
}
|
||||
Set-Content -LiteralPath $detachScript -Value @(
|
||||
"select vdisk file=`"$vhdPath`"",
|
||||
'detach vdisk',
|
||||
'exit'
|
||||
) -Encoding ascii
|
||||
& "$env:WINDIR\System32\diskpart.exe" /s $detachScript 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "DiskPart could not detach the temporary VHDX ($LASTEXITCODE)." }
|
||||
Remove-Item -LiteralPath $vhdPath -Force
|
||||
}
|
||||
foreach ($file in @($diskpartScript, $detachScript)) {
|
||||
if (Test-Path -LiteralPath $file -PathType Leaf) { Remove-Item -LiteralPath $file -Force }
|
||||
}
|
||||
if (Test-Path -LiteralPath $mountPath -PathType Container) {
|
||||
$mountItem = Get-Item -LiteralPath $mountPath -Force
|
||||
if (($mountItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -eq 0 -and
|
||||
-not (Get-ChildItem -LiteralPath $mountPath -Force)) {
|
||||
Remove-Item -LiteralPath $mountPath -Force
|
||||
}
|
||||
}
|
||||
if ((Test-Path -LiteralPath $probeDirectory -PathType Container) -and
|
||||
-not (Get-ChildItem -LiteralPath $probeDirectory -Force)) {
|
||||
Remove-Item -LiteralPath $probeDirectory -Force
|
||||
}
|
||||
if ((Test-Path -LiteralPath $probeParent -PathType Container) -and
|
||||
-not (Get-ChildItem -LiteralPath $probeParent -Force)) {
|
||||
Remove-Item -LiteralPath $probeParent -Force
|
||||
}
|
||||
} catch {
|
||||
$cleanupError = $_.Exception.Message
|
||||
$resultCode = 1
|
||||
}
|
||||
if ($cleanupError) { $report | Add-Member -NotePropertyName cleanup_error -NotePropertyValue $cleanupError -Force }
|
||||
if ($ReportPath) {
|
||||
$report | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $ReportPath -Encoding utf8
|
||||
} else {
|
||||
$report | ConvertTo-Json -Depth 5
|
||||
}
|
||||
}
|
||||
|
||||
exit $resultCode
|
||||
Reference in New Issue
Block a user