Subject: bug#18491: rm -r fails to delete entire hierarchy when path goes in and out of it

From: Gian Ntzik <[email-masked]>
Date: Fri, 19 Sep 2014 04:30:29 +0100

Bob Proulx <[email-masked]> writes:

> Gian Ntzik wrote:
>> It seems that using rm -r with a path that goes into a (non-empty)
>> directory intended for removal (and back up e.g. using dot-dots) fails
>> to remove the directory. The directory is rendered empty, but itself not
>> removed.
>> 
>> For example,
>> 
>> $ mkdir -p /tmp/a/b/c
>> $ mkdir -p /tmp/a/e
>> $ rm -r /tmp/a/b/../../a
>> rm: cannot remove ‘/tmp/a/b/../../a’: No such file or directory

Bear in mind that the issue is not restricted to dot-dots within the
pathname argument. The problem is with any pathname that traverses
the same directory we want to remove. The same thing can happen with
symlinks.

For example,
$ mkdir -p /tmp/a/b/c
$ mkdir -p /tmp/a/e
$ ln -s /tmp /tmp/a/e/x
$ ls /tmp/a
b  e
$ ls /tmp/a/e/x/a
b  e
$ rm -r /tmp/a/e/x/a
rm: cannot remove ‘/tmp/a/e/x/a’: No such file or directory
$ ls /tmp/a
$

> I don't think this can reasonably be called a bug.  

Assume this is not a bug as you claim. 

Then this means that given an acceptable path argument (not ending in dot,
dot-dot and not resolving to /) that successfully resolves to a
directory, rm -r may or may not delete the directory (assuming no
interference from a concurrently running process). So, either your rm -r
behaviour is non-deterministic even without concurrency, or rm -r is not
intended to be used by all acceptable paths, but by some subset
satisfying a property P.

A definition of this property P could be:
Path x satisfies P iff 
x is acceptable by rm, and
if x resolves to a directory then, there does not exist a prefix y of x
such that y resolves to the same entry as x.

For paths not satisfying P, rm -r does something else, or is
undefined.

There are two major problems with is:
1). This usage restriction is not mentioned in this implementation's
documentation. If this is not a bug then it really should, because
clients of rm wanting to really remove a directory must make sure their
paths meet your requirements.
2). The POSIX rm specification does not restrict the paths that can be
used to remove directories (other than those that rm does not accept in
the first place). In fact this most probably deviates from the standard.

>
> Trying to do anything to work around this seems wrong to me since it
> will require keeping track of the state before and simulating to
> create the desired state afterward and then applying a derived state
> change to the file system.  That is much too complex for this simple
> operation.
>

Yes, this is way too complex. However, for every path there is a
canonical path that does not use dot-dots or reference symbolic
links. Canonical paths would never run into such problems because they
do not traverse what they resolve to. After initial checks to see if the
argument is acceptable (not ending in dot-dot, dot or not being /), one
could take the canonical path of the argument, e.g. through a simple
realpath(), and start the recursive removal with that. This seems much
simpler.