Unable to delete files as Windows administrator
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 chmod
and 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 accounts, rebooting, booting in safe mode, and solutions similar to those might be viable, but most are cumbersome and do not always work. Running 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 administrative tasks.
There are two tools, that can help to fix the issue: takeown
and icacls
:
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 a 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 folders, 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 archived 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.
Forcing a handle closed is equivalent to reaching into a program and freeing some memory. The program thinks the handle (or memory) is still valid and will continue to use it. But since the handle is really free, it will be reused for something else. […]
If you’re extremely lucky, the next thing the program will do is close the handle you forced to close earlier. The program gets an invalid handle error, but since there’s nothing it can do, the program just proceeds as if the handle closed successfully. But this is rarely the case.
More likely, the program will eventually come back to the handle and try to use it, getting invalid handle errors. Even more likely is that the handle you forced closed will be used for something else. And that’s when things start to go completely berserk. Suppose the handle were reused as another file. Now the program thinks it’s reading and writing from the first file, but in fact it’s operating on the second file. The result is data corruption in the first file (data that should have been written wasn’t) and in the second file (data intended for the first file was written to the second file).
When the program closes the first file, it also closes the recycled handle. You’ve created a cascade error: You forced the first file closed, and the result is that some second handle was forced closed. […]
Meanwhile, another handle you forced closed was reused as a mutex handle, which is used to help prevent data from being corrupted. When the original file handle is closed, the mutex handle is closed and the protections against data corruption are lost. The longer the service runs, the more corrupted its indexes become. Eventually, somebody notices the index is returning incorrect results. And when you try to restart the service, it fails because its configuration files have been corrupted. […]
Raymond Chen
If you have questions, comments, or found typos, the notes are not clear, or there are some errors; then just contact me.