From time to time, when managing some Windows machines, I need to delete unused files created by some other local users, users on a network, users that do not exist locally or on the network anymore (for example a Linux Live cd), or from Cygwin.
It happened to me more than once, that even with full administrator privileges, I was not able to delete those files. I either needed to switch my account to the one that created the file, or the file permission was corrupt.
Windows does not have
chown, and the permission model is different than on UNIX systems.
Even if I’m using an administrator account, there are other accounts which files I’m not able to delete.
Switching account, rebooting, booting in safe mode, and solution similar to those might be viable, but most are cumbersome and do not always work.
sfc /scannow, disabling UAC and similar solutions are most of the time a waste of time, as it does not fix anything relevant to the issue.
Booting from a Linux live CD is the only one guaranteed to work anytime, but it is very hard to automate for administrative tasks.
There are two tools, that can help to fix the issue:
Using external tools
$file="path to folder to delete" takeown.exe /skipsl /r /d y /f $file | Out-Null icacls.exe $file /reset /T | Out-Null Remove-Item -Recurse $tmp -Force | Out-Null
I was actually quite happy with this solution until I tried to delete a file instead of one directory, the error message is
ERROR: The specified path is not a valid directory path..
The solution is to remove the recursive (
/r) and thus confirmation (
/d y) options from takeown.
As I use primarily PowerShell as native Windows shell, I also found another way to script those operations (and according to some sources, it should be faster as there are no external tools invoked):
$ACL = Get-ACL -Path $file $Group = New-Object System.Security.Principal.NTAccount("$env:UserName") $ACL.SetOwner($Group) Set-Acl -Path $tmp -AclObject $ACL $Acl = Get-Acl -Path $file $Ar = New-Object system.security.accesscontrol.filesystemaccessrule("$env:UserName","FullControl","Allow") $Acl.SetAccessRule($Ar) Set-Acl -Path $tmp $Acl Remove-Item -Recurse $file -Force | Out-Null
As for now the solution with takeown and icacls works without issues is way less verbose, and can be used also from cmd (might be relevant if the system gets really slow) I’m going to stick with it.
Notice that those commands (takeown and the PowerShell variant), do not add the current user as owner, but replace it. In my use-case, for deleting files and folder, it is not relevant who owns the file, as it is going to disappear. But for other operations, it might be relevant.
Of course, there is always the possibility that the file is opened by another process. In this case, the solution is to kill the offending process (or wait until it finishes) and then delete the file.
The command-line utility
handle.exe of the Sysinternals suite can show which process has an open handle to which file.
It also provides the ability to forcefully close the handle, but it is discouraged, as it can cause system instability and data corruption.
This Article on msdn gives a nice example of what catastrophic side effects forcefully closing a handle can have, the best option, if waiting is not a reasonable option, is to terminate the offending process.