7

In the following situation ls -alh

total 0
drwxrwx--- 1 user http  20 Nov 30 08:08 .
drwxrws--- 1 user http 310 Nov 30 08:07 ..
drwx------ 1 http http  10 Nov 30 08:08 empty-subdir
drwx------ 1 http http  12 Nov 30 08:08 non-empty-subdir

where two subdirectories (not owned by me) exist, which I list as:

sudo ls empty-subdir -alh
total 0
drwx------ 1 http http 10 Nov 30 08:08 .
drwxrwx--- 1 user http 20 Nov 30 08:08 ..

sudo ls non-empty-subdir -alh
total 0
drwx------ 1 http http 12 Nov 30 08:08 .
drwxrwx--- 1 user http 20 Nov 30 08:08 ..
drwx------ 1 http http  0 Nov 30 08:08 subdir

The difference between the two subdirectories being that the non-empty non-empty-subdir contains a folder.

My question is whether it is by design that trying to rm -rf remove the subdirectories I get results:

$ rm empty-subdir -rf
$ rm non-empty-subdir -rf
rm: cannot remove 'non-empty-subdir': Permission denied
$ ls -alh
total 0
drwxrwx---+ 1 user http  10 Nov 30 08:14 .
drwxrws---+ 1 user http 310 Nov 30 08:07 ..
drwx------+ 1 http http  12 Nov 30 08:08 non-empty-subdir

It seems that the user with write permissions to a directory is allowed to remove an entry for a file, or an empty subdirectory of some other user, but not a non-empty subdirectory.

An ideal answer to this question would provide information such as:

  • a confirmation that the outlined behaviour is reproducible on other machines (and not mere quirks of my screwed up box)
  • a rationale to explain that behaviour (e.g. are there use cases?)
  • an overview if there are differences between systems (BSD, Linux....)

Update: With respect to the comment by Ipor Sircer, I did retest the scenario, without any ACL features and it is the same. I therefore modified the question to remove the +es from the listings as not to give rise to an idea that the behaviour mightbe related to ACLs.

  • 3
    the trailing + sign means there are ACLs (access control list) permissions, which can be listed by getfacl <directory>. – Ipor Sircer Nov 30 '17 at 7:29
  • @IporSircer thank you for the info. I have first encountered and reproduced this behaviour in a folder that coincidently hat ACL enabled. As I said in the update/edit to the question, the behavior was not influenced by the ACL (which anyway was only to impose a default group for newly created files) – humanityANDpeace Nov 30 '17 at 7:37
14

One can only remove a directory (with the rmdir() system call) if it's empty.

rm -r dir removes the directory and all the files in it, starting with the leaves of the directory tree and walking its way up to the root (dir).

To remove a file (with rmdir() for directories and unlink() for other types of files, or *at() variants), what matters it not the permission of the file itself but those of the directory you're removing the file from (beware the t bit in the permissions, like for /tmp, adds further complications to that).

Before all, you're not really removing the file, you're unlinking it from a directory (and when it's the last link that you're removing, the file ends up being deleted as a consequence), that is, you're modifying the directory, so you need modifying (write) permissions to that directory.

The reason you can't remove non-empty-dir is that you can't unlink subdir from it first, as you don't have the right to modify non-empty-dir. You would have the right to unlink non-empty-dir from your home directory as you have write/modification permission to that one, only you can't remove a directory that is not empty.

In your case, as noted by @PeterCordes in comments, the rmdir() system call fails with a ENOTEMPTY (Directory not empty) error code, but since you don't have read permission to the directory, rm cannot even find out which files and directories (including subdir) it would need to unlink from it to be able to empty it (not that it could unlink them if it knew, as it doesn't have write permissions).

You can also get into situations where rm could remove a directory if only it could find out which files are in it, like in the case of a write-only directory:

$ mkdir dir
$ touch dir/file
$ chmod a=,u=wx dir
$ ls -ld dir
d-wx------ 2 me me 4096 Nov 30 19:43 dir/
$ rm -rf dir
rm: cannot remove 'dir': Permission denied

Still, I am able to remove it as I happen to know it only contains one file file:

$ rm dir/file
$ rmdir dir
$

Also note that with modern Unices you could rename that non-empty-dir, but on some like Linux or FreeBSD (but not Solaris), not move it to a different directory, even if you also had write permission to that directory, as (I think and for Linux, as suggested by the comment for the relevant code) doing so would involve modifying non-empty-dir (the .. entry in it would point to a different directory).

One could argue that removing your empty-dir also involves removing the .. and . entries in it, so modifying it, but still, the system lets you do that.

  • BTW, I think the error message is rm: cannot remove 'non-empty-subdir': Permission denied is because the OP doesn't have read permission on the directory, so rm couldn't even list its contents to try to remove its contents. If it had o+r permission (e.g. 755 or even 744), the error message would have been more illuminating, like ... 'non-empty-subdir/subdir': Permission denied. I upvoted the question because of this. – Peter Cordes Nov 30 '17 at 19:23
  • So I think your last paragraph should point out that rm can't even see that non-empty-dir isn't empty. The link counts in the OP's ls -alh are all 1, even for directories (bogus), but if the dir had contained a non-directory you still couldn't tell the difference between an empty and non-empty directory if you can't read it. (Except by trying to remove it.) – Peter Cordes Nov 30 '17 at 19:29
  • Maybe interesting to point out that the OP could rename the directory they don't own, because (without the t sticky bit) that does only require write permission on the directory they own. – Peter Cordes Nov 30 '17 at 19:30
  • 1
    @PeterCordes, good points. See edit. – Stéphane Chazelas Nov 30 '17 at 20:10
5

Ignoring the potential change through the ACLs, I can confirm this behaviour for my system (without ACLs).

The observed behaviour is the logical consequence of two principles:

1) The rights for a directory determine who can change the directory, i.e. delete entries in the directory. The rights of the entries in that directory play no role in this.

2) Removing a file potentially requires removing and cleaning up associated imformation, i.e. inodes, block allocation lists etc. That's why you can't remove a non-empty subdirecty without having cleaned up all files it contains, because otherwise the files it contains would become inaccessible, but there associated information would not have been cleaned up.

So you can remove empty-subdir, because you have the rights to write to the directory it is in. You can't remove non-empty-subdir, because you don't have the rights to clean up the files that are contained in this subdir first.

There's really no rationale or use case for this. One could have built recursive clean up of a subdirectory into the kernel, but the original Unix kept everything simple, and recursive clean-up would have been too complicated when it can be achieved with a user-space utility.

I can't provide a comprehensive overview between different flavours, but this was the behaviour in the original Unix, and I would expect it to be the same in every flavour of Unix, and I'd be surprised if there was a flavour of Unix that behaved differently.

  • 3
    Note that the original Unix didn't have rm -r and mkdir and rmdir were suid (making (with mknod(2)) and removing (with unlink(2) like for other types of files) directories was a privileged operation, and the linking and unlinking of "." and ".." entries in them was done in user space, access control was also done in user space by the mkdir and rmdir utilities). – Stéphane Chazelas Nov 30 '17 at 9:42
  • @StéphaneChazelas: Oh, so that's where the historical mentions of systems where you could open and read a directory as a regular file (instead of with readdir to have the kernel list entries) comes from? Fascinating. – Peter Cordes Nov 30 '17 at 20:25
  • @PeterCordes: But note that this was on really early Unix. – dirkt Nov 30 '17 at 20:49
  • I think I've seen it mentioned in (old versions of) Linux man pages, as a historical footnote. I didn't find it again when looking now. – Peter Cordes Nov 30 '17 at 21:00
  • 1
    @PeterCordes, when there was only one filesystem, there was little point abstracting the directory layout to the user. a readdir was just a read into a struct dirent. – Stéphane Chazelas Nov 30 '17 at 21:26
3

I tried to reproduce what you describe, and ran strace rm -rf ./nonempty. What that reveals is the following:

unlinkat(4, "subdir", AT_REMOVEDIR)     = -1 EACCES (Permission denied)

and according to unlinkat manual ( which on Linux is same as unlink(2), emphasis added by me):

EACCES Write access to the directory containing pathname is not allowed for the process's effective UID, or one of the directories in pathname did not allow search permission. (See also path_resolution(7).)

Since the parent directory, nonempty, doesn't grant user x (search) permission, it makes sense based on the EACCES description that subdir can't be removed.

  • No, you don't need any permission on the directory to be removed, only write permission to the containing directory. The point about search permission means the intervening directories, i.e. foo and bar, but not some in /foo/bar/some. You can try this: mkdir d; chmod 0 d; rmdir d, it should work ok. Of course without search permission to non-empty-subdir this happens, but then without write permission it would happen anyway. Also: search permission is the x bit, it's not the same as read permission – ilkkachu Nov 30 '17 at 10:48
  • @ilkkachu Makes sense. I've edited the answer to reflect that. – Sergiy Kolodyazhnyy Nov 30 '17 at 11:15
  • You must have been testing with a non-empty-subdir that is readable but not writeable. The OP's rm command couldn't even find out that there is a subdir inside it, because they don't have read permission (note they used sudo to list the contents). See Stéphane Chazelas's updated answer. – Peter Cordes Nov 30 '17 at 20:17
  • @PeterCordes I've set both non-empty and subdir to 700 permissions, just as in OP's example and chowned both to be owned by another user and their group. – Sergiy Kolodyazhnyy Nov 30 '17 at 20:56
  • Then how was rm -rf ./nonempty able to make an unlinkat(4, "subdir", AT_REMOVEDIR) system call, if it couldn't even read ./nonempty to find the name subdir? – Peter Cordes Nov 30 '17 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.