src/Private/Backup/Test-BackupArchiveContentIntegrity.ps1
|
function Test-BackupArchiveContentIntegrity { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ProjectPath, [Parameter(Mandatory)] [string]$ArchivePath, [hashtable]$SourceIndex, [ValidateSet("SHA256", "SHA1", "MD5")] [string]$Algorithm = "SHA256" ) if (-not (Test-Path -Path $ProjectPath -PathType Container)) { Write-RenderKitLog -Level Error -Message "Project path '$ProjectPath' does not exist." throw "Project path '$ProjectPath' does not exist." } if (-not (Test-Path -Path $ArchivePath -PathType Leaf)) { Write-RenderKitLog -Level Error -Message "Archive path '$ArchivePath' does not exist." throw "Archive path '$ArchivePath' does not exist." } Write-RenderKitLog -Level Debug -Message "Test-BackupArchiveContentIntegrity started: ProjectPath='$ProjectPath', ArchivePath='$ArchivePath', Algorithm='$Algorithm', SourceIndexProvided=$($null -ne $SourceIndex -and $SourceIndex.Count -gt 0)." $extractRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("renderkit-archive-verify-" + [guid]::NewGuid().ToString("N")) New-Item -ItemType Directory -Path $extractRoot -Force | Out-Null try { Expand-Archive -Path $ArchivePath -DestinationPath $extractRoot -Force $projectLeafName = Split-Path -Path $ProjectPath -Leaf $candidateRoot = Join-Path $extractRoot $projectLeafName $archiveProjectRoot = if (Test-Path -Path $candidateRoot -PathType Container) { $candidateRoot } else { $topLevelDirectories = @( Get-ChildItem -Path $extractRoot -Directory -Force -ErrorAction SilentlyContinue ) $topLevelFiles = @( Get-ChildItem -Path $extractRoot -File -Force -ErrorAction SilentlyContinue ) if ($topLevelDirectories.Count -eq 1 -and $topLevelFiles.Count -eq 0) { $topLevelDirectories[0].FullName } else { $extractRoot } } $effectiveSourceIndex = $SourceIndex if ($null -eq $effectiveSourceIndex -or $effectiveSourceIndex.Count -eq 0) { $effectiveSourceIndex = Get-BackupFileHashIndex ` -RootPath $ProjectPath ` -BasePath $ProjectPath ` -Algorithm $Algorithm } $archiveIndex = Get-BackupFileHashIndex ` -RootPath $archiveProjectRoot ` -BasePath $archiveProjectRoot ` -Algorithm $Algorithm $sourcePaths = @($effectiveSourceIndex.Keys | Sort-Object) $archivePaths = @($archiveIndex.Keys | Sort-Object) $missingInArchive = @($sourcePaths | Where-Object { -not $archiveIndex.ContainsKey($_) }) $extraInArchive = @($archivePaths | Where-Object { -not $effectiveSourceIndex.ContainsKey($_) }) $hashMismatches = New-Object System.Collections.Generic.List[object] foreach ($path in $sourcePaths) { if (-not $archiveIndex.ContainsKey($path)) { continue } $sourceEntry = $effectiveSourceIndex[$path] $archiveEntry = $archiveIndex[$path] if ($sourceEntry.Length -ne $archiveEntry.Length -or -not $sourceEntry.Hash.Equals($archiveEntry.Hash, [System.StringComparison]::OrdinalIgnoreCase)) { $hashMismatches.Add([PSCustomObject]@{ RelativePath = $path SourceLength = [int64]$sourceEntry.Length ArchiveLength = [int64]$archiveEntry.Length SourceHash = [string]$sourceEntry.Hash ArchiveHash = [string]$archiveEntry.Hash }) } } $isMatch = ($missingInArchive.Count -eq 0 -and $extraInArchive.Count -eq 0 -and $hashMismatches.Count -eq 0) Write-RenderKitLog -Level Debug -Message ( "Archive integrity computed: SourceFiles={0}, ArchiveFiles={1}, Missing={2}, Extra={3}, HashMismatches={4}." -f $sourcePaths.Count, $archivePaths.Count, $missingInArchive.Count, $extraInArchive.Count, $hashMismatches.Count ) return [PSCustomObject]@{ IsMatch = $isMatch Algorithm = $Algorithm SourceFileCount = $sourcePaths.Count ArchiveFileCount = $archivePaths.Count MissingInArchiveCount = $missingInArchive.Count ExtraInArchiveCount = $extraInArchive.Count HashMismatchCount = $hashMismatches.Count MissingInArchive = @($missingInArchive) ExtraInArchive = @($extraInArchive) HashMismatches = @($hashMismatches.ToArray()) } } finally { if (Test-Path -Path $extractRoot -PathType Container) { Remove-Item -Path $extractRoot -Recurse -Force -ErrorAction SilentlyContinue } } } |