Subject: Re: bug#12339: Bug: rm -fr . doesn't dir depth first deletion yet it is documented to do so.

From: Bernhard Voelker <[email-masked]>
Date: Tue, 04 Sep 2012 07:20:59 +0200

On 09/03/2012 10:04 PM, Paul Eggert wrote:
> I was about to suggest the following, on GNU/Linux:
> 
>   # Don't do this unless you know what you're doing!
>   rm -fr /proc/self/cwd/
> 
> Except it doesn't work!  Not even if I append '.':
> 
>   $ mkdir /tmp/victim
>   $ cd /tmp/victim
>   $ touch foo
>   $ rm -fr /proc/self/cwd/
>   rm: cannot remove `/proc/self/cwd': Too many levels of symbolic links

That is in rm_fts():

      /* Perform checks that can apply only for command-line arguments.  */
      if (ent->fts_level == FTS_ROOTLEVEL)
        {
          if (strip_trailing_slashes (ent->fts_path))
            ent->fts_pathlen = strlen (ent->fts_path);

ent->fts_path is stripped from /proc/self/cwd/ to /proc/self/cwd.
Then this:

        Ternary is_empty_directory;
        enum RM_status s = prompt (fts, ent, true /*is_dir*/, x,
                                   PA_DESCEND_INTO_DIR, &is_empty_directory);

        if (s == RM_OK && is_empty_directory == T_YES)
          {
            /* When we know (from prompt when in interactive mode)
               that this is an empty directory, don't prompt twice.  */
            s = excise (fts, ent, x, true);
            fts_skip_tree (fts, ent);
          }

s is RM_OK, but is_empty_directory is T_NO, because it's no DIR ...
The FTS loop continues and diagnoses ELOOP. That's the strace output:

  newfstatat(AT_FDCWD, "/proc/self/cwd/", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
  openat(AT_FDCWD, "/proc/self/cwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = -1 ELOOP (Too many levels of
symbolic links)
  openat(AT_FDCWD, "/proc/self/cwd", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = -1 ELOOP (Too many levels of
symbolic links)
  unlinkat(AT_FDCWD, "/proc/self/cwd", AT_REMOVEDIR) = -1 EACCES (Permission denied)

The example applies to all symlinks to directories (i.e. not only
to /proc/self/cwd):

  $ mkdir d
  $ touch d/file
  $ ln -s d dl
  $ rm -rv dl/
  /home/berny/cu/src/rm: cannot remove ‘dl’: Too many levels of symbolic links

However, in this case, unlinkat returns ENOTDIR:

  unlinkat(AT_FDCWD, "dl", AT_REMOVEDIR)  = -1 ENOTDIR (Not a directory)

>   $ rm -fr /proc/self/cwd/.
>   rm: cannot remove directory: `/proc/self/cwd/.'
>   $ ls
>   foo

That's the dot case.

> Aren't these bugs, at least?

The latter: no, because dot must not be removed.
The former: maybe, but the question is what rm should remove:
the symlink or the target? The info page is quiet about this.
I tend to say it's okay as it is, because if someone wants to
remove the target of a symlink,(s)he can always to
  $ rm -rv $(readlink -f dl)
Only the diagnostic (ELOOP) looks a bit strange ...

Have a nice day,
Berny