Skip to content

Instantly share code, notes, and snippets.

@MyITGuy
Last active May 20, 2024 22:01
Show Gist options
  • Save MyITGuy/618fb5cf6c5938095eb97c63dfce1e8e to your computer and use it in GitHub Desktop.
Save MyITGuy/618fb5cf6c5938095eb97c63dfce1e8e to your computer and use it in GitHub Desktop.
On Windows, the WinSxS InFlight folder can grow to thousands of folders, subfolders and files. When DISM component analysis and cleanup operations are performed, this causes enumeration of the folders, subfolders and files.
function Invoke-WinSxSTempCleanup {
[CmdletBinding()]
param(
[string[]]
$ScanID
)
begin {
#region Enable Privilege function
function Enable-Privilege {
param(
## The privilege to adjust. This set is taken from
## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
[ValidateSet(
"SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
"SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
"SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
"SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
"SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
"SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
"SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
"SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
"SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
"SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
"SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
$Privilege,
## The process on which to adjust the privilege. Defaults to the current process.
$ProcessId = $pid,
## Switch to disable the privilege, rather than enable it.
[Switch] $Disable
)
## Taken from P/Invoke.NET with minor adjustments.
$definition = @'
using System;
using System.Runtime.InteropServices;
public class AdjPriv
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = new IntPtr(processHandle);
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
if(disable)
{
tp.Attr = SE_PRIVILEGE_DISABLED;
}
else
{
tp.Attr = SE_PRIVILEGE_ENABLED;
}
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
}
'@
$processHandle = (Get-Process -id $ProcessId).Handle
$typeexists = try { ([AdjPriv] -is [type]); $true } catch { $false }
if ( $typeexists -eq $false ) {
$type = Add-Type $definition -PassThru
}
[AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable)
}
#endregion Enable Privilege function
}
process {
try {
$PendingDeletes = Get-ChildItem -Path $env:windir\WinSxS\Temp\PendingDeletes -File -Recurse -Force | Measure-Object | Select-Object -ExpandProperty Count
if ($PendingDeletes) {
Write-Warning "PendingDeletes is not empty. Exiting."
return
}
$PendingRenames = Get-ChildItem -Path $env:windir\WinSxS\Temp\PendingRenames -File -Recurse -Force | Measure-Object | Select-Object -ExpandProperty Count
if ($PendingRenames) {
Write-Warning "PendingRenames is not empty. Exiting."
return
}
$StartTime = Get-Process -Name dism, tiworker -ErrorAction SilentlyContinue | Sort-Object -Property StartTime -Descending | Select-Object -First 1 -ExpandProperty StartTime -ErrorAction SilentlyContinue
if ( [datetime]::TryParse($StartTime, [ref][datetime]::MinValue ) -eq $false ) {
$StartTime = [System.DateTime]::Now
}
$StaleInFlightFolders = Get-ChildItem -Path $env:windir\WinSxS\Temp\InFlight | Where-Object { $_.PSIsContainer -eq $True -and $_.LastWriteTime -lt $StartTime }
if ($ScanID) { $StaleInFlightFolders = $StaleInFlightFolders | Where-Object Name -in $ScanID }
if ( $StaleInFlightFolders.Count -eq 0 ) {
Write-Host "No stale InFlight folder(s) found."
return
}
#Check for Admin rights
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
Write-Warning "Must be run with administrator credentials"
return
}
# Changes can be made
# Add SeTakeOwnershipPrivilege for this process
Enable-Privilege -Privilege SeTakeOwnershipPrivilege | Out-Null
# Add SeRestorePrivilege for this process
Enable-Privilege -Privilege SeRestorePrivilege | Out-Null
$NTAccount_Administrators = [System.Security.Principal.NTAccount]"Administrators"
foreach ($StaleInFlightFolder In $StaleInFlightFolders) {
# #region Change ownership of the root folder
$acl = $StaleInFlightFolder | Get-Acl
$acl.SetOwner($NTAccount_Administrators)
Set-Acl -Path $StaleInFlightFolder.FullName -AclObject $acl
# #endregion
# #region Add Administrators Full Control
$acl = $StaleInFlightFolder | Get-Acl
$acl.SetAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule ($NTAccount_Administrators, [System.Security.AccessControl.FileSystemRights]::FullControl, @("ObjectInherit", "ContainerInherit"), "None", "Allow")))
Set-Acl -Path $StaleInFlightFolder.FullName -AclObject $acl
# #endregion
# #region Change ownership of the subfolders
$StaleInFlightFolder | Get-ChildItem -Force -Recurse | ForEach-Object {
$Item = $_
Write-Host "Changing owner on '$($Item.FullName)' to '$($NTAccount_Administrators)'..."
$acl = $Item | Get-Acl
$acl.SetOwner($NTAccount_Administrators)
if ($Item.PSIsContainer -eq $true) {
Write-Host "[Folder] Adding Administrators with full control on '$($Item.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Cyan
$acl.SetAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule ($NTAccount_Administrators, [System.Security.AccessControl.FileSystemRights]::FullControl, @("ObjectInherit", "ContainerInherit"), "None", "Allow")))
} else {
Write-Host "[File] Adding Administrators with full control on '$($Item.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Yellow
$acl.SetAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule ($NTAccount_Administrators, [System.Security.AccessControl.FileSystemRights]::FullControl, "None", "None", "Allow")))
}
$Item | Set-Acl -AclObject $acl
}
# #endregion
$StaleInFlightFolder | Remove-Item -Recurse -Force
}
}
catch {
throw $_
}
}
end {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment