Subject: Re: bug#12339: Gnu rm, changed only recently (4-5 years), and didn't follow letter of posix...(statement follows)

From: Eric Blake <[email-masked]>
Date: Wed, 12 Sep 2012 18:50:32 -0600

On 09/12/2012 06:28 PM, Eric Blake wrote:
>> Before, "rm -r bbb/" was not valid syntax --
> 
> Sorry, but 'rm -r bbb/' has ALWAYS been valid syntax in POSIX, and has
> always meant 'remove the directory found by resolving 'bbb', even if
> 'bbb' is a symlink to a directory.  The fact that the Linux kernel
> rmdir("bbb/") has not always followed POSIX in this regards makes it
> harder to use as a use case - because then we are torn with whether to
> honor the kernel decision of how it should behave or whether to pay the
> penalty of a slower wrapper function that does it the way POSIX says it
> should behave.

On re-reading this, I think a bit more on this topic might help.
Remember that remove() is required to call either rmdir() or unlink() as
appropriate.

On Solaris 10, if 'bbb' is a symlink to an empty directory, then
remove("bbb") removes the symlink but leaves the directory; and
remove("bbb/") removes the directory, but leaves bbb as a danging
symlink.  This is the behavior POSIX mandates, but can be non-intuitive
if you are not expecting it.

On Solaris 8, remove("bbb/") had a bug where it silently ignored the
trailing slash, and was blindly treated as remove("bbb").  Even if "bbb"
was some other file type, like a regular file, it was still removed.
Coreutils works around this bug by using a gnulib module to wrap a large
number of Solaris 8 kernel calls that silently ignore trailing slash.

On Linux (at least through kernel 3.5.3), remove("bbb") removes the
symlink, and remove("bbb/") fails with ENOTDIR, which is contrary to
POSIX.  The failure intuitively makes more sense (who wants to leave a
dangling symlink behind?), although the choice of errno value does not
(POSIX requires that bbb/ be treated as a directory, and the symlink
does indeed resolve to a directory).  But gnulib and coreutils so far
have chosen not to penalize Linux with a wrapper around remove(), and
instead this means 'rm' currently ends up with non-POSIX behavior
because the kernel still has non-POSIX behavior.

On all three systems, remove("bbb/.") fails, consistent with the POSIX
requirement that the basename of "." cannot be removed directly, but
only by rmdir()ing another name for the same directory.  In this case,
POSIX mandates EINVAL as the failure (not all systems meet this), which
is different than the ENOTDIR failure of Linux' remove("bbb/").

-- 
Eric Blake   [email-masked]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org