dist/temp/WindowsUpdateTools/Private/Resolve-WUSystemPartition.ps1
|
function Resolve-WUSystemPartition { <# .SYNOPSIS Resolves System Reserved Partition disk space issues for Windows Update. .DESCRIPTION Mounts the System Reserved partition and performs comprehensive cleanup operations to free disk space for Windows Update operations. Includes: - Font file cleanup (all font files in EFI\Microsoft\Boot\Fonts) - Language folder cleanup (removes non-English language folders) - Vendor firmware cleanup (HP, Dell, Lenovo, ASUS BIOS/firmware files) - Windows Update cache cleanup Based on Microsoft support recommendations and Garytown cleanup procedures. .PARAMETER LogPath Path to the log file for detailed logging. .EXAMPLE $result = Resolve-WUSystemPartition -LogPath "C:\Logs\wu.log" .NOTES This function requires Administrator privileges. Returns an object with Success, SpaceFreed, and RebootRequired properties. Vendor firmware files are permanently deleted to maximize space recovery. .REFERENCE https://support.microsoft.com/en-us/topic/-we-couldn-t-update-system-reserved-partition-error-installing-windows-10-46865f3f-37bb-4c51-c69f-07271b6672ac https://garytown.com/low-space-on-efi-system-partition-clean-up #> [CmdletBinding()] param( [string]$LogPath ) # Initialize result object $result = [PSCustomObject]@{ Success = $false SpaceFreed = 0 RebootRequired = $false ActionsPerformed = @() ErrorMessage = $null } Write-WULog -Message "Starting System Reserved Partition remediation" -LogPath $LogPath try { # Mount the system reserved partition to Y: Write-WULog -Message "Mounting System Reserved partition to Y:..." -LogPath $LogPath $mountResult = & cmd.exe /c "mountvol Y: /s" 2>&1 if (!(Test-Path 'Y:\')) { throw "Failed to mount System Reserved partition to Y:. Output: $mountResult" } Write-WULog -Message "System Reserved partition mounted successfully" -LogPath $LogPath # Get initial free space try { $initialSpace = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq "Y:" }).FreeSpace Write-WULog -Message "Initial free space on System Reserved: $([math]::Round($initialSpace / 1MB, 2)) MB" -LogPath $LogPath } catch { Write-WULog -Message "Could not determine initial free space" -Level Warning -LogPath $LogPath $initialSpace = 0 } # Delete all font files in the Fonts directory (enhanced cleanup) $fontsPath = 'Y:\EFI\Microsoft\Boot\Fonts' if (Test-Path $fontsPath) { try { $fontFiles = Get-ChildItem -Path $fontsPath -File -ErrorAction SilentlyContinue if ($fontFiles) { $totalSize = ($fontFiles | Measure-Object -Property Length -Sum).Sum Remove-Item -Path "$fontsPath\*.*" -Force -ErrorAction Stop $result.ActionsPerformed += "Removed $($fontFiles.Count) font files ($([math]::Round($totalSize / 1MB, 2)) MB)" $result.SpaceFreed += $totalSize } } catch { Write-WULog -Message "Error cleaning font files: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } # Clean up language folders (keeping only essential languages) try { $bootPath = 'Y:\EFI\Microsoft\Boot' $preserveLanguages = @('en-US', 'en-GB', 'fr-CA') $deletedLanguages = @() $totalLangSize = 0 if (Test-Path $bootPath) { $bootLangFolders = Get-ChildItem -Path $bootPath -Directory -ErrorAction SilentlyContinue foreach ($folder in $bootLangFolders) { if ($folder.Name -match '^[a-z]{2}-[A-Z]{2}$') { if ($preserveLanguages -notcontains $folder.Name) { try { $langFiles = Get-ChildItem -Path $folder.FullName -File -Recurse -ErrorAction SilentlyContinue if ($langFiles) { $langSize = ($langFiles | Measure-Object -Property Length -Sum).Sum Remove-Item -Path $folder.FullName -Recurse -Force -ErrorAction Stop $deletedLanguages += $folder.Name $totalLangSize += $langSize } } catch { Write-WULog -Message "Failed to delete language folder $($folder.Name): $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } } } if ($deletedLanguages.Count -gt 0) { $result.ActionsPerformed += "Removed $($deletedLanguages.Count) language folders ($([math]::Round($totalLangSize / 1MB, 2)) MB)" $result.SpaceFreed += $totalLangSize } } } catch { Write-WULog -Message "Error cleaning language folders: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } # Handle manufacturer-specific cleanup (enhanced with additional vendor paths) try { $manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer Write-WULog -Message "Detected Manufacturer: $manufacturer" -LogPath $LogPath # Generic vendor firmware cleanup (from Garytown recommendations) $vendorPaths = @( 'Y:\EFI\HP\BIOS\Previous', 'Y:\EFI\HP\BIOS\Current', 'Y:\EFI\HP\DEVFW', 'Y:\EFI\Lenovo\fw', 'Y:\EFI\Dell\BIOS', 'Y:\EFI\ASUS\fw' ) $vendorFilesDeleted = 0 $totalVendorSize = 0 foreach ($vendorPath in $vendorPaths) { if (Test-Path $vendorPath) { try { $vendorFiles = Get-ChildItem -Path $vendorPath -File -Recurse -ErrorAction SilentlyContinue if ($vendorFiles) { $vendorSize = ($vendorFiles | Measure-Object -Property Length -Sum).Sum $vendorName = ($vendorPath -split '\\')[2] # Extract vendor name from path # Delete vendor firmware files directly Remove-Item -Path $vendorPath -Recurse -Force -ErrorAction Stop $vendorFilesDeleted += $vendorFiles.Count $totalVendorSize += $vendorSize } } catch { Write-WULog -Message "Failed to delete vendor path $vendorPath : $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } } if ($vendorFilesDeleted -gt 0) { $result.ActionsPerformed += "Deleted $vendorFilesDeleted vendor firmware files ($([math]::Round($totalVendorSize / 1MB, 2)) MB)" $result.SpaceFreed += $totalVendorSize } # Manufacturer-specific additional cleanup switch -Wildcard ($manufacturer) { "*Dell*" { # Check for additional Dell paths not covered above $additionalDellPaths = @('Y:\Dell') foreach ($dellPath in $additionalDellPaths) { if (Test-Path $dellPath) { $dellFiles = Get-ChildItem -Path $dellPath -File -Recurse -ErrorAction SilentlyContinue if ($dellFiles) { $totalSize = ($dellFiles | Measure-Object -Property Length -Sum).Sum if ($totalSize -gt 10MB) { # Only clean if significant space try { Remove-Item -Path $dellPath -Recurse -Force -ErrorAction Stop $result.ActionsPerformed += "Deleted additional Dell files ($([math]::Round($totalSize / 1MB, 2)) MB)" $result.SpaceFreed += $totalSize } catch { Write-WULog -Message "Failed to delete additional Dell files: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } } } } } } } catch { Write-WULog -Message "Error during manufacturer-specific cleanup: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } # Additional cleanup - old Windows Update cache files on System Reserved try { $updateCachePaths = @( 'Y:\$Windows.~BT', 'Y:\$Windows.~WS', 'Y:\Windows.old' ) $cacheFilesDeleted = 0 $totalCacheSize = 0 foreach ($cachePath in $updateCachePaths) { if (Test-Path $cachePath) { $cacheFiles = Get-ChildItem -Path $cachePath -Recurse -File -ErrorAction SilentlyContinue if ($cacheFiles) { $cacheSize = ($cacheFiles | Measure-Object -Property Length -Sum).Sum if ($cacheSize -gt 0) { # Only remove if it's actually taking significant space and is old $cacheAge = (Get-Date) - (Get-Item $cachePath).LastWriteTime if ($cacheAge.TotalDays -gt 7 -and $cacheSize -gt 50MB) { Remove-Item -Path $cachePath -Recurse -Force -ErrorAction Stop $cacheFilesDeleted += $cacheFiles.Count $totalCacheSize += $cacheSize } } } } } if ($cacheFilesDeleted -gt 0) { $result.ActionsPerformed += "Removed old Windows Update cache ($([math]::Round($totalCacheSize / 1MB, 2)) MB)" $result.SpaceFreed += $totalCacheSize } } catch { Write-WULog -Message "Error cleaning Windows Update cache: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } # Check final free space try { $finalSpace = (Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq "Y:" }).FreeSpace $freedSpace = $finalSpace - $initialSpace Write-WULog -Message "Final free space on System Reserved: $([math]::Round($finalSpace / 1MB, 2)) MB" -LogPath $LogPath Write-WULog -Message "Space freed: $([math]::Round($freedSpace / 1MB, 2)) MB" -LogPath $LogPath # Update the actual space freed (may be different from file sizes due to cluster sizes) if ($freedSpace -gt 0) { $result.SpaceFreed = $freedSpace } } catch { Write-WULog -Message "Could not determine final free space" -Level Warning -LogPath $LogPath } $result.Success = $true } catch { $result.ErrorMessage = $_.Exception.Message Write-WULog -Message "Error during System Reserved partition remediation: $($_.Exception.Message)" -Level Error -LogPath $LogPath } finally { # Always attempt to unmount Write-WULog -Message "Unmounting System Reserved partition..." -LogPath $LogPath try { $unmountResult = & cmd.exe /c "mountvol Y: /d" 2>&1 # Give it a moment to unmount Start-Sleep -Seconds 2 if (Test-Path 'Y:\') { Write-WULog -Message "Y: drive still mounted after unmount attempt" -Level Warning -LogPath $LogPath # Try force unmount try { & cmd.exe /c "mountvol Y: /d" 2>&1 | Out-Null Start-Sleep -Seconds 1 } catch { Write-WULog -Message "Force unmount also failed" -Level Warning -LogPath $LogPath } } } catch { Write-WULog -Message "Error during unmount: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } # Summary if ($result.Success) { Write-WULog -Message "System Reserved Partition remediation completed successfully" -LogPath $LogPath if ($result.SpaceFreed -gt 0) { Write-WULog -Message "Total space freed: $([math]::Round($result.SpaceFreed / 1MB, 2)) MB" -LogPath $LogPath } } else { Write-WULog -Message "System Reserved Partition remediation failed: $($result.ErrorMessage)" -Level Error -LogPath $LogPath } return $result } |