[Haskell-cafe] Something's weird about System.Directory.removeDirectoryRecursive

John Millikin jmillikin at gmail.com
Sat Jan 28 08:48:33 CET 2012


http://hackage.haskell.org/packages/archive/directory/1.1.0.1/doc/html/System-Directory.html#v:removeDirectoryRecursive

The documentation says that removeDirectoryRecursive follows symlinks.
However, the implementation doesn't (in most cases, see below).

------------------------------------------------------------------------------------------------------------
$ mkdir test-removeDirectoryRecursive
$ cd test-removeDirectoryRecursive
$ mkdir a b
$ touch a/a.txt b/b.txt
$ ln -s $PWD/b a/
$ ln -s $PWD/b/b.txt a/
$ ls -l a
total 8.2k
-rw-rw-r-- 1 john john  0 2012-01-27 23:33 a.txt
lrwxrwxrwx 1 john john 65 2012-01-27 23:33 b ->
/home/john/test-removeDirectoryRecursive/b
lrwxrwxrwx 1 john john 71 2012-01-27 23:34 b.txt ->
/home/john/test-removeDirectoryRecursive/b/b.txt

# OK, a/ has a normal file and two symlinks in it. Let's recursively
remove a/ and see what happens.

$ ghci
Prelude> import System.Directory
Prelude System.Directory> removeDirectoryRecursive "a"
Prelude System.Directory>
Leaving GHCi.

$ ls -l a b
ls: cannot access a: No such file or directory
b:
total 0
-rw-rw-r-- 1 john john 0 2012-01-27 23:33 b.txt

# a/ was removed -- good!
#
# b/ and its contents are untouched, good, but goes against the docs

------------------------------------------------------------------------------------------------------------

Now, there is one case where this function *will* follow symlinks.
However, I believe it is a bug because it produces odd behavior:

------------------------------------------------------------------------------------------------------------
$ sudo mkdir a
[sudo] password for john:
$ sudo ln -s $PWD/b a/
$ ls -l a b
a:
total 4.1k
lrwxrwxrwx 1 root root 65 2012-01-27 23:38 b ->
/home/john/test-removeDirectoryRecursive/b

b:
total 0
-rw-rw-r-- 1 john john 0 2012-01-27 23:33 b.txt

# Now a/ has a symlink, which cannot be deleted, because its
containing directory is read-only to the current user.
$ rm a/b
rm: remove symbolic link `a/b'? y
rm: cannot remove `a/b': Permission denied

# What happens if removeDirectoryRecursive is called now?
$ ghci
Prelude> import System.Directory
Prelude System.Directory> removeDirectoryRecursive "a"
*** Exception: a/b: removeDirectory: permission denied (Permission denied)
Prelude System.Directory>
Leaving GHCi.

$ ls -l a b
a:
total 4.1k
lrwxrwxrwx 1 root root 65 2012-01-27 23:38 b ->
/home/john/test-removeDirectoryRecursive/b

b:
total 0

# a/ is untouched, but b/ has been emptied!

------------------------------------------------------------------------------------------------------------

So what is the expected behavior of this function? What should it do
in the presence of symlinks?

IMO, the function should be documented as *not* following symlinks,
and the directory check should be changed so that it returns False for
symlink-to-directory.



More information about the Haskell-Cafe mailing list