Bug 116477 - rm(1): rm behaves unexpectedly when using -r and relative paths
Summary: rm(1): rm behaves unexpectedly when using -r and relative paths
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: standards (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-standards (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-09-19 23:00 UTC by Justin Mitchell
Modified: 2022-04-02 13:02 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Justin Mitchell 2007-09-19 23:00:05 UTC
This is a bug with the 'rm' commandline utility that can cause data
loss.

When using 'rm -r' with relative paths and a trailing slash rm can
delete the wrong item, or no item at all. Here is an example of rm
deleting the wrong file.

mac:~ $ mkdir tmp
mac:~ $ cd tmp
mac:~/tmp $ mkdir test
mac:~/tmp $ ln -s test test2
mac:~/tmp $ ls -l
total 8
drwxr-xr-x   2 justin  justin  68 Aug 10 01:41 test
lrwxr-xr-x   1 justin  justin   4 Aug 10 01:42 test2 -> test
mac:~/tmp $ cd ..
mac:~ $ rm -r tmp/test2/
mac:~ $ ls -l tmp
total 8
lrwxr-xr-x   1 justin  justin  4 Aug 10 01:42 test2 -> test

Note that if you do you not use the trailing slash in 'rm -f tmp/
test2/' that it will behave normally. It will also behave normally if
you do not 'cd' to another directory.

I confirmed this with another individual running OS X, but I do
not know the version. For comparison, I tested this with an older
version of Redhat, and it worked fine.

I submitted this bug report to Apple, but they said "This is how FreeBSD
operates and rm is based on their sources."

How-To-Repeat: mac:~ $ mkdir tmp
mac:~ $ cd tmp
mac:~/tmp $ mkdir test
mac:~/tmp $ ln -s test test2
mac:~/tmp $ ls -l
total 8
drwxr-xr-x   2 justin  justin  68 Aug 10 01:41 test
lrwxr-xr-x   1 justin  justin   4 Aug 10 01:42 test2 -> test
mac:~/tmp $ cd ..
mac:~ $ rm -r tmp/test2/
mac:~ $ ls -l tmp
total 8
lrwxr-xr-x   1 justin  justin  4 Aug 10 01:42 test2 -> test
Comment 1 Bruce Evans freebsd_committer freebsd_triage 2007-09-20 12:45:20 UTC
On Wed, 19 Sep 2007, Justin Mitchell wrote:

>> Description:
> This is a bug with the 'rm' commandline utility that can cause data
> loss.

This is rm doing what you asked it to.

> When using 'rm -r' with relative paths and a trailing slash rm can
> delete the wrong item, or no item at all. Here is an example of rm
> deleting the wrong file.
>
> mac:~ $ mkdir tmp
> mac:~ $ cd tmp
> mac:~/tmp $ mkdir test
> mac:~/tmp $ ln -s test test2
> mac:~/tmp $ ls -l
> total 8
> drwxr-xr-x   2 justin  justin  68 Aug 10 01:41 test
> lrwxr-xr-x   1 justin  justin   4 Aug 10 01:42 test2 -> test
> mac:~/tmp $ cd ..
> mac:~ $ rm -r tmp/test2/
> mac:~ $ ls -l tmp
> total 8
> lrwxr-xr-x   1 justin  justin  4 Aug 10 01:42 test2 -> test
>
> Note that if you do you not use the trailing slash in 'rm -f tmp/
> test2/' that it will behave normally. It will also behave normally if
> you do not 'cd' to another directory.
>
> I confirmed this with another individual running OS X, but I do
> not know the version. For comparison, I tested this with an older
> version of Redhat, and it worked fine.

gnu/Linux rm or Linux could easily have a bug like that, but at least
the version in an old FreeBSD package (linux_base-8-8.0_4 with bin/rm
dated Sep 2 2002) works correctly under FreeBSD -- it removes the
directory.

> I submitted this bug report to Apple, but they said "This is how FreeBSD operates and rm is based on their sources."

This is how symbolic links work.  The trailing slash causes the symlink
to be followed.  POSIX spec: trailing slashes shall be resolved as if
a single dot character were appended to a pathname.  Hmm, this seems
to require a small part behaviour that you want -- the pathname ends
up with a trailing dot no matter where in the process of pathname
resolution that the dot is added.  Then rm(1)'s and/or rmdir(2)'s
(bogus) restriction on removing "." and ".." comes into play and
according to POSIX should prevent the removal of the directory pointed
to by the symlink (but not the contents of the directory -- see below).

What FreeBSD does is follow "test2/" to resolve to "test".  It doesn't
add the dot as required by POSIX.  POSIX doesn't seem to say clearly
when the dot should be added, but it would have to be added at the
beginning of pathname resolution to be useful, since then it would
cause "test2/" to be resolved to "test/.", while adding it after
resolving to "test" would give the name "test." where the dot doesn't
have anything to do with the directory entry "." and just messes up
the name "test".

After resolving to "test/.", rmdir() on the directory must fail due
to rmdir()'s bogus restriction on removing ".".  This restriction is
bogus because it is only by name -- you can easily rmdir() your current
directory (if it is empty) by spelling its name differently, e.g., as
the absolute path to th current directory, but you can't dubdirectories
if you spell their name with a ".".  Before I started writing this mail,
I thought that you could use the spelling of "<symlink>/" to remove
current and other directories, but the POSIX spec prevents that.

However, the POSIX spec doesn't affect removing the directories entire
contents using "rm -r <symlink>/".  rm has its own bogus restriction
on removing "." (and "..") by name.  This restriction doesn't apply
here since there is no dot in "<symlink>/".  The pathname resolution
that POSIX requires to add the dot occurs entirely in the kernel.

Thus the expected behaviour for a POSIX "rm -r test2/" is:

     follow the symlink to get "test/."
     remove the contents of "test/."
        rm will probably just apply rm -r to each file in "test/.".
        I verified that the Linux rm from 2002 when run under FreeBSD
        just chdir()'s into any subdirs to remove them recursively.
     fail with errno EINVAL trying to rmdir() "test/."
        (the Linux rm from 2002 just tries to remove "test/", and this
        succeeds under FreeBSD)
     don't remove the symlink

     This is not what you want -- it removes everything except the one
     thing that is easy to recover.

and the expected behaviour for a FreeBSD "rm -r test2/" is:

     same as above, except it won't fail on the top-level rmdir().

I know too much about this because I broke following of symlinks for
"<symlink>/" when fixing the handling of trailing slashes in general.

Some other cases of interest:

(1) when test2 is symlink to "test" and "test" doesn't exist at the start
     of each of the following operations:

     FreeBSD mkdir test2/    succeeds
     FreeBSD mkdir test2/.   fails with errno ENOENT (reasonable, since test2
 			    exists, but test2/ and test2/, don't)
     POSIX   mkdir test2/    must fail after adding dot

(2) when "test" doesn't exist at the start:

     FreeBSD mkdir test      succeeds, nothing special
     FreeBSD mkdir test/     succeeds (tricky to make this work)
     FreeBSD mkdir test/.    fails with ENOENT
     POSIX   mkdir test/     ?  POSIX says that trailing slashes shall be
 			    ignored in directory contexts.  FreeBSD
 			    considers this to be a directory context

(3) cases involving rename(2) are more complicated, and cases involving
     mv(1) are even more complicated, since mv to a directory has to manage
     pathnames before calling rename(2).

Bruce
Comment 2 Gavin Atkinson freebsd_committer freebsd_triage 2008-06-27 14:51:44 UTC
State Changed
From-To: open->feedback

On Solaris 8: 
--------------- 
gavin@sol8:/var/tmp/tmp 7% ls -l 
total 4 
drwx--x--x   2 gavin      gavin       512 Jun 27 14:42 test 
lrwxrwxrwx   1 gavin      gavin         4 Jun 27 14:43 test2 -> test 
gavin@sol8:/var/tmp/tmp 8% cd .. 
gavin@sol8:/var/tmp 9% rm -r tmp/test2/ 
gavin@sol8:/var/tmp 10% ls -l tmp 
total 2 
drwx--x--x   2 gavin      gavin       512 Jun 27 14:42 test 
--------------- 

On Solaris 10: 
--------------- 
gavin@sol10:/var/tmp/tmp 7% ls -l 
total 4 
drwx--x--x   2 gavin      gavin       512 Jun 27 14:44 test 
lrwxrwxrwx   1 gavin      gavin         4 Jun 27 14:44 test2 -> test 
gavin@sol10:/var/tmp/tmp 8% cd .. 
gavin@sol10:/var/tmp 9% rm -r tmp/test2/ 
gavin@sol10:/var/tmp 10% ls -l tmp 
total 2 
lrwxrwxrwx   1 gavin      gavin         4 Jun 27 14:44 test2 -> test 
--------------- 

So, it looks like Solaris now behaves in the same way FreeBSD does in this 
case, which (along with bde@'s analysis) suggests to me that the current 
behaviour is correct. 

Submitter: do you still believe this to be incorrect, or can I close the PR?
Comment 3 Gavin Atkinson freebsd_committer freebsd_triage 2008-06-30 12:06:18 UTC
State Changed
From-To: feedback->open

bde@ still feels that our rm(1) doesn't follow POSIX: 
http://docs.FreeBSD.org/cgi/mid.cgi?20080628113326.Y89027 



Comment 4 Gavin Atkinson freebsd_committer freebsd_triage 2008-06-30 12:06:18 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-standards

Over to -standards for consideration
Comment 5 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 08:01:32 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped
Comment 6 Minsoo Choo 2022-04-02 13:02:52 UTC
macOS follows UNIX 03 and works the same as FreeBSD does, which means there is no problem with current rm(1). This bug report should be closed.