Someone wanted to know how to create a file that denies itself from being removed. He had tried denying DELETE access from Everyone. However, the file was still removable, thus this post on V2EX (in Chinese).
It took no time of me to recall this blog post by Raymond Chen. Here’s a relevant excerpt:
Note that the "Delete Subfolders and Files" and "Delete" attributes together determine whether you can delete a file or subdirectory: You can delete an item either if you have DELETE permission on the item or if you have DELETE_CHILD permission on its parent.
Let’s say the file is C:\1\2\3. According to Raymond (and my experiment), the following configuration should prevent everyone from deleting it:
File/folder | Principal | Type | Access |
---|---|---|---|
C:\1 | Everyone | Allow | Full control |
C:\1\2 | Everyone | Deny | DELETE, DELETE_CHILD |
C:\1\2\3 | Everyone | Deny | DELETE |
However, there is an apparent loophole here. Instead of deleting C:\1\2\3, couldn’t the user delete C:\1\2, since we have DELETE access to its parent, C:\1?
If you try this out in File Explorer, it tells you ‘You’ll need to provide administrator permission to delete this folder’. Why? The answer is in two sentences:
Le vassal de mon vassal n’est pas mon vassal.
You cannot remove a directory unless it is empty.
The first sentence dictates that access to DELETE_CHILD on C:\1 does not automatically grant access to removal of C:\1\2\3 (this is the case even if you disable ACL inheritance and set ACL on the leaf directly, instead of denying access, which might trigger denial-first logic). In other words, DELETE_CHILD only grants rights to remove direct children.
The second sentence should be familiar to most people who have used command line, or who have encountered problems in deleting a folder. In our case, deleting C:\1\2 is not possible until C:\1\2\3 is deleted. Therefore, the failure is not a denial of removal by ACL on C:\1\2, but that on C:\1\2\3.
If we edit the ACL of C:\1\2\3 and delete it, we will be able to delete C:\1\2 without having DELETE access to it, as the directory is empty now.
The reason why removal of a non-empty directory has to be done explicitly by recursion has always been a mystery to me. I haven’t found or read any explanation on this. My initial guess is that this design keeps the kernel and file system drivers simple, thus easier to be bullet-proof. Supporting recursive removal directly from kernel/driver requires careful coding, as neither stack overflow nor out-of-memory error during recursion is desired. And after all, user programs can always delete a directory recursively, possibly crashing themselves if the folder is a deep tree.
There is now at least another reason for this rule in NTFS. Without it, the whole DELETE_CHILD logic wouldn’t work, unless this access is denied all the way from the root directory of a volume. I’m not familiar with other file systems (that said, I’m not familiar with NTFS either), but I can tell that if you cannot delete a file inside some directory, neither will you be able to delete the directory on UNIX and the likes.
Another interesting observation is that File Explorer actually recursively checks ACL before trying to delete anything. To see this, set the following ACLs:
File/folder | Principal | Type | Access |
---|---|---|---|
C:\1 | Everyone | Allow | Full control |
C:\1\2 | Everyone | Deny | DELETE, DELETE_CHILD |
C:\1\2\a | Everyone | Allow | Full control |
C:\1\2\b | Everyone | Deny | DELETE |
If you try deleting C:\1\2 in File Explorer, it gives the same prompt without side effects (even if prompt before moving to Recycle Bin is disabled, and you are moving the item to Recycle Bin). By contrast, if you do the following in PowerShell:
Remove-Item C:\1\2 -Force -Recurse
You will find C:\1\2\a deleted whereas C:\1\2\b and C:\1\2 are left untouched.
Please enable JavaScript to view the comments powered by Disqus.